diff options
541 files changed, 16886 insertions, 5631 deletions
diff --git a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__riscv64_CtsShimPriv_apk.asciipb b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__riscv64_CtsShimPriv_apk.asciipb index e89809138548..e8e7ec901785 100644 --- a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__riscv64_CtsShimPriv_apk.asciipb +++ b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__riscv64_CtsShimPriv_apk.asciipb @@ -1,6 +1,6 @@ drops { android_build_drop { - build_id: "9653376" + build_id: "11947186" target: "CtsShim" source_file: "aosp_riscv64/CtsShimPriv.apk" } @@ -8,7 +8,7 @@ drops { version: "" version_group: "" git_project: "platform/frameworks/base" - git_branch: "master" + git_branch: "main" transform: TRANSFORM_NONE transform_options { } diff --git a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__riscv64_CtsShim_apk.asciipb b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__riscv64_CtsShim_apk.asciipb index 04092366c257..6113b6a6dc8f 100644 --- a/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__riscv64_CtsShim_apk.asciipb +++ b/.prebuilt_info/prebuilt_info_packages_CtsShim_apk__riscv64_CtsShim_apk.asciipb @@ -1,6 +1,6 @@ drops { android_build_drop { - build_id: "9653376" + build_id: "11947186" target: "CtsShim" source_file: "aosp_riscv64/CtsShim.apk" } @@ -8,7 +8,7 @@ drops { version: "" version_group: "" git_project: "platform/frameworks/base" - git_branch: "master" + git_branch: "main" transform: TRANSFORM_NONE transform_options { } diff --git a/apct-tests/perftests/core/src/android/permission/AppOpsPerfTest.kt b/apct-tests/perftests/core/src/android/permission/AppOpsPerfTest.kt index 2af878e60db7..daf991c313c8 100644 --- a/apct-tests/perftests/core/src/android/permission/AppOpsPerfTest.kt +++ b/apct-tests/perftests/core/src/android/permission/AppOpsPerfTest.kt @@ -17,8 +17,7 @@ package android.permission import android.app.AppOpsManager import android.content.Context -import androidx.benchmark.BenchmarkState -import androidx.benchmark.junit4.BenchmarkRule +import android.perftests.utils.PerfStatusReporter import androidx.test.core.app.ApplicationProvider import androidx.test.filters.LargeTest import org.junit.Before @@ -34,7 +33,7 @@ import org.junit.Test * these APIs should be monitored closely for performance. */ class AppOpsPerfTest { - @get:Rule val mBenchmarkRule: BenchmarkRule = BenchmarkRule() + @get:Rule val perfStatusReporter = PerfStatusReporter() private lateinit var appOpsManager: AppOpsManager private lateinit var opPackageName: String private var opPackageUid: Int = 0 @@ -49,7 +48,7 @@ class AppOpsPerfTest { @Test fun testNoteOp() { - val state: BenchmarkState = mBenchmarkRule.getState() + val state = perfStatusReporter.benchmarkState while (state.keepRunning()) { appOpsManager.noteOp( AppOpsManager.OPSTR_FINE_LOCATION, @@ -63,7 +62,7 @@ class AppOpsPerfTest { @Test fun testUnsafeCheckOp() { - val state: BenchmarkState = mBenchmarkRule.getState() + val state = perfStatusReporter.benchmarkState while (state.keepRunning()) { appOpsManager.unsafeCheckOp( AppOpsManager.OPSTR_FINE_LOCATION, diff --git a/core/api/current.txt b/core/api/current.txt index bbb3932f0b17..831cf0194ca3 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -15974,6 +15974,7 @@ package android.graphics { enum_constant public static final android.graphics.ColorSpace.Named LINEAR_EXTENDED_SRGB; enum_constant public static final android.graphics.ColorSpace.Named LINEAR_SRGB; enum_constant public static final android.graphics.ColorSpace.Named NTSC_1953; + enum_constant @FlaggedApi("com.android.graphics.flags.ok_lab_colorspace") public static final android.graphics.ColorSpace.Named OK_LAB; enum_constant public static final android.graphics.ColorSpace.Named PRO_PHOTO_RGB; enum_constant public static final android.graphics.ColorSpace.Named SMPTE_C; enum_constant public static final android.graphics.ColorSpace.Named SRGB; @@ -19910,7 +19911,6 @@ package android.hardware.camera2 { } @FlaggedApi("com.android.internal.camera.flags.concert_mode_api") public final class ExtensionCaptureRequest { - ctor public ExtensionCaptureRequest(); field @FlaggedApi("com.android.internal.camera.flags.concert_mode_api") @NonNull public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Boolean> EFV_AUTO_ZOOM; field @FlaggedApi("com.android.internal.camera.flags.concert_mode_api") @NonNull public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Float> EFV_MAX_PADDING_ZOOM_FACTOR; field @FlaggedApi("com.android.internal.camera.flags.concert_mode_api") @NonNull public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Float> EFV_PADDING_ZOOM_FACTOR; @@ -19923,7 +19923,6 @@ package android.hardware.camera2 { } @FlaggedApi("com.android.internal.camera.flags.concert_mode_api") public final class ExtensionCaptureResult { - ctor public ExtensionCaptureResult(); field @FlaggedApi("com.android.internal.camera.flags.concert_mode_api") @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Boolean> EFV_AUTO_ZOOM; field @FlaggedApi("com.android.internal.camera.flags.concert_mode_api") @NonNull public static final android.hardware.camera2.CaptureResult.Key<int[]> EFV_AUTO_ZOOM_PADDING_REGION; field @FlaggedApi("com.android.internal.camera.flags.concert_mode_api") @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Float> EFV_MAX_PADDING_ZOOM_FACTOR; @@ -47134,6 +47133,8 @@ package android.telephony.ims { method public int describeContents(); method public int getAttributeFlags(); method @NonNull public java.util.Set<java.lang.String> getFeatureTags(); + method @FlaggedApi("com.android.internal.telephony.flags.emergency_registration_state") public boolean getFlagRegistrationTypeEmergency(); + method @FlaggedApi("com.android.internal.telephony.flags.emergency_registration_state") public boolean getFlagVirtualRegistrationForEmergencyCall(); method @Nullable public android.telephony.ims.SipDetails getSipDetails(); method public int getTransportType(); method public void writeToParcel(@NonNull android.os.Parcel, int); diff --git a/core/api/lint-baseline.txt b/core/api/lint-baseline.txt index 1b0da055038d..e6a7ca5bf54c 100644 --- a/core/api/lint-baseline.txt +++ b/core/api/lint-baseline.txt @@ -245,14 +245,6 @@ BroadcastBehavior: android.telephony.euicc.EuiccManager#ACTION_NOTIFY_CARRIER_SE Field 'ACTION_NOTIFY_CARRIER_SETUP_INCOMPLETE' is missing @BroadcastBehavior -CompileTimeConstant: android.hardware.camera2.ExtensionCaptureRequest#EFV_STABILIZATION_MODE_GIMBAL: - All constants must be defined at compile time: android.hardware.camera2.ExtensionCaptureRequest#EFV_STABILIZATION_MODE_GIMBAL -CompileTimeConstant: android.hardware.camera2.ExtensionCaptureRequest#EFV_STABILIZATION_MODE_LOCKED: - All constants must be defined at compile time: android.hardware.camera2.ExtensionCaptureRequest#EFV_STABILIZATION_MODE_LOCKED -CompileTimeConstant: android.hardware.camera2.ExtensionCaptureRequest#EFV_STABILIZATION_MODE_OFF: - All constants must be defined at compile time: android.hardware.camera2.ExtensionCaptureRequest#EFV_STABILIZATION_MODE_OFF - - DeprecationMismatch: android.accounts.AccountManager#newChooseAccountIntent(android.accounts.Account, java.util.ArrayList<android.accounts.Account>, String[], boolean, String, String, String[], android.os.Bundle): Method android.accounts.AccountManager.newChooseAccountIntent(android.accounts.Account, java.util.ArrayList<android.accounts.Account>, String[], boolean, String, String, String[], android.os.Bundle): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match DeprecationMismatch: android.app.Activity#enterPictureInPictureMode(): @@ -1095,14 +1087,6 @@ RequiresPermission: android.webkit.WebSettings#setGeolocationEnabled(boolean): Method 'setGeolocationEnabled' documentation mentions permissions without declaring @RequiresPermission -StaticUtils: ExtensionCaptureRequest: - Fully-static utility classes must not have constructor -StaticUtils: android.hardware.camera2.ExtensionCaptureRequest: - Fully-static utility classes must not have constructor -StaticUtils: android.hardware.camera2.ExtensionCaptureResult: - Fully-static utility classes must not have constructor - - Todo: android.hardware.camera2.params.StreamConfigurationMap: Documentation mentions 'TODO' Todo: android.provider.ContactsContract.RawContacts#newEntityIterator(android.database.Cursor): @@ -1462,14 +1446,6 @@ UnflaggedApi: android.graphics.text.PositionedGlyphs#getItalicOverride(int): UnflaggedApi: android.graphics.text.PositionedGlyphs#getWeightOverride(int): New API must be flagged with @FlaggedApi: method android.graphics.text.PositionedGlyphs.getWeightOverride(int) -UnflaggedApi: android.hardware.camera2.ExtensionCaptureRequest: - New API must be flagged with @FlaggedApi: class android.hardware.camera2.ExtensionCaptureRequest -UnflaggedApi: android.hardware.camera2.ExtensionCaptureRequest#ExtensionCaptureRequest(): - New API must be flagged with @FlaggedApi: constructor android.hardware.camera2.ExtensionCaptureRequest() -UnflaggedApi: android.hardware.camera2.ExtensionCaptureResult: - New API must be flagged with @FlaggedApi: class android.hardware.camera2.ExtensionCaptureResult -UnflaggedApi: android.hardware.camera2.ExtensionCaptureResult#ExtensionCaptureResult(): - New API must be flagged with @FlaggedApi: constructor android.hardware.camera2.ExtensionCaptureResult() UnflaggedApi: android.media.MediaRoute2Info#TYPE_REMOTE_CAR: New API must be flagged with @FlaggedApi: field android.media.MediaRoute2Info.TYPE_REMOTE_CAR UnflaggedApi: android.media.MediaRoute2Info#TYPE_REMOTE_COMPUTER: diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 14ae3f543436..d03dd1623074 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -46,6 +46,7 @@ package android { field public static final String REMAP_MODIFIER_KEYS = "android.permission.REMAP_MODIFIER_KEYS"; field public static final String REMOVE_TASKS = "android.permission.REMOVE_TASKS"; field public static final String REQUEST_UNIQUE_ID_ATTESTATION = "android.permission.REQUEST_UNIQUE_ID_ATTESTATION"; + field public static final String RESERVED_FOR_TESTING_SIGNATURE = "android.permission.RESERVED_FOR_TESTING_SIGNATURE"; field public static final String RESET_APP_ERRORS = "android.permission.RESET_APP_ERRORS"; field public static final String REVOKE_POST_NOTIFICATIONS_WITHOUT_KILL = "android.permission.REVOKE_POST_NOTIFICATIONS_WITHOUT_KILL"; field public static final String SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS = "android.permission.SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS"; diff --git a/core/api/test-lint-baseline.txt b/core/api/test-lint-baseline.txt index 6cc71e5450ae..b4a3abc4ad22 100644 --- a/core/api/test-lint-baseline.txt +++ b/core/api/test-lint-baseline.txt @@ -1973,6 +1973,8 @@ Todo: android.window.WindowContainerTransaction#setActivityWindowingMode(android UnflaggedApi: android.Manifest.permission#MANAGE_REMOTE_AUTH: New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_REMOTE_AUTH +UnflaggedApi: android.Manifest.permission#RESERVED_FOR_TESTING_SIGNATURE: + New API must be flagged with @FlaggedApi: field android.Manifest.permission.RESERVED_FOR_TESTING_SIGNATURE UnflaggedApi: android.Manifest.permission#START_ACTIVITIES_FROM_SDK_SANDBOX: New API must be flagged with @FlaggedApi: field android.Manifest.permission.START_ACTIVITIES_FROM_SDK_SANDBOX UnflaggedApi: android.Manifest.permission#USE_REMOTE_AUTH: diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 79e2bd437c3e..1c247a6a8b39 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -9291,11 +9291,11 @@ public class Activity extends ContextThemeWrapper if (DEBUG_LIFECYCLE) Slog.v(TAG, "dispatchMultiWindowModeChanged " + this + ": " + isInMultiWindowMode + " " + newConfig); + mIsInMultiWindowMode = isInMultiWindowMode; mFragments.dispatchMultiWindowModeChanged(isInMultiWindowMode, newConfig); if (mWindow != null) { mWindow.onMultiWindowModeChanged(); } - mIsInMultiWindowMode = isInMultiWindowMode; onMultiWindowModeChanged(isInMultiWindowMode, newConfig); } @@ -9304,11 +9304,11 @@ public class Activity extends ContextThemeWrapper if (DEBUG_LIFECYCLE) Slog.v(TAG, "dispatchPictureInPictureModeChanged " + this + ": " + isInPictureInPictureMode + " " + newConfig); + mIsInPictureInPictureMode = isInPictureInPictureMode; mFragments.dispatchPictureInPictureModeChanged(isInPictureInPictureMode, newConfig); if (mWindow != null) { mWindow.onPictureInPictureModeChanged(isInPictureInPictureMode); } - mIsInPictureInPictureMode = isInPictureInPictureMode; onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig); } diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java index 97852528d014..83c3bf6eda76 100644 --- a/core/java/android/app/ActivityManagerInternal.java +++ b/core/java/android/app/ActivityManagerInternal.java @@ -477,6 +477,11 @@ public abstract class ActivityManagerInternal { */ public static final int OOM_ADJ_REASON_COMPONENT_DISABLED = 22; + /** + * Oom Adj Reason: Follow up update for time sensitive state evaluations. + */ + public static final int OOM_ADJ_REASON_FOLLOW_UP = 23; + @IntDef(prefix = {"OOM_ADJ_REASON_"}, value = { OOM_ADJ_REASON_NONE, OOM_ADJ_REASON_ACTIVITY, @@ -501,6 +506,7 @@ public abstract class ActivityManagerInternal { OOM_ADJ_REASON_EXECUTING_SERVICE, OOM_ADJ_REASON_RESTRICTION_CHANGE, OOM_ADJ_REASON_COMPONENT_DISABLED, + OOM_ADJ_REASON_FOLLOW_UP, }) @Retention(RetentionPolicy.SOURCE) public @interface OomAdjReason {} diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl index 15b13dc97554..ffb920b907ab 100644 --- a/core/java/android/app/IActivityManager.aidl +++ b/core/java/android/app/IActivityManager.aidl @@ -757,6 +757,15 @@ interface IActivityManager { void addStartInfoTimestamp(int key, long timestampNs, int userId); /** + * Reports view related timestamps to be added to the calling apps most + * recent {@link ApplicationStartInfo}. + * + * @param renderThreadDrawStartTimeNs Clock monotonic time in nanoseconds of RenderThread draw start + * @param framePresentedTimeNs Clock monotonic time in nanoseconds of frame presented + */ + oneway void reportStartInfoViewTimestamps(long renderThreadDrawStartTimeNs, long framePresentedTimeNs); + + /** * Return a list of {@link ApplicationExitInfo} records. * * <p class="note"> Note: System stores these historical information in a ring buffer, older diff --git a/core/java/android/app/INotificationManager.aidl b/core/java/android/app/INotificationManager.aidl index cf0641651d66..5bc0ddc48c8b 100644 --- a/core/java/android/app/INotificationManager.aidl +++ b/core/java/android/app/INotificationManager.aidl @@ -42,6 +42,7 @@ import android.service.notification.StatusBarNotification; import android.service.notification.ZenPolicy; import android.app.AutomaticZenRule; import android.service.notification.ZenModeConfig; +import android.service.notification.ZenDeviceEffects; /** {@hide} */ interface INotificationManager @@ -227,6 +228,7 @@ interface INotificationManager int getRuleInstanceCount(in ComponentName owner); int getAutomaticZenRuleState(String id); void setAutomaticZenRuleState(String id, in Condition condition); + void setManualZenRuleDeviceEffects(in ZenDeviceEffects effects); byte[] getBackupPayload(int user); void applyRestore(in byte[] payload, int user); diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java index fc3bb0288d67..a1fa4047f839 100644 --- a/core/java/android/app/Notification.java +++ b/core/java/android/app/Notification.java @@ -2239,9 +2239,6 @@ public class Notification implements Parcelable private void visitUris(@NonNull Consumer<Uri> visitor) { visitIconUri(visitor, getIcon()); - if (actionIntent != null) { - actionIntent.visitUris(visitor); - } } @Override @@ -2957,21 +2954,6 @@ public class Notification implements Parcelable } } - // allPendingIntents should contain all associated intents after parcelling, but it may also - // contain intents added by the app to extras for their own purposes. We only care about - // checking the intents known and used by system_server, to avoid the confused deputy issue. - List<PendingIntent> pendingIntents = Arrays.asList(contentIntent, deleteIntent, - fullScreenIntent); - for (PendingIntent intent : pendingIntents) { - if (intent != null) { - intent.visitUris(visitor); - } - } - - if (mBubbleMetadata != null) { - mBubbleMetadata.visitUris(visitor); - } - if (extras != null) { visitIconUri(visitor, extras.getParcelable(EXTRA_LARGE_ICON_BIG, Icon.class)); visitIconUri(visitor, extras.getParcelable(EXTRA_PICTURE_ICON, Icon.class)); @@ -3043,28 +3025,15 @@ public class Notification implements Parcelable callPerson.visitUris(visitor); } visitIconUri(visitor, extras.getParcelable(EXTRA_VERIFICATION_ICON, Icon.class)); + } - // Extras for MediaStyle. - PendingIntent deviceIntent = extras.getParcelable(EXTRA_MEDIA_REMOTE_INTENT, - PendingIntent.class); - if (deviceIntent != null) { - deviceIntent.visitUris(visitor); - } - - if (extras.containsKey(WearableExtender.EXTRA_WEARABLE_EXTENSIONS)) { - WearableExtender extender = new WearableExtender(this); - extender.visitUris(visitor); - } - - if (extras.containsKey(TvExtender.EXTRA_TV_EXTENDER)) { - TvExtender extender = new TvExtender(this); - extender.visitUris(visitor); - } + if (mBubbleMetadata != null) { + visitIconUri(visitor, mBubbleMetadata.getIcon()); + } - if (extras.containsKey(CarExtender.EXTRA_CAR_EXTENDER)) { - CarExtender extender = new CarExtender(this); - extender.visitUris(visitor); - } + if (extras != null && extras.containsKey(WearableExtender.EXTRA_WEARABLE_EXTENSIONS)) { + WearableExtender extender = new WearableExtender(this); + extender.visitUris(visitor); } } @@ -11459,16 +11428,6 @@ public class Notification implements Parcelable } } - private void visitUris(@NonNull Consumer<Uri> visitor) { - visitIconUri(visitor, getIcon()); - if (mPendingIntent != null) { - mPendingIntent.visitUris(visitor); - } - if (mDeleteIntent != null) { - mDeleteIntent.visitUris(visitor); - } - } - /** * Builder to construct a {@link BubbleMetadata} object. */ @@ -12667,9 +12626,6 @@ public class Notification implements Parcelable } private void visitUris(@NonNull Consumer<Uri> visitor) { - if (mDisplayIntent != null) { - mDisplayIntent.visitUris(visitor); - } for (Action action : mActions) { action.visitUris(visitor); } @@ -12829,12 +12785,6 @@ public class Notification implements Parcelable return mUnreadConversation; } - private void visitUris(@NonNull Consumer<Uri> visitor) { - if (mUnreadConversation != null) { - mUnreadConversation.visitUris(visitor); - } - } - /** * A class which holds the unread messages from a conversation. */ @@ -12986,16 +12936,7 @@ public class Notification implements Parcelable onRead, participants, b.getLong(KEY_TIMESTAMP)); } - - private void visitUris(@NonNull Consumer<Uri> visitor) { - if (mReadPendingIntent != null) { - mReadPendingIntent.visitUris(visitor); - } - if (mReplyPendingIntent != null) { - mReplyPendingIntent.visitUris(visitor); - } - } - } + }; /** * Builder class for {@link CarExtender.UnreadConversation} objects. @@ -13318,15 +13259,6 @@ public class Notification implements Parcelable public boolean isSuppressShowOverApps() { return mSuppressShowOverApps; } - - private void visitUris(@NonNull Consumer<Uri> visitor) { - if (mContentIntent != null) { - mContentIntent.visitUris(visitor); - } - if (mDeleteIntent != null) { - mDeleteIntent.visitUris(visitor); - } - } } /** diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java index e4310c1a5361..bd80dc1e3c60 100644 --- a/core/java/android/app/NotificationManager.java +++ b/core/java/android/app/NotificationManager.java @@ -57,6 +57,7 @@ import android.provider.Settings.Global; import android.service.notification.Adjustment; import android.service.notification.Condition; import android.service.notification.StatusBarNotification; +import android.service.notification.ZenDeviceEffects; import android.service.notification.ZenModeConfig; import android.service.notification.ZenPolicy; import android.util.Log; @@ -1828,6 +1829,18 @@ public class NotificationManager { throw e.rethrowFromSystemServer(); } } + /** + * @hide + */ + @FlaggedApi(Flags.FLAG_MODES_UI) + public void setManualZenRuleDeviceEffects(@NonNull ZenDeviceEffects effects) { + INotificationManager service = getService(); + try { + service.setManualZenRuleDeviceEffects(effects); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } /** * For apps targeting {@link Build.VERSION_CODES#VANILLA_ICE_CREAM} and above, the diff --git a/core/java/android/app/PendingIntent.java b/core/java/android/app/PendingIntent.java index 1ac08ac4cd24..3714e5de23ca 100644 --- a/core/java/android/app/PendingIntent.java +++ b/core/java/android/app/PendingIntent.java @@ -44,8 +44,6 @@ import android.content.IntentSender; import android.content.pm.PackageManager.ResolveInfoFlagsBits; import android.content.pm.ParceledListSlice; import android.content.pm.ResolveInfo; -import android.net.Uri; -import android.os.Binder; import android.os.Build; import android.os.Bundle; import android.os.Handler; @@ -71,7 +69,6 @@ import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.concurrent.Executor; -import java.util.function.Consumer; /** * A description of an Intent and target action to perform with it. Instances @@ -1471,21 +1468,6 @@ public final class PendingIntent implements Parcelable { return sb.toString(); } - /** - * See {@link Intent#visitUris(Consumer)}. - * - * @hide - */ - public void visitUris(@NonNull Consumer<Uri> visitor) { - if (android.app.Flags.visitRiskyUris()) { - Intent intent = Binder.withCleanCallingIdentity(this::getIntent); - - if (intent != null) { - intent.visitUris(visitor); - } - } - } - /** @hide */ public void dumpDebug(ProtoOutputStream proto, long fieldId) { final long token = proto.start(fieldId); diff --git a/core/java/android/app/Person.java b/core/java/android/app/Person.java index 04186600125f..96f6f4eac372 100644 --- a/core/java/android/app/Person.java +++ b/core/java/android/app/Person.java @@ -189,7 +189,7 @@ public final class Person implements Parcelable { */ public void visitUris(@NonNull Consumer<Uri> visitor) { visitor.accept(getIconUri()); - if (Flags.visitRiskyUris()) { + if (Flags.visitPersonUri()) { if (mUri != null && !mUri.isEmpty()) { visitor.accept(Uri.parse(mUri)); } diff --git a/core/java/android/app/assist/AssistContent.java b/core/java/android/app/assist/AssistContent.java index e5316bc05749..a48868906487 100644 --- a/core/java/android/app/assist/AssistContent.java +++ b/core/java/android/app/assist/AssistContent.java @@ -34,12 +34,22 @@ public class AssistContent implements Parcelable { } /** + * Create an AssistContent with extras initialized. + * * @hide + */ + public AssistContent(@android.annotation.NonNull Bundle extras) { + mExtras = extras; + } + + /** * Called by {@link android.app.ActivityThread} to set the default Intent based on * {@link android.app.Activity#getIntent Activity.getIntent}. * * <p>Automatically populates {@link #mUri} if that Intent is an {@link Intent#ACTION_VIEW} * of a web (http or https scheme) URI.</p> + * + * @hide */ public void setDefaultIntent(Intent intent) { mIntent = intent; diff --git a/core/java/android/app/contextualsearch/flags.aconfig b/core/java/android/app/contextualsearch/flags.aconfig index 3385b2b42074..3b0c86795738 100644 --- a/core/java/android/app/contextualsearch/flags.aconfig +++ b/core/java/android/app/contextualsearch/flags.aconfig @@ -7,3 +7,9 @@ flag { description: "Flag to enable the service" bug: "309689654" } +flag { + name: "enable_token_refresh" + namespace: "machine_learning" + description: "Flag to refresh the token to the callback" + bug: "309689654" +}
\ No newline at end of file diff --git a/core/java/android/app/notification.aconfig b/core/java/android/app/notification.aconfig index 73ac2630427a..f751a234ccac 100644 --- a/core/java/android/app/notification.aconfig +++ b/core/java/android/app/notification.aconfig @@ -46,10 +46,13 @@ flag { } flag { - name: "visit_risky_uris" + name: "visit_person_uri" namespace: "systemui" - description: "Guards the security fix that ensures all URIs in intents and Person.java are valid" + description: "Guards the security fix that ensures all URIs Person.java are valid" bug: "281044385" + metadata { + purpose: PURPOSE_BUGFIX + } } # vvv Prototypes for using app icons in notifications vvv diff --git a/core/java/android/companion/ObservingDevicePresenceRequest.java b/core/java/android/companion/ObservingDevicePresenceRequest.java index 11ea735dff4f..3150b8735033 100644 --- a/core/java/android/companion/ObservingDevicePresenceRequest.java +++ b/core/java/android/companion/ObservingDevicePresenceRequest.java @@ -180,6 +180,9 @@ public final class ObservingDevicePresenceRequest implements Parcelable { * <p>Calling apps must use either this API or {@link #setAssociationId(int)}, * but not both.</p> * + * <p>Calling app must hold the + * {@link AssociationRequest#DEVICE_PROFILE_AUTOMOTIVE_PROJECTION} profile.</p> + * * @param uuid The ParcelUuid for observing device presence. */ @NonNull diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 02d62a2a402b..2e60cb03d18b 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -106,7 +106,6 @@ 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 @@ -8330,27 +8329,6 @@ public class Intent implements Parcelable, Cloneable { } } - /** - * Note all {@link Uri} that are referenced internally, with the expectation that Uri permission - * grants will need to be issued to ensure the recipient of this object is able to render its - * contents. - * See b/281044385 for more context and examples about what happens when this isn't done - * correctly. - * - * @hide - */ - public void visitUris(@NonNull Consumer<Uri> visitor) { - if (android.app.Flags.visitRiskyUris()) { - visitor.accept(mData); - if (mSelector != null) { - mSelector.visitUris(visitor); - } - if (mOriginalIntent != null) { - mOriginalIntent.visitUris(visitor); - } - } - } - public static Intent getIntentOld(String uri) throws URISyntaxException { Intent intent = getIntentOld(uri, 0); intent.mLocalFlags |= LOCAL_FLAG_FROM_URI; diff --git a/core/java/android/content/pm/LauncherApps.java b/core/java/android/content/pm/LauncherApps.java index 93cc71b34e47..df27f9d4e457 100644 --- a/core/java/android/content/pm/LauncherApps.java +++ b/core/java/android/content/pm/LauncherApps.java @@ -696,14 +696,11 @@ public class LauncherApps { * Otherwise it'll return the same list as {@link UserManager#getUserProfiles()} would. * * <p>To get hidden profile {@link UserManager#USER_TYPE_PROFILE_PRIVATE}, - * caller should have either:</p> - * <ul> - * <li>the privileged {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} - * permission</li> - * <li>the normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES} permission and the - * {@link android.app.role.RoleManager#ROLE_HOME} role. </li> - * </ul> + * caller should have normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES} + * permission and the {@link android.app.role.RoleManager#ROLE_HOME} role. */ + // Alternatively, a system app can access this api for private profile if they've been granted + // with the {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} permission. @SuppressLint("RequiresPermission") @RequiresPermission(conditional = true, anyOf = {ACCESS_HIDDEN_PROFILES_FULL, ACCESS_HIDDEN_PROFILES}) @@ -765,19 +762,16 @@ public class LauncherApps { * </ul> * * <p>If the user in question is a hidden profile {@link UserManager#USER_TYPE_PROFILE_PRIVATE}, - * caller should have either:</p> - * <ul> - * <li>the privileged {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} - * permission</li> - * <li>the normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES} permission and the - * {@link android.app.role.RoleManager#ROLE_HOME} role. </li> - * </ul> + * caller should have normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES} + * permission and the {@link android.app.role.RoleManager#ROLE_HOME} role. * * @param packageName The specific package to query. If null, it checks all installed packages * in the profile. * @param user The UserHandle of the profile. * @return List of launchable activities. Can be an empty list but will not be null. */ + // Alternatively, a system app can access this api for private profile if they've been granted + // with the {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} permission. @SuppressLint("RequiresPermission") @RequiresPermission(conditional = true, anyOf = {ACCESS_HIDDEN_PROFILES_FULL, ACCESS_HIDDEN_PROFILES}) @@ -821,18 +815,15 @@ public class LauncherApps { * to distinguish it from other users (eg, badges). * * <p>If the user in question is a hidden profile {@link UserManager#USER_TYPE_PROFILE_PRIVATE}, - * caller should have either:</p> - * <ul> - * <li>the privileged {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} - * permission</li> - * <li>the normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES} permission and the - * {@link android.app.role.RoleManager#ROLE_HOME} role. </li> - * </ul> + * caller should have normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES} + * permission and the {@link android.app.role.RoleManager#ROLE_HOME} role. * * @param userHandle user handle of the user for which LauncherUserInfo is requested. * @return the {@link LauncherUserInfo} object related to the user specified, null in case * the user is inaccessible. */ + // Alternatively, a system app can access this api for private profile if they've been granted + // with the {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} permission. @Nullable @SuppressLint("RequiresPermission") @FlaggedApi(Flags.FLAG_ALLOW_PRIVATE_PROFILE) @@ -874,13 +865,8 @@ public class LauncherApps { * </p> * * <p>If the user in question is a hidden profile {@link UserManager#USER_TYPE_PROFILE_PRIVATE}, - * caller should have either:</p> - * <ul> - * <li>the privileged {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} - * permission</li> - * <li>the normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES} permission and the - * {@link android.app.role.RoleManager#ROLE_HOME} role. </li> - * </ul> + * caller should have normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES} + * permission and the {@link android.app.role.RoleManager#ROLE_HOME} role. * * @param packageName the package for which intent sender to launch App Market Activity is * required. @@ -888,6 +874,8 @@ public class LauncherApps { * @return {@link IntentSender} object which launches the App Market Activity, null in case * there is no such activity. */ + // Alternatively, a system app can access this api for private profile if they've been granted + // with the {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} permission. @Nullable @SuppressLint("RequiresPermission") @FlaggedApi(Flags.FLAG_ALLOW_PRIVATE_PROFILE) @@ -914,18 +902,15 @@ public class LauncherApps { * user at creation. * * <p>If the user in question is a hidden profile {@link UserManager#USER_TYPE_PROFILE_PRIVATE}, - * caller should have either:</p> - * <ul> - * <li>the privileged {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} - * permission</li> - * <li>the normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES} permission and the - * {@link android.app.role.RoleManager#ROLE_HOME} role. </li> - * </ul> + * caller should have normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES} + * permission and the {@link android.app.role.RoleManager#ROLE_HOME} role. * * @param userHandle the user for which installed system packages are required. * @return {@link List} of {@link String}, representing the package name of the installed * package. Can be empty but not null. */ + // Alternatively, a system app can access this api for private profile if they've been granted + // with the {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} permission. @FlaggedApi(Flags.FLAG_ALLOW_PRIVATE_PROFILE) @NonNull @SuppressLint("RequiresPermission") @@ -952,6 +937,8 @@ public class LauncherApps { * successful, null otherwise. * @hide */ + // Alternatively, a system app can access this api for private profile if they've been granted + // with the {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} permission. @Nullable @FlaggedApi(Flags.FLAG_ALLOW_PRIVATE_PROFILE) @RequiresPermission(conditional = true, @@ -969,18 +956,15 @@ public class LauncherApps { * returns null. * * <p>If the user in question is a hidden profile {@link UserManager#USER_TYPE_PROFILE_PRIVATE}, - * caller should have either:</p> - * <ul> - * <li>the privileged {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} - * permission</li> - * <li>the normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES} permission and the - * {@link android.app.role.RoleManager#ROLE_HOME} role. </li> - * </ul> + * caller should have normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES} + * permission and the {@link android.app.role.RoleManager#ROLE_HOME} role. * * @param intent The intent to find a match for. * @param user The profile to look in for a match. * @return An activity info object if there is a match. */ + // Alternatively, a system app can access this api for private profile if they've been granted + // with the {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} permission. @SuppressLint("RequiresPermission") @RequiresPermission(conditional = true, anyOf = {ACCESS_HIDDEN_PROFILES_FULL, ACCESS_HIDDEN_PROFILES}) @@ -1034,19 +1018,16 @@ public class LauncherApps { * Starts a Main activity in the specified profile. * * <p>If the user in question is a hidden profile {@link UserManager#USER_TYPE_PROFILE_PRIVATE}, - * caller should have either:</p> - * <ul> - * <li>the privileged {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} - * permission</li> - * <li>the normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES} permission and the - * {@link android.app.role.RoleManager#ROLE_HOME} role. </li> - * </ul> + * caller should have normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES} + * permission and the {@link android.app.role.RoleManager#ROLE_HOME} role. * * @param component The ComponentName of the activity to launch * @param user The UserHandle of the profile * @param sourceBounds The Rect containing the source bounds of the clicked icon * @param opts Options to pass to startActivity */ + // Alternatively, a system app can access this api for private profile if they've been granted + // with the {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} permission. @SuppressLint("RequiresPermission") @RequiresPermission(conditional = true, anyOf = {ACCESS_HIDDEN_PROFILES_FULL, ACCESS_HIDDEN_PROFILES}) @@ -1088,19 +1069,16 @@ public class LauncherApps { * package in the specified profile. * * <p>If the user in question is a hidden profile {@link UserManager#USER_TYPE_PROFILE_PRIVATE}, - * caller should have either:</p> - * <ul> - * <li>the privileged {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} - * permission</li> - * <li>the normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES} permission and the - * {@link android.app.role.RoleManager#ROLE_HOME} role. </li> - * </ul> + * caller should have normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES} + * permission and the {@link android.app.role.RoleManager#ROLE_HOME} role. * * @param component The ComponentName of the package to launch settings for. * @param user The UserHandle of the profile * @param sourceBounds The Rect containing the source bounds of the clicked icon * @param opts Options to pass to startActivity */ + // Alternatively, a system app can access this api for private profile if they've been granted + // with the {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} permission. @SuppressLint("RequiresPermission") @RequiresPermission(conditional = true, anyOf = {ACCESS_HIDDEN_PROFILES_FULL, ACCESS_HIDDEN_PROFILES}) @@ -1216,19 +1194,16 @@ public class LauncherApps { * Checks if the package is installed and enabled for a profile. * * <p>If the user in question is a hidden profile {@link UserManager#USER_TYPE_PROFILE_PRIVATE}, - * caller should have either:</p> - * <ul> - * <li>the privileged {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} - * permission</li> - * <li>the normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES} permission and the - * {@link android.app.role.RoleManager#ROLE_HOME} role. </li> - * </ul> + * caller should have normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES} + * permission and the {@link android.app.role.RoleManager#ROLE_HOME} role. * * @param packageName The package to check. * @param user The UserHandle of the profile. * * @return true if the package exists and is enabled. */ + // Alternatively, a system app can access this api for private profile if they've been granted + // with the {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} permission. @SuppressLint("RequiresPermission") @RequiresPermission(conditional = true, anyOf = {ACCESS_HIDDEN_PROFILES_FULL, ACCESS_HIDDEN_PROFILES}) @@ -1250,13 +1225,8 @@ public class LauncherApps { * app and the launcher. * * <p>If the user in question is a hidden profile {@link UserManager#USER_TYPE_PROFILE_PRIVATE}, - * caller should have either:</p> - * <ul> - * <li>the privileged {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} - * permission</li> - * <li>the normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES} permission and the - * {@link android.app.role.RoleManager#ROLE_HOME} role. </li> - * </ul> + * caller should have normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES} + * permission and the {@link android.app.role.RoleManager#ROLE_HOME} role. * * <p>Note: This just returns whatever extras were provided to the system, <em>which might * even be {@code null}.</em> @@ -1269,6 +1239,8 @@ public class LauncherApps { * @see Callback#onPackagesSuspended(String[], UserHandle, Bundle) * @see PackageManager#isPackageSuspended() */ + // Alternatively, a system app can access this api for private profile if they've been granted + // with the {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} permission. @SuppressLint("RequiresPermission") @RequiresPermission(conditional = true, anyOf = {ACCESS_HIDDEN_PROFILES_FULL, ACCESS_HIDDEN_PROFILES}) @@ -1287,18 +1259,15 @@ public class LauncherApps { * {@code PackageManager.setDistractingPackageRestrictions(String[], int)}. * * <p>If the user in question is a hidden profile {@link UserManager#USER_TYPE_PROFILE_PRIVATE}, - * caller should have either:</p> - * <ul> - * <li>the privileged {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} - * permission</li> - * <li>the normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES} permission and the - * {@link android.app.role.RoleManager#ROLE_HOME} role. </li> - * </ul> + * caller should have normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES} + * permission and the {@link android.app.role.RoleManager#ROLE_HOME} role. * * @param packageName The package for which to check. * @param user the {@link UserHandle} of the profile. * @return */ + // Alternatively, a system app can access this api for private profile if they've been granted + // with the {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} permission. @SuppressLint("RequiresPermission") @RequiresPermission(conditional = true, anyOf = {ACCESS_HIDDEN_PROFILES_FULL, ACCESS_HIDDEN_PROFILES}) @@ -1317,13 +1286,8 @@ public class LauncherApps { * Returns {@link ApplicationInfo} about an application installed for a specific user profile. * * <p>If the user in question is a hidden profile {@link UserManager#USER_TYPE_PROFILE_PRIVATE}, - * caller should have either:</p> - * <ul> - * <li>the privileged {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} - * permission</li> - * <li>the normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES} permission and the - * {@link android.app.role.RoleManager#ROLE_HOME} role. </li> - * </ul> + * caller should have normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES} + * permission and the {@link android.app.role.RoleManager#ROLE_HOME} role. * * @param packageName The package name of the application * @param flags Additional option flags {@link PackageManager#getApplicationInfo} @@ -1333,6 +1297,8 @@ public class LauncherApps { * {@code null} if the package isn't installed for the given profile, or the profile * isn't enabled. */ + // Alternatively, a system app can access this api for private profile if they've been granted + // with the {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} permission. @SuppressLint("RequiresPermission") @RequiresPermission(conditional = true, anyOf = {ACCESS_HIDDEN_PROFILES_FULL, ACCESS_HIDDEN_PROFILES}) @@ -1386,19 +1352,16 @@ public class LauncherApps { * throw a {@link SecurityException} unless the caller has the same UID as the target app's. * * <p>If the user in question is a hidden profile {@link UserManager#USER_TYPE_PROFILE_PRIVATE}, - * caller should have either:</p> - * <ul> - * <li>the privileged {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} - * permission</li> - * <li>the normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES} permission and the - * {@link android.app.role.RoleManager#ROLE_HOME} role. </li> - * </ul> + * caller should have normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES} + * permission and the {@link android.app.role.RoleManager#ROLE_HOME} role. * * @param component The activity to check. * @param user The UserHandle of the profile. * * @return true if the activity exists and is enabled. */ + // Alternatively, a system app can access this api for private profile if they've been granted + // with the {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} permission. @SuppressLint("RequiresPermission") @RequiresPermission(conditional = true, anyOf = {ACCESS_HIDDEN_PROFILES_FULL, ACCESS_HIDDEN_PROFILES}) @@ -1961,16 +1924,13 @@ public class LauncherApps { * Registers a callback for changes to packages in this user and managed profiles. * * <p>To receive callbacks for hidden profile {@link UserManager#USER_TYPE_PROFILE_PRIVATE}, - * caller should have either:</p> - * <ul> - * <li>the privileged {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} - * permission</li> - * <li>the normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES} permission and the - * {@link android.app.role.RoleManager#ROLE_HOME} role. </li> - * </ul> + * caller should have normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES} + * permission and the {@link android.app.role.RoleManager#ROLE_HOME} role. * * @param callback The callback to register. */ + // Alternatively, a system app can access this api for private profile if they've been granted + // with the {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} permission. @SuppressLint("RequiresPermission") @RequiresPermission(conditional = true, anyOf = {ACCESS_HIDDEN_PROFILES_FULL, ACCESS_HIDDEN_PROFILES}) @@ -1982,17 +1942,14 @@ public class LauncherApps { * Registers a callback for changes to packages in this user and managed profiles. * * <p>To receive callbacks for hidden profile {@link UserManager#USER_TYPE_PROFILE_PRIVATE}, - * caller should have either:</p> - * <ul> - * <li>the privileged {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} - * permission</li> - * <li>the normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES} permission and the - * {@link android.app.role.RoleManager#ROLE_HOME} role. </li> - * </ul> + * caller should have normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES} + * permission and the {@link android.app.role.RoleManager#ROLE_HOME} role. * * @param callback The callback to register. * @param handler that should be used to post callbacks on, may be null. */ + // Alternatively, a system app can access this api for private profile if they've been granted + // with the {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} permission. @SuppressLint("RequiresPermission") @RequiresPermission(conditional = true, anyOf = {ACCESS_HIDDEN_PROFILES_FULL, ACCESS_HIDDEN_PROFILES}) @@ -2447,13 +2404,8 @@ public class LauncherApps { * the session owner to retrieve these details. * * <p>To receive callbacks for hidden profile {@link UserManager#USER_TYPE_PROFILE_PRIVATE}, - * caller should have either:</p> - * <ul> - * <li>the privileged {@code android.Manifest.permission#ACCESS_HIDDEN_PROFILES_FULL} - * permission</li> - * <li>the normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES} permission and the - * {@link android.app.role.RoleManager#ROLE_HOME} role. </li> - * </ul> + * caller should have normal {@link android.Manifest.permission#ACCESS_HIDDEN_PROFILES} + * permission and the {@link android.app.role.RoleManager#ROLE_HOME} role. * * @see PackageInstaller#getAllSessions() */ diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java index e2159f7eb9de..37a2df83cc00 100644 --- a/core/java/android/hardware/biometrics/BiometricPrompt.java +++ b/core/java/android/hardware/biometrics/BiometricPrompt.java @@ -200,7 +200,10 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan throw new IllegalStateException( "Exclusively one of logo resource or logo bitmap can be set"); } - mPromptInfo.setLogo(logoRes, convertDrawableToBitmap(mContext.getDrawable(logoRes))); + if (logoRes != 0) { + mPromptInfo.setLogo(logoRes, + convertDrawableToBitmap(mContext.getDrawable(logoRes))); + } return this; } @@ -219,11 +222,11 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan @RequiresPermission(SET_BIOMETRIC_DIALOG_ADVANCED) @NonNull public BiometricPrompt.Builder setLogoBitmap(@NonNull Bitmap logoBitmap) { - if (mPromptInfo.getLogoRes() != -1) { + if (mPromptInfo.getLogoRes() != 0) { throw new IllegalStateException( "Exclusively one of logo resource or logo bitmap can be set"); } - mPromptInfo.setLogo(-1, logoBitmap); + mPromptInfo.setLogo(0, logoBitmap); return this; } @@ -832,7 +835,7 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan * Gets the drawable resource of the logo for the prompt, as set by * {@link Builder#setLogoRes(int)}. Currently for system applications use only. * - * @return The drawable resource of the logo, or -1 if the prompt has no logo resource set. + * @return The drawable resource of the logo, or 0 if the prompt has no logo resource set. */ @FlaggedApi(FLAG_CUSTOM_BIOMETRIC_PROMPT) @RequiresPermission(SET_BIOMETRIC_DIALOG_ADVANCED) diff --git a/core/java/android/hardware/biometrics/PromptInfo.java b/core/java/android/hardware/biometrics/PromptInfo.java index f4a3c877ecdc..ba9f30dfcf4b 100644 --- a/core/java/android/hardware/biometrics/PromptInfo.java +++ b/core/java/android/hardware/biometrics/PromptInfo.java @@ -33,7 +33,7 @@ import java.util.List; */ public class PromptInfo implements Parcelable { - @DrawableRes private int mLogoRes = -1; + @DrawableRes private int mLogoRes; @Nullable private Bitmap mLogoBitmap; @Nullable private String mLogoDescription; @NonNull private CharSequence mTitle; @@ -187,7 +187,7 @@ public class PromptInfo implements Parcelable { * this permission. */ public boolean requiresAdvancedPermission() { - if (mLogoRes != -1) { + if (mLogoRes != 0) { return true; } else if (mLogoBitmap != null) { return true; @@ -221,7 +221,7 @@ public class PromptInfo implements Parcelable { /** * Sets logo res and bitmap * - * @param logoRes The logo res set by the app; Or -1 if the app sets bitmap directly. + * @param logoRes The logo res set by the app; Or 0 if the app sets bitmap directly. * @param logoBitmap The bitmap from logoRes if the app sets logoRes; Or the bitmap set by the * app directly. */ @@ -351,7 +351,7 @@ public class PromptInfo implements Parcelable { public Bitmap getLogoBitmap() { // If mLogoRes has been set, return null since mLogoBitmap is from the res, but not from // the app directly. - return mLogoRes == -1 ? mLogoBitmap : null; + return mLogoRes == 0 ? mLogoBitmap : null; } public String getLogoDescription() { diff --git a/core/java/android/hardware/camera2/ExtensionCaptureRequest.java b/core/java/android/hardware/camera2/ExtensionCaptureRequest.java index c33956b59f2f..b681ce40dfd9 100644 --- a/core/java/android/hardware/camera2/ExtensionCaptureRequest.java +++ b/core/java/android/hardware/camera2/ExtensionCaptureRequest.java @@ -18,7 +18,6 @@ package android.hardware.camera2; import android.annotation.FlaggedApi; import android.annotation.NonNull; -import android.hardware.camera2.CameraCharacteristics; import android.hardware.camera2.CaptureRequest; import android.hardware.camera2.CaptureRequest.Key; import android.hardware.camera2.impl.ExtensionKey; @@ -43,6 +42,9 @@ import com.android.internal.camera.flags.Flags; @FlaggedApi(Flags.FLAG_CONCERT_MODE_API) public final class ExtensionCaptureRequest { + /** To avoid exposing constructor */ + private ExtensionCaptureRequest() {} + /** * <p>Used to apply an additional digital zoom factor for the * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY } diff --git a/core/java/android/hardware/camera2/ExtensionCaptureResult.java b/core/java/android/hardware/camera2/ExtensionCaptureResult.java index 95feb2fd268a..b7ba78cb34b5 100644 --- a/core/java/android/hardware/camera2/ExtensionCaptureResult.java +++ b/core/java/android/hardware/camera2/ExtensionCaptureResult.java @@ -18,7 +18,6 @@ package android.hardware.camera2; import android.annotation.FlaggedApi; import android.annotation.NonNull; -import android.hardware.camera2.CameraCharacteristics; import android.hardware.camera2.CameraExtensionCharacteristics; import android.hardware.camera2.CaptureResult; import android.hardware.camera2.CaptureResult.Key; @@ -45,6 +44,9 @@ import com.android.internal.camera.flags.Flags; @FlaggedApi(Flags.FLAG_CONCERT_MODE_API) public final class ExtensionCaptureResult { + /** To avoid exposing constructor */ + private ExtensionCaptureResult() {} + /** * <p>The padding region for the * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY } diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceSetupImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceSetupImpl.java index 8898a4c37446..df057a1489f0 100644 --- a/core/java/android/hardware/camera2/impl/CameraDeviceSetupImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraDeviceSetupImpl.java @@ -105,7 +105,8 @@ public class CameraDeviceSetupImpl extends CameraDevice.CameraDeviceSetup { try { return cameraService.isSessionConfigurationWithParametersSupported(mCameraId, - mTargetSdkVersion, config, mContext.getDeviceId(), + mTargetSdkVersion, config, + mContext.getDeviceId(), mCameraManager.getDevicePolicyFromContext(mContext)); } catch (ServiceSpecificException e) { throw ExceptionUtils.throwAsPublicException(e); diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java index 3d7b714a2f5b..85197221e651 100644 --- a/core/java/android/hardware/display/DisplayManagerGlobal.java +++ b/core/java/android/hardware/display/DisplayManagerGlobal.java @@ -548,6 +548,20 @@ public final class DisplayManagerGlobal { } } + /** + * Request to power a display ON or OFF. + * @hide + */ + @RequiresPermission("android.permission.MANAGE_DISPLAYS") + public boolean requestDisplayPower(int displayId, boolean on) { + try { + return mDm.requestDisplayPower(displayId, on); + } catch (RemoteException ex) { + Log.e(TAG, "Error trying to request display power " + on, ex); + return false; + } + } + public void startWifiDisplayScan() { synchronized (mLock) { if (mWifiDisplayScanNestCount++ == 0) { diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl index 70efc6f2e33f..b7c02b0d0720 100644 --- a/core/java/android/hardware/display/IDisplayManager.aidl +++ b/core/java/android/hardware/display/IDisplayManager.aidl @@ -236,6 +236,10 @@ interface IDisplayManager { @EnforcePermission("MANAGE_DISPLAYS") void disableConnectedDisplay(int displayId); + // Request to power display ON or OFF. + @EnforcePermission("MANAGE_DISPLAYS") + boolean requestDisplayPower(int displayId, boolean on); + // Restricts display modes to specified modeIds. @EnforcePermission("RESTRICT_DISPLAY_MODES") void requestDisplayModes(in IBinder token, int displayId, in @nullable int[] modeIds); diff --git a/core/java/android/hardware/usb/flags/system_sw_usb_flags.aconfig b/core/java/android/hardware/usb/flags/system_sw_usb_flags.aconfig index 967fc42e8783..0f944cfb7f67 100644 --- a/core/java/android/hardware/usb/flags/system_sw_usb_flags.aconfig +++ b/core/java/android/hardware/usb/flags/system_sw_usb_flags.aconfig @@ -28,4 +28,11 @@ flag { namespace: "preload_safety" description: "Enables signal API with staking" bug: "287498482" -}
\ No newline at end of file +} + +flag { + name: "enable_usb_sysfs_midi_identification" + namespace: "system_sw_usb" + description: "Enable identifying midi device using USB sysfs" + bug: "333778731" +} diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 05345d88f771..63f0b9ebca36 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -6101,6 +6101,15 @@ public final class Settings { public static final String POINTER_SPEED = "pointer_speed"; /** + * Pointer scale setting. + * + * <p>This float value represents the scale by which the size of the pointer increases. + * @hide + */ + @Readable + public static final String POINTER_SCALE = "pointer_scale"; + + /** * Touchpad pointer speed setting. * This is an integer value in a range between -7 and +7, so there are 15 possible values. * -7 = slowest @@ -6358,6 +6367,7 @@ public final class Settings { PRIVATE_SETTINGS.add(SIP_ASK_ME_EACH_TIME); PRIVATE_SETTINGS.add(POINTER_SPEED); PRIVATE_SETTINGS.add(POINTER_FILL_STYLE); + PRIVATE_SETTINGS.add(POINTER_SCALE); PRIVATE_SETTINGS.add(LOCK_TO_APP_ENABLED); PRIVATE_SETTINGS.add(EGG_MODE); PRIVATE_SETTINGS.add(SHOW_BATTERY_PERCENT); diff --git a/core/java/android/service/contentcapture/ContentCaptureService.java b/core/java/android/service/contentcapture/ContentCaptureService.java index e1965ef25562..405fe26a20b8 100644 --- a/core/java/android/service/contentcapture/ContentCaptureService.java +++ b/core/java/android/service/contentcapture/ContentCaptureService.java @@ -29,6 +29,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; import android.app.Service; +import android.app.assist.AssistContent; import android.content.ComponentName; import android.content.ContentCaptureOptions; import android.content.Intent; @@ -133,6 +134,16 @@ public abstract class ContentCaptureService extends Service { */ public static final String SERVICE_META_DATA = "android.content_capture"; + + /** + * Extras key to flag that the passed in {@link AssistContent} is sent only during Activity + * start. + * + * @hide + */ + public static final String ASSIST_CONTENT_ACTIVITY_START_KEY = "activity_start_assist_content"; + + private final LocalDataShareAdapterResourceManager mDataShareAdapterResourceManager = new LocalDataShareAdapterResourceManager(); diff --git a/core/java/android/service/notification/ZenAdapters.java b/core/java/android/service/notification/ZenAdapters.java index b249815ce8db..a122b7155b18 100644 --- a/core/java/android/service/notification/ZenAdapters.java +++ b/core/java/android/service/notification/ZenAdapters.java @@ -33,7 +33,7 @@ public class ZenAdapters { .allowAlarms(policy.allowAlarms()) .allowCalls( policy.allowCalls() - ? notificationPolicySendersToZenPolicyPeopleType( + ? prioritySendersToPeopleType( policy.allowCallsFrom()) : ZenPolicy.PEOPLE_TYPE_NONE) .allowConversations( @@ -45,7 +45,7 @@ public class ZenAdapters { .allowMedia(policy.allowMedia()) .allowMessages( policy.allowMessages() - ? notificationPolicySendersToZenPolicyPeopleType( + ? prioritySendersToPeopleType( policy.allowMessagesFrom()) : ZenPolicy.PEOPLE_TYPE_NONE) .allowReminders(policy.allowReminders()) @@ -71,7 +71,7 @@ public class ZenAdapters { /** Maps {@link ZenPolicy.PeopleType} enum to {@link Policy.PrioritySenders}. */ @Policy.PrioritySenders - public static int zenPolicyPeopleTypeToNotificationPolicySenders( + public static int peopleTypeToPrioritySenders( @ZenPolicy.PeopleType int zpPeopleType, @Policy.PrioritySenders int defaultResult) { switch (zpPeopleType) { case ZenPolicy.PEOPLE_TYPE_ANYONE: @@ -87,7 +87,7 @@ public class ZenAdapters { /** Maps {@link Policy.PrioritySenders} enum to {@link ZenPolicy.PeopleType}. */ @ZenPolicy.PeopleType - public static int notificationPolicySendersToZenPolicyPeopleType( + public static int prioritySendersToPeopleType( @Policy.PrioritySenders int npPrioritySenders) { switch (npPrioritySenders) { case Policy.PRIORITY_SENDERS_ANY: diff --git a/core/java/android/service/notification/ZenDeviceEffects.aidl b/core/java/android/service/notification/ZenDeviceEffects.aidl new file mode 100644 index 000000000000..e635493d0e06 --- /dev/null +++ b/core/java/android/service/notification/ZenDeviceEffects.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.service.notification; + +parcelable ZenDeviceEffects; + diff --git a/core/java/android/service/notification/ZenModeConfig.java b/core/java/android/service/notification/ZenModeConfig.java index 610a317db6d2..7a0c016fa0c6 100644 --- a/core/java/android/service/notification/ZenModeConfig.java +++ b/core/java/android/service/notification/ZenModeConfig.java @@ -24,9 +24,26 @@ import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_FULL_SCRE import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS; import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK; import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_SCREEN_OFF; -import static android.service.notification.ZenAdapters.notificationPolicySendersToZenPolicyPeopleType; +import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS; +import static android.service.notification.ZenAdapters.peopleTypeToPrioritySenders; +import static android.service.notification.ZenAdapters.prioritySendersToPeopleType; import static android.service.notification.ZenAdapters.zenPolicyConversationSendersToNotificationPolicy; -import static android.service.notification.ZenAdapters.zenPolicyPeopleTypeToNotificationPolicySenders; +import static android.service.notification.ZenModeConfig.EventInfo.REPLY_YES_OR_MAYBE; +import static android.service.notification.ZenPolicy.PEOPLE_TYPE_STARRED; +import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_ALARMS; +import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_CALLS; +import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_EVENTS; +import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_MEDIA; +import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_MESSAGES; +import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_REMINDERS; +import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_REPEAT_CALLERS; +import static android.service.notification.ZenPolicy.PRIORITY_CATEGORY_SYSTEM; +import static android.service.notification.ZenPolicy.STATE_ALLOW; +import static android.service.notification.ZenPolicy.STATE_DISALLOW; +import static android.service.notification.ZenPolicy.VISUAL_EFFECT_AMBIENT; +import static android.service.notification.ZenPolicy.VISUAL_EFFECT_FULL_SCREEN_INTENT; +import static android.service.notification.ZenPolicy.VISUAL_EFFECT_LIGHTS; +import static android.service.notification.ZenPolicy.VISUAL_EFFECT_PEEK; import android.annotation.FlaggedApi; import android.annotation.IntDef; @@ -58,6 +75,7 @@ import android.util.Slog; import android.util.proto.ProtoOutputStream; import com.android.internal.R; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.XmlUtils; import com.android.modules.utils.TypedXmlPullParser; import com.android.modules.utils.TypedXmlSerializer; @@ -320,31 +338,107 @@ public class ZenModeConfig implements Parcelable { @UnsupportedAppUsage public ZenModeConfig() { + if (Flags.modesUi()) { + ensureManualZenRule(); + } } public ZenModeConfig(Parcel source) { - allowCalls = source.readInt() == 1; - allowRepeatCallers = source.readInt() == 1; - allowMessages = source.readInt() == 1; - allowReminders = source.readInt() == 1; - allowEvents = source.readInt() == 1; - allowCallsFrom = source.readInt(); - allowMessagesFrom = source.readInt(); + if (!Flags.modesUi()) { + allowCalls = source.readInt() == 1; + allowRepeatCallers = source.readInt() == 1; + allowMessages = source.readInt() == 1; + allowReminders = source.readInt() == 1; + allowEvents = source.readInt() == 1; + allowCallsFrom = source.readInt(); + allowMessagesFrom = source.readInt(); + } user = source.readInt(); manualRule = source.readParcelable(null, ZenRule.class); readRulesFromParcel(automaticRules, source); if (Flags.modesApi()) { readRulesFromParcel(deletedRules, source); } - allowAlarms = source.readInt() == 1; - allowMedia = source.readInt() == 1; - allowSystem = source.readInt() == 1; - suppressedVisualEffects = source.readInt(); + if (!Flags.modesUi()) { + allowAlarms = source.readInt() == 1; + allowMedia = source.readInt() == 1; + allowSystem = source.readInt() == 1; + suppressedVisualEffects = source.readInt(); + } areChannelsBypassingDnd = source.readInt() == 1; - allowConversations = source.readBoolean(); - allowConversationsFrom = source.readInt(); - if (Flags.modesApi()) { - allowPriorityChannels = source.readBoolean(); + if (!Flags.modesUi()) { + allowConversations = source.readBoolean(); + allowConversationsFrom = source.readInt(); + if (Flags.modesApi()) { + allowPriorityChannels = source.readBoolean(); + } + } + } + + public static ZenPolicy getDefaultZenPolicy() { + ZenPolicy policy = new ZenPolicy.Builder() + .allowAlarms(true) + .allowMedia(true) + .allowSystem(false) + .allowCalls(PEOPLE_TYPE_STARRED) + .allowMessages(PEOPLE_TYPE_STARRED) + .allowReminders(false) + .allowEvents(false) + .allowRepeatCallers(true) + .allowConversations(CONVERSATION_SENDERS_IMPORTANT) + .showAllVisualEffects() + .showVisualEffect(VISUAL_EFFECT_FULL_SCREEN_INTENT, false) + .showVisualEffect(VISUAL_EFFECT_LIGHTS, false) + .showVisualEffect(VISUAL_EFFECT_PEEK, false) + .showVisualEffect(VISUAL_EFFECT_AMBIENT, false) + .allowPriorityChannels(true) + .build(); + return policy; + } + + public static ZenModeConfig getDefaultConfig() { + ZenModeConfig config = new ZenModeConfig(); + + EventInfo eventInfo = new EventInfo(); + eventInfo.reply = REPLY_YES_OR_MAYBE; + ZenRule events = new ZenRule(); + events.id = EVENTS_DEFAULT_RULE_ID; + events.conditionId = toEventConditionId(eventInfo); + events.component = ComponentName.unflattenFromString( + "android/com.android.server.notification.EventConditionProvider"); + events.enabled = false; + events.zenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS; + events.pkg = "android"; + config.automaticRules.put(EVENTS_DEFAULT_RULE_ID, events); + + ScheduleInfo scheduleInfo = new ScheduleInfo(); + scheduleInfo.days = new int[] {1, 2, 3, 4, 5, 6, 7}; + scheduleInfo.startHour = 22; + scheduleInfo.endHour = 7; + scheduleInfo.exitAtAlarm = true; + ZenRule sleeping = new ZenRule(); + sleeping.id = EVERY_NIGHT_DEFAULT_RULE_ID; + sleeping.conditionId = toScheduleConditionId(scheduleInfo); + sleeping.component = ComponentName.unflattenFromString( + "android/com.android.server.notification.ScheduleConditionProvider"); + sleeping.enabled = false; + sleeping.zenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS; + sleeping.pkg = "android"; + config.automaticRules.put(EVERY_NIGHT_DEFAULT_RULE_ID, sleeping); + + return config; + } + + void ensureManualZenRule() { + if (manualRule == null) { + final ZenRule newRule = new ZenRule(); + newRule.type = AutomaticZenRule.TYPE_OTHER; + newRule.enabled = true; + newRule.conditionId = Uri.EMPTY; + newRule.allowManualInvocation = true; + newRule.zenPolicy = getDefaultZenPolicy(); + newRule.pkg = "android"; + manualRule = newRule; } } @@ -363,28 +457,34 @@ public class ZenModeConfig implements Parcelable { @Override public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(allowCalls ? 1 : 0); - dest.writeInt(allowRepeatCallers ? 1 : 0); - dest.writeInt(allowMessages ? 1 : 0); - dest.writeInt(allowReminders ? 1 : 0); - dest.writeInt(allowEvents ? 1 : 0); - dest.writeInt(allowCallsFrom); - dest.writeInt(allowMessagesFrom); + if (!Flags.modesUi()) { + dest.writeInt(allowCalls ? 1 : 0); + dest.writeInt(allowRepeatCallers ? 1 : 0); + dest.writeInt(allowMessages ? 1 : 0); + dest.writeInt(allowReminders ? 1 : 0); + dest.writeInt(allowEvents ? 1 : 0); + dest.writeInt(allowCallsFrom); + dest.writeInt(allowMessagesFrom); + } dest.writeInt(user); dest.writeParcelable(manualRule, 0); writeRulesToParcel(automaticRules, dest); if (Flags.modesApi()) { writeRulesToParcel(deletedRules, dest); } - dest.writeInt(allowAlarms ? 1 : 0); - dest.writeInt(allowMedia ? 1 : 0); - dest.writeInt(allowSystem ? 1 : 0); - dest.writeInt(suppressedVisualEffects); + if (!Flags.modesUi()) { + dest.writeInt(allowAlarms ? 1 : 0); + dest.writeInt(allowMedia ? 1 : 0); + dest.writeInt(allowSystem ? 1 : 0); + dest.writeInt(suppressedVisualEffects); + } dest.writeInt(areChannelsBypassingDnd ? 1 : 0); - dest.writeBoolean(allowConversations); - dest.writeInt(allowConversationsFrom); - if (Flags.modesApi()) { - dest.writeBoolean(allowPriorityChannels); + if (!Flags.modesUi()) { + dest.writeBoolean(allowConversations); + dest.writeInt(allowConversationsFrom); + if (Flags.modesApi()) { + dest.writeBoolean(allowPriorityChannels); + } } } @@ -408,35 +508,251 @@ public class ZenModeConfig implements Parcelable { @Override public String toString() { StringBuilder sb = new StringBuilder(ZenModeConfig.class.getSimpleName()).append('[') - .append("user=").append(user) - .append(",allowAlarms=").append(allowAlarms) - .append(",allowMedia=").append(allowMedia) - .append(",allowSystem=").append(allowSystem) - .append(",allowReminders=").append(allowReminders) - .append(",allowEvents=").append(allowEvents) - .append(",allowCalls=").append(allowCalls) - .append(",allowRepeatCallers=").append(allowRepeatCallers) - .append(",allowMessages=").append(allowMessages) - .append(",allowConversations=").append(allowConversations) - .append(",allowCallsFrom=").append(sourceToString(allowCallsFrom)) - .append(",allowMessagesFrom=").append(sourceToString(allowMessagesFrom)) - .append(",allowConvFrom=").append(ZenPolicy.conversationTypeToString - (allowConversationsFrom)) - .append(",suppressedVisualEffects=").append(suppressedVisualEffects); + .append("user=").append(user); + if (!Flags.modesUi()) { + sb.append(",allowAlarms=").append(allowAlarms) + .append(",allowMedia=").append(allowMedia) + .append(",allowSystem=").append(allowSystem) + .append(",allowReminders=").append(allowReminders) + .append(",allowEvents=").append(allowEvents) + .append(",allowCalls=").append(allowCalls) + .append(",allowRepeatCallers=").append(allowRepeatCallers) + .append(",allowMessages=").append(allowMessages) + .append(",allowConversations=").append(allowConversations) + .append(",allowCallsFrom=").append(sourceToString(allowCallsFrom)) + .append(",allowMessagesFrom=").append(sourceToString(allowMessagesFrom)) + .append(",allowConvFrom=").append(ZenPolicy.conversationTypeToString + (allowConversationsFrom)) + .append("\nsuppressedVisualEffects=").append(suppressedVisualEffects); + } if (Flags.modesApi()) { - sb.append(",hasPriorityChannels=").append(areChannelsBypassingDnd); + sb.append("\nhasPriorityChannels=").append(areChannelsBypassingDnd); sb.append(",allowPriorityChannels=").append(allowPriorityChannels); } else { - sb.append(",areChannelsBypassingDnd=").append(areChannelsBypassingDnd); + sb.append("\nareChannelsBypassingDnd=").append(areChannelsBypassingDnd); } - sb.append(",\nautomaticRules=").append(rulesToString(automaticRules)) - .append(",\nmanualRule=").append(manualRule); + sb.append(",\nautomaticRules=").append(rulesToString(automaticRules)); + sb.append(",\nmanualRule=").append(manualRule); if (Flags.modesApi()) { sb.append(",\ndeletedRules=").append(rulesToString(deletedRules)); } return sb.append(']').toString(); } + public boolean isAllowPriorityChannels() { + if (Flags.modesUi()) { + throw new IllegalStateException("can't be used with modesUI flag"); + } + return allowPriorityChannels; + } + + public void setAllowPriorityChannels(boolean allowPriorityChannels) { + if (Flags.modesUi()) { + throw new IllegalStateException("can't be used with modesUI flag"); + } else { + this.allowPriorityChannels = allowPriorityChannels; + } + } + + public int getSuppressedVisualEffects() { + if (Flags.modesUi()) { + throw new IllegalStateException("can't be used with modesUI flag"); + } else { + return this.suppressedVisualEffects; + } + } + + public void setSuppressedVisualEffects(int suppressedVisualEffects) { + if (Flags.modesUi()) { + throw new IllegalStateException("can't be used with modesUI flag"); + } else { + this.suppressedVisualEffects = suppressedVisualEffects; + } + } + + public @ZenPolicy.ConversationSenders int getAllowConversationsFrom() { + if (Flags.modesUi()) { + return manualRule.zenPolicy.getPriorityConversationSenders(); + } + return allowConversationsFrom; + } + + public void setAllowConversationsFrom( + @ZenPolicy.ConversationSenders int allowConversationsFrom) { + if (Flags.modesUi()) { + throw new IllegalStateException("can't be used with modesUI flag"); + } else { + this.allowConversationsFrom = allowConversationsFrom; + } + } + + public void setAllowConversations(boolean allowConversations) { + if (Flags.modesUi()) { + throw new IllegalStateException("can't be used with modesUI flag"); + } else { + this.allowConversations = allowConversations; + } + } + + public boolean isAllowConversations() { + if (Flags.modesUi()) { + throw new IllegalStateException("can't be used with modesUI flag"); + } + return allowConversations; + } + + public @Policy.PrioritySenders int getAllowMessagesFrom() { + if (Flags.modesUi()) { + throw new IllegalStateException("can't be used with modesUI flag"); + } + return allowMessagesFrom; + } + + public void setAllowMessagesFrom(@Policy.PrioritySenders int allowMessagesFrom) { + if (Flags.modesUi()) { + throw new IllegalStateException("can't be used with modesUI flag"); + } else { + this.allowMessagesFrom = allowMessagesFrom; + } + } + + public void setAllowMessages(boolean allowMessages) { + if (Flags.modesUi()) { + throw new IllegalStateException("can't be used with modesUI flag"); + } + this.allowMessages = allowMessages; + } + + public @Policy.PrioritySenders int getAllowCallsFrom() { + if (Flags.modesUi()) { + return peopleTypeToPrioritySenders( + manualRule.zenPolicy.getPriorityCallSenders(), DEFAULT_CALLS_SOURCE); + } + return allowCallsFrom; + } + + public void setAllowCallsFrom(@Policy.PrioritySenders int allowCallsFrom) { + if (Flags.modesUi()) { + manualRule.zenPolicy = new ZenPolicy.Builder(manualRule.zenPolicy) + .allowCalls(prioritySendersToPeopleType(allowCallsFrom)) + .build(); + } else { + this.allowCallsFrom = allowCallsFrom; + } + } + + public void setAllowCalls(boolean allowCalls) { + if (Flags.modesUi()) { + throw new IllegalStateException("can't be used with modesUI flag"); + } + this.allowCalls = allowCalls; + } + + public boolean isAllowEvents() { + if (Flags.modesUi()) { + return manualRule.zenPolicy.isCategoryAllowed( + ZenPolicy.PRIORITY_CATEGORY_EVENTS, false); + } + return allowEvents; + } + + public void setAllowEvents(boolean allowEvents) { + if (Flags.modesUi()) { + throw new IllegalStateException("can't be used with modesUI flag"); + } else { + this.allowEvents = allowEvents; + } + } + + public boolean isAllowReminders() { + if (Flags.modesUi()) { + throw new IllegalStateException("can't be used with modesUI flag"); + } + return allowReminders; + } + + public void setAllowReminders(boolean allowReminders) { + if (Flags.modesUi()) { + throw new IllegalStateException("can't be used with modesUI flag"); + } else { + this.allowReminders = allowReminders; + } + } + + public boolean isAllowMessages() { + if (Flags.modesUi()) { + throw new IllegalStateException("can't be used with modesUI flag"); + } + return allowMessages; + } + + public boolean isAllowRepeatCallers() { + if (Flags.modesUi()) { + throw new IllegalStateException("can't be used with modesUI flag"); + } + return allowRepeatCallers; + } + + public void setAllowRepeatCallers(boolean allowRepeatCallers) { + if (Flags.modesUi()) { + throw new IllegalStateException("can't be used with modesUI flag"); + } else { + this.allowRepeatCallers = allowRepeatCallers; + } + } + + public boolean isAllowSystem() { + if (Flags.modesUi()) { + throw new IllegalStateException("can't be used with modesUI flag"); + } + return allowSystem; + } + + public void setAllowSystem(boolean allowSystem) { + if (Flags.modesUi()) { + throw new IllegalStateException("can't be used with modesUI flag"); + } else { + this.allowSystem = allowSystem; + } + } + + public boolean isAllowMedia() { + if (Flags.modesUi()) { + throw new IllegalStateException("can't be used with modesUI flag"); + } + return allowMedia; + } + + public void setAllowMedia(boolean allowMedia) { + if (Flags.modesUi()) { + throw new IllegalStateException("can't be used with modesUI flag"); + } else { + this.allowMedia = allowMedia; + } + } + + public boolean isAllowAlarms() { + if (Flags.modesUi()) { + throw new IllegalStateException("can't be used with modesUI flag"); + } + return allowAlarms; + } + + public void setAllowAlarms(boolean allowAlarms) { + if (Flags.modesUi()) { + throw new IllegalStateException("can't be used with modesUI flag"); + } else { + this.allowAlarms = allowAlarms; + } + } + + public boolean isAllowCalls() { + if (Flags.modesUi()) { + throw new IllegalStateException("can't be used with modesUI flag"); + } + return allowCalls; + } + private static String rulesToString(ArrayMap<String, ZenRule> ruleList) { if (ruleList.isEmpty()) { return "{}"; @@ -512,6 +828,8 @@ public class ZenModeConfig implements Parcelable { if (!(o instanceof ZenModeConfig)) return false; if (o == this) return true; final ZenModeConfig other = (ZenModeConfig) o; + // The policy fields that live on config are compared directly because the fields will + // contain data until MODES_UI is rolled out/cleaned up. boolean eq = other.allowAlarms == allowAlarms && other.allowMedia == allowMedia && other.allowSystem == allowSystem @@ -539,6 +857,8 @@ public class ZenModeConfig implements Parcelable { @Override public int hashCode() { + // The policy fields that live on config are compared directly because the fields will + // contain data until MODES_UI is rolled out/cleaned up. if (Flags.modesApi()) { return Objects.hash(allowAlarms, allowMedia, allowSystem, allowCalls, allowRepeatCallers, allowMessages, @@ -619,9 +939,14 @@ public class ZenModeConfig implements Parcelable { rt.version = safeInt(parser, ZEN_ATT_VERSION, getCurrentXmlVersion()); rt.user = safeInt(parser, ZEN_ATT_USER, rt.user); boolean readSuppressedEffects = false; + boolean readManualRule = false; while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) { tag = parser.getName(); if (type == XmlPullParser.END_TAG && ZEN_TAG.equals(tag)) { + if (Flags.modesUi() && !readManualRule) { + // migrate from fields on config into manual rule + rt.manualRule.zenPolicy = rt.toZenPolicy(); + } return rt; } if (type == XmlPullParser.START_TAG) { @@ -693,6 +1018,9 @@ public class ZenModeConfig implements Parcelable { DEFAULT_SUPPRESSED_VISUAL_EFFECTS); } else if (MANUAL_TAG.equals(tag)) { rt.manualRule = readRuleXml(parser); + if (rt.manualRule != null) { + readManualRule = true; + } } else if (AUTOMATIC_TAG.equals(tag) || (Flags.modesApi() && AUTOMATIC_DELETED_TAG.equals(tag))) { final String id = parser.getAttributeValue(null, RULE_ATT_ID); @@ -742,6 +1070,9 @@ public class ZenModeConfig implements Parcelable { ? Integer.toString(xmlVersion) : Integer.toString(version)); out.attributeInt(null, ZEN_ATT_USER, user); out.startTag(null, ALLOW_TAG); + // From MODES_UI these fields are only read if the flag has transitioned from off to on + // However, we will continue to write these fields until the flag is cleaned up so it's + // possible to turn the flag off without losing user data out.attributeBoolean(null, ALLOW_ATT_CALLS, allowCalls); out.attributeBoolean(null, ALLOW_ATT_REPEAT_CALLERS, allowRepeatCallers); out.attributeBoolean(null, ALLOW_ATT_MESSAGES, allowMessages); @@ -816,11 +1147,11 @@ public class ZenModeConfig implements Parcelable { rt.enabler = parser.getAttributeValue(null, RULE_ATT_ENABLER); rt.condition = readConditionXml(parser); - if (!Flags.modesApi() && rt.zenMode != Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS + if (!Flags.modesApi() && rt.zenMode != ZEN_MODE_IMPORTANT_INTERRUPTIONS && Condition.isValidId(rt.conditionId, SYSTEM_AUTHORITY)) { // all default rules and user created rules updated to zenMode important interruptions Slog.i(TAG, "Updating zenMode of automatic rule " + rt.name); - rt.zenMode = Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS; + rt.zenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS; } rt.modified = safeBoolean(parser, RULE_ATT_MODIFIED, false); rt.zenPolicy = readZenPolicyXml(parser); @@ -952,7 +1283,7 @@ public class ZenModeConfig implements Parcelable { if (Flags.modesApi()) { final int channels = safeInt(parser, ALLOW_ATT_CHANNELS, ZenPolicy.STATE_UNSET); if (channels != ZenPolicy.STATE_UNSET) { - builder.allowPriorityChannels(channels == ZenPolicy.STATE_ALLOW); + builder.allowPriorityChannels(channels == STATE_ALLOW); policySet = true; } } @@ -966,7 +1297,7 @@ public class ZenModeConfig implements Parcelable { policySet = true; } if (repeatCallers != ZenPolicy.STATE_UNSET) { - builder.allowRepeatCallers(repeatCallers == ZenPolicy.STATE_ALLOW); + builder.allowRepeatCallers(repeatCallers == STATE_ALLOW); policySet = true; } if (conversations != ZenPolicy.CONVERSATION_SENDERS_UNSET) { @@ -974,23 +1305,23 @@ public class ZenModeConfig implements Parcelable { policySet = true; } if (alarms != ZenPolicy.STATE_UNSET) { - builder.allowAlarms(alarms == ZenPolicy.STATE_ALLOW); + builder.allowAlarms(alarms == STATE_ALLOW); policySet = true; } if (media != ZenPolicy.STATE_UNSET) { - builder.allowMedia(media == ZenPolicy.STATE_ALLOW); + builder.allowMedia(media == STATE_ALLOW); policySet = true; } if (system != ZenPolicy.STATE_UNSET) { - builder.allowSystem(system == ZenPolicy.STATE_ALLOW); + builder.allowSystem(system == STATE_ALLOW); policySet = true; } if (events != ZenPolicy.STATE_UNSET) { - builder.allowEvents(events == ZenPolicy.STATE_ALLOW); + builder.allowEvents(events == STATE_ALLOW); policySet = true; } if (reminders != ZenPolicy.STATE_UNSET) { - builder.allowReminders(reminders == ZenPolicy.STATE_ALLOW); + builder.allowReminders(reminders == STATE_ALLOW); policySet = true; } @@ -1005,31 +1336,31 @@ public class ZenModeConfig implements Parcelable { ZenPolicy.STATE_UNSET); if (fullScreenIntent != ZenPolicy.STATE_UNSET) { - builder.showFullScreenIntent(fullScreenIntent == ZenPolicy.STATE_ALLOW); + builder.showFullScreenIntent(fullScreenIntent == STATE_ALLOW); policySet = true; } if (lights != ZenPolicy.STATE_UNSET) { - builder.showLights(lights == ZenPolicy.STATE_ALLOW); + builder.showLights(lights == STATE_ALLOW); policySet = true; } if (peek != ZenPolicy.STATE_UNSET) { - builder.showPeeking(peek == ZenPolicy.STATE_ALLOW); + builder.showPeeking(peek == STATE_ALLOW); policySet = true; } if (statusBar != ZenPolicy.STATE_UNSET) { - builder.showStatusBarIcons(statusBar == ZenPolicy.STATE_ALLOW); + builder.showStatusBarIcons(statusBar == STATE_ALLOW); policySet = true; } if (badges != ZenPolicy.STATE_UNSET) { - builder.showBadges(badges == ZenPolicy.STATE_ALLOW); + builder.showBadges(badges == STATE_ALLOW); policySet = true; } if (ambient != ZenPolicy.STATE_UNSET) { - builder.showInAmbientDisplay(ambient == ZenPolicy.STATE_ALLOW); + builder.showInAmbientDisplay(ambient == STATE_ALLOW); policySet = true; } if (notificationList != ZenPolicy.STATE_UNSET) { - builder.showInNotificationList(notificationList == ZenPolicy.STATE_ALLOW); + builder.showInNotificationList(notificationList == STATE_ALLOW); policySet = true; } @@ -1266,17 +1597,22 @@ public class ZenModeConfig implements Parcelable { } }; + public ZenPolicy getZenPolicy() { + return Flags.modesUi() ? manualRule.zenPolicy : toZenPolicy(); + } + /** * Converts a ZenModeConfig to a ZenPolicy */ - public ZenPolicy toZenPolicy() { + @VisibleForTesting + ZenPolicy toZenPolicy() { ZenPolicy.Builder builder = new ZenPolicy.Builder() .allowCalls(allowCalls - ? notificationPolicySendersToZenPolicyPeopleType(allowCallsFrom) + ? prioritySendersToPeopleType(allowCallsFrom) : ZenPolicy.PEOPLE_TYPE_NONE) .allowRepeatCallers(allowRepeatCallers) .allowMessages(allowMessages - ? notificationPolicySendersToZenPolicyPeopleType(allowMessagesFrom) + ? prioritySendersToPeopleType(allowMessagesFrom) : ZenPolicy.PEOPLE_TYPE_NONE) .allowReminders(allowReminders) .allowEvents(allowEvents) @@ -1336,7 +1672,7 @@ public class ZenModeConfig implements Parcelable { if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_MESSAGES, isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_MESSAGES, defaultPolicy))) { priorityCategories |= Policy.PRIORITY_CATEGORY_MESSAGES; - messageSenders = zenPolicyPeopleTypeToNotificationPolicySenders( + messageSenders = peopleTypeToPrioritySenders( zenPolicy.getPriorityMessageSenders(), messageSenders); } @@ -1352,7 +1688,7 @@ public class ZenModeConfig implements Parcelable { if (zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_CALLS, isPriorityCategoryEnabled(Policy.PRIORITY_CATEGORY_CALLS, defaultPolicy))) { priorityCategories |= Policy.PRIORITY_CATEGORY_CALLS; - callSenders = zenPolicyPeopleTypeToNotificationPolicySenders( + callSenders = peopleTypeToPrioritySenders( zenPolicy.getPriorityCallSenders(), callSenders); } @@ -1452,46 +1788,155 @@ public class ZenModeConfig implements Parcelable { return (policy.suppressedVisualEffects & visualEffect) == 0; } + private boolean isVisualEffectAllowed(int suppressedVisualEffects, int visualEffect) { + return (suppressedVisualEffects & visualEffect) == 0; + } + public Policy toNotificationPolicy() { int priorityCategories = 0; int priorityCallSenders = Policy.PRIORITY_SENDERS_CONTACTS; int priorityMessageSenders = Policy.PRIORITY_SENDERS_CONTACTS; int priorityConversationSenders = Policy.CONVERSATION_SENDERS_IMPORTANT; - if (allowConversations) { - priorityCategories |= Policy.PRIORITY_CATEGORY_CONVERSATIONS; - } - if (allowCalls) { - priorityCategories |= Policy.PRIORITY_CATEGORY_CALLS; - } - if (allowMessages) { - priorityCategories |= Policy.PRIORITY_CATEGORY_MESSAGES; - } - if (allowEvents) { - priorityCategories |= Policy.PRIORITY_CATEGORY_EVENTS; - } - if (allowReminders) { - priorityCategories |= Policy.PRIORITY_CATEGORY_REMINDERS; - } - if (allowRepeatCallers) { - priorityCategories |= Policy.PRIORITY_CATEGORY_REPEAT_CALLERS; - } - if (allowAlarms) { - priorityCategories |= Policy.PRIORITY_CATEGORY_ALARMS; - } - if (allowMedia) { - priorityCategories |= Policy.PRIORITY_CATEGORY_MEDIA; - } - if (allowSystem) { - priorityCategories |= Policy.PRIORITY_CATEGORY_SYSTEM; - } - priorityCallSenders = sourceToPrioritySenders(allowCallsFrom, priorityCallSenders); - priorityMessageSenders = sourceToPrioritySenders(allowMessagesFrom, priorityMessageSenders); - priorityConversationSenders = zenPolicyConversationSendersToNotificationPolicy( - allowConversationsFrom, priorityConversationSenders); + int state = 0; + int suppressedVisualEffects = 0; - int state = areChannelsBypassingDnd ? Policy.STATE_CHANNELS_BYPASSING_DND : 0; - if (Flags.modesApi()) { - state = Policy.policyState(areChannelsBypassingDnd, allowPriorityChannels); + if (Flags.modesUi()) { + if (manualRule.zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_EVENTS, false)) { + priorityCategories |= Policy.PRIORITY_CATEGORY_EVENTS; + } + if (manualRule.zenPolicy.isCategoryAllowed( + ZenPolicy.PRIORITY_CATEGORY_REMINDERS, false)) { + priorityCategories |= Policy.PRIORITY_CATEGORY_REMINDERS; + } + if (manualRule.zenPolicy.isCategoryAllowed( + ZenPolicy.PRIORITY_CATEGORY_REPEAT_CALLERS, false)) { + priorityCategories |= Policy.PRIORITY_CATEGORY_REPEAT_CALLERS; + } + if (manualRule.zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_ALARMS, false)) { + priorityCategories |= Policy.PRIORITY_CATEGORY_ALARMS; + } + if (manualRule.zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_MEDIA, false)) { + priorityCategories |= Policy.PRIORITY_CATEGORY_MEDIA; + } + if (manualRule.zenPolicy.isCategoryAllowed(ZenPolicy.PRIORITY_CATEGORY_SYSTEM, false)) { + priorityCategories |= Policy.PRIORITY_CATEGORY_SYSTEM; + } + + if (manualRule.zenPolicy.getPriorityCategoryConversations() == STATE_ALLOW) { + priorityCategories |= Policy.PRIORITY_CATEGORY_CONVERSATIONS; + } + priorityConversationSenders = zenPolicyConversationSendersToNotificationPolicy( + manualRule.zenPolicy.getPriorityConversationSenders(), + CONVERSATION_SENDERS_NONE); + if (manualRule.zenPolicy.getPriorityCategoryCalls() == STATE_ALLOW) { + priorityCategories |= Policy.PRIORITY_CATEGORY_CALLS; + } + priorityCallSenders = peopleTypeToPrioritySenders( + manualRule.zenPolicy.getPriorityCallSenders(), DEFAULT_CALLS_SOURCE); + if (manualRule.zenPolicy.getPriorityCategoryMessages() == STATE_ALLOW) { + priorityCategories |= Policy.PRIORITY_CATEGORY_MESSAGES; + } + priorityMessageSenders = peopleTypeToPrioritySenders( + manualRule.zenPolicy.getPriorityMessageSenders(), DEFAULT_SOURCE); + + state = Policy.policyState(areChannelsBypassingDnd, + manualRule.zenPolicy.getPriorityChannelsAllowed() != STATE_DISALLOW); + + boolean suppressFullScreenIntent = !manualRule.zenPolicy.isVisualEffectAllowed( + ZenPolicy.VISUAL_EFFECT_FULL_SCREEN_INTENT, + isVisualEffectAllowed(DEFAULT_SUPPRESSED_VISUAL_EFFECTS, + ZenPolicy.VISUAL_EFFECT_FULL_SCREEN_INTENT)); + + boolean suppressLights = !manualRule.zenPolicy.isVisualEffectAllowed( + ZenPolicy.VISUAL_EFFECT_LIGHTS, + isVisualEffectAllowed(DEFAULT_SUPPRESSED_VISUAL_EFFECTS, + ZenPolicy.VISUAL_EFFECT_LIGHTS)); + + boolean suppressAmbient = !manualRule.zenPolicy.isVisualEffectAllowed( + ZenPolicy.VISUAL_EFFECT_AMBIENT, + isVisualEffectAllowed(DEFAULT_SUPPRESSED_VISUAL_EFFECTS, + ZenPolicy.VISUAL_EFFECT_AMBIENT)); + + if (suppressFullScreenIntent && suppressLights && suppressAmbient) { + suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_SCREEN_OFF; + } + + if (suppressFullScreenIntent) { + suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT; + } + + if (suppressLights) { + suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_LIGHTS; + } + + if (!manualRule.zenPolicy.isVisualEffectAllowed(ZenPolicy.VISUAL_EFFECT_PEEK, + isVisualEffectAllowed(DEFAULT_SUPPRESSED_VISUAL_EFFECTS, + ZenPolicy.VISUAL_EFFECT_PEEK))) { + suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_PEEK; + suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_SCREEN_ON; + } + + if (!manualRule.zenPolicy.isVisualEffectAllowed(ZenPolicy.VISUAL_EFFECT_STATUS_BAR, + isVisualEffectAllowed(DEFAULT_SUPPRESSED_VISUAL_EFFECTS, + ZenPolicy.VISUAL_EFFECT_STATUS_BAR))) { + suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_STATUS_BAR; + } + + if (!manualRule.zenPolicy.isVisualEffectAllowed(ZenPolicy.VISUAL_EFFECT_BADGE, + isVisualEffectAllowed(DEFAULT_SUPPRESSED_VISUAL_EFFECTS, + ZenPolicy.VISUAL_EFFECT_BADGE))) { + suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_BADGE; + } + + if (suppressAmbient) { + suppressedVisualEffects |= SUPPRESSED_EFFECT_AMBIENT; + } + + if (!manualRule.zenPolicy.isVisualEffectAllowed( + ZenPolicy.VISUAL_EFFECT_NOTIFICATION_LIST, + isVisualEffectAllowed(DEFAULT_SUPPRESSED_VISUAL_EFFECTS, + ZenPolicy.VISUAL_EFFECT_NOTIFICATION_LIST))) { + suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_NOTIFICATION_LIST; + } + } else { + if (isAllowConversations()) { + priorityCategories |= Policy.PRIORITY_CATEGORY_CONVERSATIONS; + } + if (isAllowCalls()) { + priorityCategories |= Policy.PRIORITY_CATEGORY_CALLS; + } + if (isAllowMessages()) { + priorityCategories |= Policy.PRIORITY_CATEGORY_MESSAGES; + } + if (isAllowEvents()) { + priorityCategories |= Policy.PRIORITY_CATEGORY_EVENTS; + } + if (isAllowReminders()) { + priorityCategories |= Policy.PRIORITY_CATEGORY_REMINDERS; + } + if (isAllowRepeatCallers()) { + priorityCategories |= Policy.PRIORITY_CATEGORY_REPEAT_CALLERS; + } + if (isAllowAlarms()) { + priorityCategories |= Policy.PRIORITY_CATEGORY_ALARMS; + } + if (isAllowMedia()) { + priorityCategories |= Policy.PRIORITY_CATEGORY_MEDIA; + } + if (isAllowSystem()) { + priorityCategories |= Policy.PRIORITY_CATEGORY_SYSTEM; + } + priorityCallSenders = sourceToPrioritySenders(getAllowCallsFrom(), priorityCallSenders); + priorityMessageSenders = sourceToPrioritySenders( + getAllowMessagesFrom(), priorityMessageSenders); + priorityConversationSenders = zenPolicyConversationSendersToNotificationPolicy( + getAllowConversationsFrom(), priorityConversationSenders); + + state = areChannelsBypassingDnd ? Policy.STATE_CHANNELS_BYPASSING_DND : 0; + if (Flags.modesApi()) { + state = Policy.policyState(areChannelsBypassingDnd, allowPriorityChannels); + } + suppressedVisualEffects = getSuppressedVisualEffects(); } return new Policy(priorityCategories, priorityCallSenders, priorityMessageSenders, @@ -1544,31 +1989,38 @@ public class ZenModeConfig implements Parcelable { public void applyNotificationPolicy(Policy policy) { if (policy == null) return; - allowAlarms = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_ALARMS) != 0; - allowMedia = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_MEDIA) != 0; - allowSystem = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_SYSTEM) != 0; - allowEvents = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_EVENTS) != 0; - allowReminders = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_REMINDERS) != 0; - allowCalls = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_CALLS) != 0; - allowMessages = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_MESSAGES) != 0; - allowRepeatCallers = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_REPEAT_CALLERS) - != 0; - allowCallsFrom = normalizePrioritySenders(policy.priorityCallSenders, allowCallsFrom); - allowMessagesFrom = normalizePrioritySenders(policy.priorityMessageSenders, - allowMessagesFrom); - if (policy.suppressedVisualEffects != Policy.SUPPRESSED_EFFECTS_UNSET) { - suppressedVisualEffects = policy.suppressedVisualEffects; - } - allowConversations = (policy.priorityCategories - & Policy.PRIORITY_CATEGORY_CONVERSATIONS) != 0; - allowConversationsFrom = normalizeConversationSenders(allowConversations, - policy.priorityConversationSenders, - allowConversationsFrom); + if (Flags.modesUi()) { + manualRule.zenPolicy = ZenAdapters.notificationPolicyToZenPolicy(policy); + } else { + setAllowAlarms((policy.priorityCategories & Policy.PRIORITY_CATEGORY_ALARMS) != 0); + allowMedia = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_MEDIA) != 0; + allowSystem = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_SYSTEM) != 0; + allowEvents = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_EVENTS) != 0; + allowReminders = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_REMINDERS) != 0; + allowCalls = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_CALLS) != 0; + allowMessages = (policy.priorityCategories & Policy.PRIORITY_CATEGORY_MESSAGES) != 0; + allowRepeatCallers = + (policy.priorityCategories & Policy.PRIORITY_CATEGORY_REPEAT_CALLERS) + != 0; + allowCallsFrom = normalizePrioritySenders(policy.priorityCallSenders, allowCallsFrom); + allowMessagesFrom = normalizePrioritySenders(policy.priorityMessageSenders, + allowMessagesFrom); + if (policy.suppressedVisualEffects != Policy.SUPPRESSED_EFFECTS_UNSET) { + suppressedVisualEffects = policy.suppressedVisualEffects; + } + allowConversations = (policy.priorityCategories + & Policy.PRIORITY_CATEGORY_CONVERSATIONS) != 0; + allowConversationsFrom = normalizeConversationSenders(allowConversations, + policy.priorityConversationSenders, + allowConversationsFrom); + if (policy.state != Policy.STATE_UNSET) { + if (Flags.modesApi()) { + setAllowPriorityChannels(policy.allowPriorityChannels()); + } + } + } if (policy.state != Policy.STATE_UNSET) { areChannelsBypassingDnd = (policy.state & Policy.STATE_CHANNELS_BYPASSING_DND) != 0; - if (Flags.modesApi()) { - allowPriorityChannels = policy.allowPriorityChannels(); - } } } @@ -1995,49 +2447,11 @@ public class ZenModeConfig implements Parcelable { return ""; } - public static String getConditionSummary(Context context, ZenModeConfig config, - int userHandle, boolean shortVersion) { - return getConditionLine(context, config, userHandle, false /*useLine1*/, shortVersion); - } - - private static String getConditionLine(Context context, ZenModeConfig config, - int userHandle, boolean useLine1, boolean shortVersion) { - if (config == null) return ""; - String summary = ""; - if (config.manualRule != null) { - final Uri id = config.manualRule.conditionId; - if (config.manualRule.enabler != null) { - summary = getOwnerCaption(context, config.manualRule.enabler); - } else { - if (id == null) { - summary = context.getString(com.android.internal.R.string.zen_mode_forever); - } else { - final long time = tryParseCountdownConditionId(id); - Condition c = config.manualRule.condition; - if (time > 0) { - final long now = System.currentTimeMillis(); - final long span = time - now; - c = toTimeCondition(context, time, Math.round(span / (float) MINUTES_MS), - userHandle, shortVersion); - } - final String rt = c == null ? "" : useLine1 ? c.line1 : c.summary; - summary = TextUtils.isEmpty(rt) ? "" : rt; - } - } + public boolean isManualActive() { + if (!Flags.modesUi()) { + return manualRule != null; } - for (ZenRule automaticRule : config.automaticRules.values()) { - if (automaticRule.isAutomaticActive()) { - if (summary.isEmpty()) { - summary = automaticRule.name; - } else { - summary = context.getResources() - .getString(R.string.zen_mode_rule_name_combination, summary, - automaticRule.name); - } - - } - } - return summary; + return manualRule != null && manualRule.isAutomaticActive(); } public static class ZenRule implements Parcelable { @@ -2401,7 +2815,7 @@ public class ZenModeConfig implements Parcelable { public static boolean isZenOverridingRinger(int zen, Policy consolidatedPolicy) { return zen == Global.ZEN_MODE_NO_INTERRUPTIONS || zen == Global.ZEN_MODE_ALARMS - || (zen == Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS + || (zen == ZEN_MODE_IMPORTANT_INTERRUPTIONS && ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(consolidatedPolicy)); } @@ -2410,21 +2824,40 @@ public class ZenModeConfig implements Parcelable { * This includes notification, ringer and system sounds */ public static boolean areAllPriorityOnlyRingerSoundsMuted(ZenModeConfig config) { - boolean areChannelsBypassingDnd = config.areChannelsBypassingDnd; - if (Flags.modesApi()) { - areChannelsBypassingDnd = config.areChannelsBypassingDnd - && config.allowPriorityChannels; + if (Flags.modesUi()) { + final ZenPolicy policy = config.manualRule.zenPolicy; + return !policy.isCategoryAllowed(PRIORITY_CATEGORY_REMINDERS, false) + && !policy.isCategoryAllowed(PRIORITY_CATEGORY_CALLS, false) + && !policy.isCategoryAllowed(PRIORITY_CATEGORY_MESSAGES, false) + && !policy.isCategoryAllowed(PRIORITY_CATEGORY_EVENTS, false) + && !policy.isCategoryAllowed(PRIORITY_CATEGORY_REPEAT_CALLERS, false) + && !policy.isCategoryAllowed(PRIORITY_CATEGORY_SYSTEM, false) + && !(config.areChannelsBypassingDnd && policy.getPriorityChannelsAllowed() + == STATE_ALLOW); + + } else { + boolean areChannelsBypassingDnd = config.areChannelsBypassingDnd; + if (Flags.modesApi()) { + areChannelsBypassingDnd = config.areChannelsBypassingDnd + && config.isAllowPriorityChannels(); + } + return !config.isAllowReminders() && !config.isAllowCalls() && !config.isAllowMessages() + && !config.isAllowEvents() && !config.isAllowRepeatCallers() + && !areChannelsBypassingDnd && !config.isAllowSystem(); } - return !config.allowReminders && !config.allowCalls && !config.allowMessages - && !config.allowEvents && !config.allowRepeatCallers - && !areChannelsBypassingDnd && !config.allowSystem; } /** * Determines whether dnd mutes all sounds */ public static boolean areAllZenBehaviorSoundsMuted(ZenModeConfig config) { - return !config.allowAlarms && !config.allowMedia + if (Flags.modesUi()) { + final ZenPolicy policy = config.manualRule.zenPolicy; + return !policy.isCategoryAllowed(PRIORITY_CATEGORY_ALARMS, false) + && !policy.isCategoryAllowed(PRIORITY_CATEGORY_MEDIA, false) + && areAllPriorityOnlyRingerSoundsMuted(config); + } + return !config.isAllowAlarms() && !config.isAllowMedia() && areAllPriorityOnlyRingerSoundsMuted(config); } diff --git a/core/java/android/view/HandwritingInitiator.java b/core/java/android/view/HandwritingInitiator.java index 57d1b8d18fe1..ccec89bb6262 100644 --- a/core/java/android/view/HandwritingInitiator.java +++ b/core/java/android/view/HandwritingInitiator.java @@ -496,9 +496,10 @@ public class HandwritingInitiator { if (delegatorPackageName == null) { delegatorPackageName = view.getContext().getOpPackageName(); } + WeakReference<View> viewRef = new WeakReference<>(view); Consumer<Boolean> consumer = delegationAccepted -> { if (delegationAccepted) { - onDelegationAccepted(view); + onDelegationAccepted(viewRef.get()); } }; mImm.acceptStylusHandwritingDelegation(view, delegatorPackageName, view::post, consumer); @@ -509,6 +510,10 @@ public class HandwritingInitiator { mState.mHandled = true; mState.mShouldInitHandwriting = false; } + if (view == null) { + // can be null if view was detached and was GCed. + return; + } if (view instanceof TextView) { ((TextView) view).hideHint(); } diff --git a/core/java/android/view/PointerIcon.java b/core/java/android/view/PointerIcon.java index 7c2577fdf8e1..c30212657c57 100644 --- a/core/java/android/view/PointerIcon.java +++ b/core/java/android/view/PointerIcon.java @@ -193,6 +193,9 @@ public final class PointerIcon implements Parcelable { /** @hide */ public static final int POINTER_ICON_VECTOR_STYLE_FILL_END = POINTER_ICON_VECTOR_STYLE_FILL_BLUE; + /** @hide */ public static final float DEFAULT_POINTER_SCALE = 1f; + /** @hide */ public static final float LARGE_POINTER_SCALE = 2.5f; + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) private final int mType; @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @@ -253,7 +256,7 @@ public final class PointerIcon implements Parcelable { * @hide */ public static @NonNull PointerIcon getLoadedSystemIcon(@NonNull Context context, int type, - boolean useLargeIcons) { + boolean useLargeIcons, float pointerScale) { if (type == TYPE_NOT_SPECIFIED) { throw new IllegalStateException("Cannot load icon for type TYPE_NOT_SPECIFIED"); } @@ -268,13 +271,18 @@ public final class PointerIcon implements Parcelable { } final int defStyle; - // TODO(b/305193969): Use scaled vectors when large icons are requested. - if (useLargeIcons) { - defStyle = com.android.internal.R.style.LargePointer; - } else if (android.view.flags.Flags.enableVectorCursors()) { + if (android.view.flags.Flags.enableVectorCursorA11ySettings()) { defStyle = com.android.internal.R.style.VectorPointer; } else { - defStyle = com.android.internal.R.style.Pointer; + // TODO(b/346358375): Remove useLargeIcons and the legacy pointer styles when + // enableVectorCursorA11ySetting is rolled out. + if (useLargeIcons) { + defStyle = com.android.internal.R.style.LargePointer; + } else if (android.view.flags.Flags.enableVectorCursors()) { + defStyle = com.android.internal.R.style.VectorPointer; + } else { + defStyle = com.android.internal.R.style.Pointer; + } } TypedArray a = context.obtainStyledAttributes(null, com.android.internal.R.styleable.Pointer, @@ -286,11 +294,11 @@ public final class PointerIcon implements Parcelable { Log.w(TAG, "Missing theme resources for pointer icon type " + type); return type == TYPE_DEFAULT ? getSystemIcon(TYPE_NULL) - : getLoadedSystemIcon(context, TYPE_DEFAULT, useLargeIcons); + : getLoadedSystemIcon(context, TYPE_DEFAULT, useLargeIcons, pointerScale); } final PointerIcon icon = new PointerIcon(type); - icon.loadResource(context.getResources(), resourceId, context.getTheme()); + icon.loadResource(context.getResources(), resourceId, context.getTheme(), pointerScale); return icon; } @@ -353,7 +361,7 @@ public final class PointerIcon implements Parcelable { } PointerIcon icon = new PointerIcon(TYPE_CUSTOM); - icon.loadResource(resources, resourceId, null); + icon.loadResource(resources, resourceId, null, DEFAULT_POINTER_SCALE); return icon; } @@ -460,12 +468,13 @@ public final class PointerIcon implements Parcelable { } private BitmapDrawable getBitmapDrawableFromVectorDrawable(Resources resources, - VectorDrawable vectorDrawable) { + VectorDrawable vectorDrawable, float pointerScale) { // Ensure we pass the display metrics into the Bitmap constructor so that it is initialized // with the correct density. Bitmap bitmap = Bitmap.createBitmap(resources.getDisplayMetrics(), - vectorDrawable.getIntrinsicWidth(), - vectorDrawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888, true /* hasAlpha */); + (int) (vectorDrawable.getIntrinsicWidth() * pointerScale), + (int) (vectorDrawable.getIntrinsicHeight() * pointerScale), + Bitmap.Config.ARGB_8888, true /* hasAlpha */); Canvas canvas = new Canvas(bitmap); vectorDrawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); vectorDrawable.draw(canvas); @@ -473,7 +482,7 @@ public final class PointerIcon implements Parcelable { } private void loadResource(@NonNull Resources resources, @XmlRes int resourceId, - @Nullable Resources.Theme theme) { + @Nullable Resources.Theme theme, float pointerScale) { final XmlResourceParser parser = resources.getXml(resourceId); final int bitmapRes; final float hotSpotX; @@ -484,8 +493,10 @@ public final class PointerIcon implements Parcelable { final TypedArray a = resources.obtainAttributes( parser, com.android.internal.R.styleable.PointerIcon); bitmapRes = a.getResourceId(com.android.internal.R.styleable.PointerIcon_bitmap, 0); - hotSpotX = a.getDimension(com.android.internal.R.styleable.PointerIcon_hotSpotX, 0); - hotSpotY = a.getDimension(com.android.internal.R.styleable.PointerIcon_hotSpotY, 0); + hotSpotX = a.getDimension(com.android.internal.R.styleable.PointerIcon_hotSpotX, 0) + * pointerScale; + hotSpotY = a.getDimension(com.android.internal.R.styleable.PointerIcon_hotSpotY, 0) + * pointerScale; a.recycle(); } catch (Exception ex) { throw new IllegalArgumentException("Exception parsing pointer icon resource.", ex); @@ -534,7 +545,7 @@ public final class PointerIcon implements Parcelable { } if (isVectorAnimation) { drawableFrame = getBitmapDrawableFromVectorDrawable(resources, - (VectorDrawable) drawableFrame); + (VectorDrawable) drawableFrame, pointerScale); } mBitmapFrames[i - 1] = getBitmapFromDrawable((BitmapDrawable) drawableFrame); } @@ -542,7 +553,8 @@ public final class PointerIcon implements Parcelable { } if (drawable instanceof VectorDrawable) { mDrawNativeDropShadow = true; - drawable = getBitmapDrawableFromVectorDrawable(resources, (VectorDrawable) drawable); + drawable = getBitmapDrawableFromVectorDrawable(resources, (VectorDrawable) drawable, + pointerScale); } if (!(drawable instanceof BitmapDrawable)) { throw new IllegalArgumentException("<pointer-icon> bitmap attribute must " diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index a26150c469fe..a11bb788bbf0 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -177,6 +177,7 @@ import android.graphics.Region; import android.graphics.RenderNode; import android.graphics.drawable.Drawable; import android.graphics.drawable.GradientDrawable; +import android.hardware.SyncFence; import android.hardware.display.DisplayManager; import android.hardware.display.DisplayManager.DisplayListener; import android.hardware.display.DisplayManagerGlobal; @@ -219,6 +220,7 @@ import android.util.proto.ProtoOutputStream; import android.view.InputDevice.InputSourceClass; import android.view.Surface.OutOfResourcesException; import android.view.SurfaceControl.Transaction; +import android.view.SurfaceControl.TransactionStats; import android.view.View.AttachInfo; import android.view.View.FocusDirection; import android.view.View.MeasureSpec; @@ -293,6 +295,7 @@ import java.util.OptionalInt; import java.util.Queue; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; +import java.util.function.Consumer; import java.util.function.Predicate; /** * The top of a view hierarchy, implementing the needed protocol between View @@ -1189,6 +1192,13 @@ public final class ViewRootImpl implements ViewParent, private String mFpsTraceName; private String mLargestViewTraceName; + private final boolean mAppStartInfoTimestampsFlagValue; + @GuardedBy("this") + private boolean mAppStartTimestampsSent = false; + private boolean mAppStartTrackingStarted = false; + private long mRenderThreadDrawStartTimeNs = -1; + private long mFirstFramePresentedTimeNs = -1; + private static boolean sToolkitSetFrameRateReadOnlyFlagValue; private static boolean sToolkitFrameRateFunctionEnablingReadOnlyFlagValue; private static boolean sToolkitMetricsForFrameRateDecisionFlagValue; @@ -1306,6 +1316,8 @@ public final class ViewRootImpl implements ViewParent, } else { mSensitiveContentProtectionService = null; } + + mAppStartInfoTimestampsFlagValue = android.app.Flags.appStartInfoTimestamps(); } public static void addFirstDrawHandler(Runnable callback) { @@ -2578,6 +2590,12 @@ public final class ViewRootImpl implements ViewParent, notifySurfaceDestroyed(); } destroySurface(); + + // Reset so they can be sent again for warm starts. + mAppStartTimestampsSent = false; + mAppStartTrackingStarted = false; + mRenderThreadDrawStartTimeNs = -1; + mFirstFramePresentedTimeNs = -1; } } } @@ -4376,6 +4394,30 @@ public final class ViewRootImpl implements ViewParent, reportDrawFinished(t, seqId); } }); + + // Only trigger once per {@link ViewRootImpl} instance, so don't add listener if + // {link mTransactionCompletedTimeNs} has already been set. + if (mAppStartInfoTimestampsFlagValue && !mAppStartTrackingStarted) { + mAppStartTrackingStarted = true; + Transaction transaction = new Transaction(); + transaction.addTransactionCompletedListener(mExecutor, + new Consumer<TransactionStats>() { + @Override + public void accept(TransactionStats transactionStats) { + SyncFence presentFence = transactionStats.getPresentFence(); + if (presentFence.awaitForever()) { + if (mFirstFramePresentedTimeNs == -1) { + // Only trigger once per {@link ViewRootImpl} instance. + mFirstFramePresentedTimeNs = presentFence.getSignalTime(); + maybeSendAppStartTimes(); + } + } + presentFence.close(); + } + }); + applyTransactionOnDraw(transaction); + } + if (DEBUG_BLAST) { Log.d(mTag, "Setup new sync=" + mWmsRequestSyncGroup.getName()); } @@ -4383,6 +4425,45 @@ public final class ViewRootImpl implements ViewParent, mWmsRequestSyncGroup.add(this, null /* runnable */); } + private void maybeSendAppStartTimes() { + synchronized (this) { + if (mAppStartTimestampsSent) { + // Don't send timestamps more than once. + return; + } + + // If we already have {@link mRenderThreadDrawStartTimeNs} then pass it through, if not + // post to main thread and check if we have it there. + if (mRenderThreadDrawStartTimeNs != -1) { + sendAppStartTimesLocked(); + } else { + mHandler.post(new Runnable() { + @Override + public void run() { + synchronized (ViewRootImpl.this) { + if (mRenderThreadDrawStartTimeNs == -1) { + return; + } + sendAppStartTimesLocked(); + } + } + }); + } + } + } + + @GuardedBy("this") + private void sendAppStartTimesLocked() { + try { + ActivityManager.getService().reportStartInfoViewTimestamps( + mRenderThreadDrawStartTimeNs, mFirstFramePresentedTimeNs); + mAppStartTimestampsSent = true; + } catch (RemoteException e) { + // Ignore, timestamps may be lost. + if (DBG) Log.d(TAG, "Exception attempting to report start timestamps.", e); + } + } + /** * Helper used to notify the service to block projection when a sensitive * view (the view displays sensitive content) is attached to the window. @@ -5569,7 +5650,13 @@ public final class ViewRootImpl implements ViewParent, registerCallbackForPendingTransactions(); } + long timeNs = SystemClock.uptimeNanos(); mAttachInfo.mThreadedRenderer.draw(mView, mAttachInfo, this); + + // Only trigger once per {@link ViewRootImpl} instance. + if (mAppStartInfoTimestampsFlagValue && mRenderThreadDrawStartTimeNs == -1) { + mRenderThreadDrawStartTimeNs = timeNs; + } } else { // If we get here with a disabled & requested hardware renderer, something went // wrong (an invalidate posted right before we destroyed the hardware surface diff --git a/core/java/android/view/autofill/AutofillFeatureFlags.java b/core/java/android/view/autofill/AutofillFeatureFlags.java index 950dfeeacc8b..199a69a723d8 100644 --- a/core/java/android/view/autofill/AutofillFeatureFlags.java +++ b/core/java/android/view/autofill/AutofillFeatureFlags.java @@ -524,7 +524,7 @@ public class AutofillFeatureFlags { return DeviceConfig.getBoolean( DeviceConfig.NAMESPACE_AUTOFILL, DEVICE_CONFIG_INCLUDE_INVISIBLE_VIEW_GROUP_IN_ASSIST_STRUCTURE, - false); + true); } /** @hide */ diff --git a/core/java/android/view/autofill/AutofillId.java b/core/java/android/view/autofill/AutofillId.java index 15ba1a16cbfa..6b608582c1dd 100644 --- a/core/java/android/view/autofill/AutofillId.java +++ b/core/java/android/view/autofill/AutofillId.java @@ -61,6 +61,11 @@ public final class AutofillId implements Parcelable { } /** @hide */ + public AutofillId(@NonNull AutofillId hostId, int virtualChildId, int sessionId) { + this(FLAG_IS_VIRTUAL_INT | FLAG_HAS_SESSION, hostId.mViewId, virtualChildId, sessionId); + } + + /** @hide */ @TestApi public AutofillId(@NonNull AutofillId hostId, long virtualChildId, int sessionId) { this(FLAG_IS_VIRTUAL_LONG | FLAG_HAS_SESSION, hostId.mViewId, virtualChildId, sessionId); @@ -236,9 +241,9 @@ public final class AutofillId implements Parcelable { public String toString() { final StringBuilder builder = new StringBuilder().append(mViewId); if (isVirtualInt()) { - builder.append(':').append(mVirtualIntId); + builder.append(":i").append(mVirtualIntId); } else if (isVirtualLong()) { - builder.append(':').append(mVirtualLongId); + builder.append(":l").append(mVirtualLongId); } if (hasSession()) { diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java index ad513f1bc51b..53d9429acd36 100644 --- a/core/java/android/view/autofill/AutofillManager.java +++ b/core/java/android/view/autofill/AutofillManager.java @@ -193,6 +193,7 @@ import sun.misc.Cleaner; @RequiresFeature(PackageManager.FEATURE_AUTOFILL) public final class AutofillManager { + private static final boolean DBG = false; private static final String TAG = "AutofillManager"; /** @@ -2027,7 +2028,31 @@ public final class AutofillManager { if (!hasAutofillFeature()) { return; } + if (DBG) { + Log.v(TAG, "notifyValueChanged() called with virtualId:" + virtualId + " value:" + + value); + } synchronized (mLock) { + if (mLastAutofilledData != null) { + AutofillId id = new AutofillId(view.getAutofillId(), virtualId, mSessionId); + if (mLastAutofilledData.containsKey(id)) { + if (Objects.equals(mLastAutofilledData.get(id), value)) { + // Indicates that the view was autofilled + if (sDebug) { + Log.v(TAG, "notifyValueChanged() virtual view autofilled successfully:" + + virtualId + " value:" + value); + } + try { + mService.setViewAutofilled(mSessionId, id, mContext.getUserId()); + } catch (RemoteException e) { + // The failure could be a consequence of something going wrong on the + // server side. Do nothing here since it's just logging, but it's + // possible follow-up actions may fail. + Log.w(TAG, "RemoteException caught but ignored " + e); + } + } + } + } if (!mEnabled || !isActiveLocked()) { if (sVerbose) { Log.v(TAG, "notifyValueChanged(" + view.getAutofillId() + ":" + virtualId @@ -3114,6 +3139,10 @@ public final class AutofillManager { ArrayList<AutofillId> failedIds = new ArrayList<>(); + if (mLastAutofilledData == null) { + mLastAutofilledData = new ParcelableMap(itemCount); + } + for (int i = 0; i < itemCount; i++) { final AutofillId id = ids.get(i); final AutofillValue value = values.get(i); @@ -3126,6 +3155,9 @@ public final class AutofillManager { failedIds.add(id); continue; } + // Mark the view as to be autofilled with 'value' + mLastAutofilledData.put(id, value); + if (id.isVirtualInt()) { if (virtualValues == null) { // Most likely there will be just one view with virtual children. @@ -3139,12 +3171,6 @@ public final class AutofillManager { } valuesByParent.put(id.getVirtualChildIntId(), value); } else { - // Mark the view as to be autofilled with 'value' - if (mLastAutofilledData == null) { - mLastAutofilledData = new ParcelableMap(itemCount - i); - } - mLastAutofilledData.put(id, value); - view.autofill(value); // Set as autofilled if the values match now, e.g. when the value was updated diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java index bcef37f6e0c4..d74867c31bb6 100644 --- a/core/java/android/view/contentcapture/ContentCaptureManager.java +++ b/core/java/android/view/contentcapture/ContentCaptureManager.java @@ -366,6 +366,14 @@ public final class ContentCaptureManager { "enable_content_protection_receiver"; /** + * Whether AssistContent snapshot should be sent on activity start. + * + * @hide + */ + public static final String DEVICE_CONFIG_ENABLE_ACTIVITY_START_ASSIST_CONTENT = + "enable_activity_start_assist_content"; + + /** * Sets the size of the in-memory ring buffer for the content protection flow. * * @hide diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index 2d4bb90bf308..0c63e583f326 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -129,6 +129,7 @@ import java.io.FileDescriptor; import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.ref.WeakReference; import java.lang.reflect.Proxy; import java.util.Arrays; import java.util.Collections; @@ -2630,13 +2631,17 @@ public final class InputMethodManager { return false; } if (useDelegation) { + WeakReference<Executor> executorRef = new WeakReference<>(executor); + WeakReference<Consumer<Boolean>> callbackRef = new WeakReference<>(callback); if (useCallback) { IBooleanListener listener = new IBooleanListener.Stub() { @Override public void onResult(boolean value) { - executor.execute(() -> { - callback.accept(value); - }); + Executor executor = executorRef.get(); + Consumer<Boolean> callback = callbackRef.get(); + if (executor != null && callback != null) { + executor.execute(() -> callback.accept(value)); + } } }; if (!IInputMethodManagerGlobalInvoker.acceptStylusHandwritingDelegationAsync( diff --git a/core/java/android/widget/RemoteViews.java b/core/java/android/widget/RemoteViews.java index 15f9cff87458..3c5623f380f5 100644 --- a/core/java/android/widget/RemoteViews.java +++ b/core/java/android/widget/RemoteViews.java @@ -1044,11 +1044,6 @@ public class RemoteViews implements Parcelable, Filter { public int getActionTag() { return SET_PENDING_INTENT_TEMPLATE_TAG; } - - @Override - public void visitUris(@NonNull Consumer<Uri> visitor) { - mPendingIntentTemplate.visitUris(visitor); - } } /** @@ -1528,11 +1523,6 @@ public class RemoteViews implements Parcelable, Filter { public int getActionTag() { return SET_REMOTE_VIEW_ADAPTER_INTENT_TAG; } - - @Override - public void visitUris(@NonNull Consumer<Uri> visitor) { - mIntent.visitUris(visitor); - } } /** @@ -1611,11 +1601,6 @@ public class RemoteViews implements Parcelable, Filter { public int getActionTag() { return SET_ON_CLICK_RESPONSE_TAG; } - - @Override - public void visitUris(@NonNull Consumer<Uri> visitor) { - mResponse.visitUris(visitor); - } } /** Helper action to configure handwriting delegation via {@link PendingIntent}. */ @@ -1663,11 +1648,6 @@ public class RemoteViews implements Parcelable, Filter { public int getActionTag() { return SET_ON_STYLUS_HANDWRITING_RESPONSE_TAG; } - - @Override - public void visitUris(@NonNull Consumer<Uri> visitor) { - mPendingIntent.visitUris(visitor); - } } /** @@ -1738,11 +1718,6 @@ public class RemoteViews implements Parcelable, Filter { public int getActionTag() { return SET_ON_CHECKED_CHANGE_RESPONSE_TAG; } - - @Override - public void visitUris(@NonNull Consumer<Uri> visitor) { - mResponse.visitUris(visitor); - } } /** @hide **/ @@ -2302,10 +2277,6 @@ public class RemoteViews implements Parcelable, Filter { final Icon icon = (Icon) getParameterValue(null); if (icon != null) visitIconUri(icon, visitor); break; - case INTENT: - final Intent intent = (Intent) getParameterValue(null); - if (intent != null) intent.visitUris(visitor); - break; // TODO(b/281044385): Should we do anything about type BUNDLE? } } @@ -7226,20 +7197,6 @@ public class RemoteViews implements Parcelable, Filter { mElementNames = parcel.createStringArrayList(); } - /** - * See {@link RemoteViews#visitUris(Consumer)}. - * - * @hide - */ - public void visitUris(@NonNull Consumer<Uri> visitor) { - if (mPendingIntent != null) { - mPendingIntent.visitUris(visitor); - } - if (mFillIntent != null) { - mFillIntent.visitUris(visitor); - } - } - private void handleViewInteraction( View v, InteractionHandler handler) { diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java index c14a6c1ca5ba..63ff598bae61 100644 --- a/core/java/com/android/internal/policy/DecorView.java +++ b/core/java/com/android/internal/policy/DecorView.java @@ -1227,7 +1227,10 @@ public class DecorView extends FrameLayout implements RootViewSurfaceTaker, Wind requestApplyInsets(); } } - if (insets != null) { + if (insets != null && (consumedLeft > 0 + || consumedTop > 0 + || consumedRight > 0 + || consumedBottom > 0)) { insets = insets.inset(consumedLeft, consumedTop, consumedRight, consumedBottom); } } diff --git a/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java b/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java index 55f2dee95a34..00262be06623 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java +++ b/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java @@ -30,7 +30,7 @@ public class CoreDocument { ArrayList<Operation> mOperations; RemoteComposeState mRemoteComposeState = new RemoteComposeState(); - + TimeVariables mTimeVariables = new TimeVariables(); // Semantic version of the document Version mVersion = new Version(0, 1, 0); @@ -70,6 +70,7 @@ public class CoreDocument { public void setWidth(int width) { this.mWidth = width; + mRemoteComposeState.setWindowWidth(width); } public int getHeight() { @@ -78,6 +79,8 @@ public class CoreDocument { public void setHeight(int height) { this.mHeight = height; + mRemoteComposeState.setWindowHeight(height); + } public RemoteComposeBuffer getBuffer() { @@ -111,21 +114,21 @@ public class CoreDocument { /** * Sets the way the player handles the content * - * @param scroll set the horizontal behavior (NONE|SCROLL_HORIZONTAL|SCROLL_VERTICAL) + * @param scroll set the horizontal behavior (NONE|SCROLL_HORIZONTAL|SCROLL_VERTICAL) * @param alignment set the alignment of the content (TOP|CENTER|BOTTOM|START|END) - * @param sizing set the type of sizing for the content (NONE|SIZING_LAYOUT|SIZING_SCALE) - * @param mode set the mode of sizing, either LAYOUT modes or SCALE modes - * the LAYOUT modes are: - * - LAYOUT_MATCH_PARENT - * - LAYOUT_WRAP_CONTENT - * or adding an horizontal mode and a vertical mode: - * - LAYOUT_HORIZONTAL_MATCH_PARENT - * - LAYOUT_HORIZONTAL_WRAP_CONTENT - * - LAYOUT_HORIZONTAL_FIXED - * - LAYOUT_VERTICAL_MATCH_PARENT - * - LAYOUT_VERTICAL_WRAP_CONTENT - * - LAYOUT_VERTICAL_FIXED - * The LAYOUT_*_FIXED modes will use the intrinsic document size + * @param sizing set the type of sizing for the content (NONE|SIZING_LAYOUT|SIZING_SCALE) + * @param mode set the mode of sizing, either LAYOUT modes or SCALE modes + * the LAYOUT modes are: + * - LAYOUT_MATCH_PARENT + * - LAYOUT_WRAP_CONTENT + * or adding an horizontal mode and a vertical mode: + * - LAYOUT_HORIZONTAL_MATCH_PARENT + * - LAYOUT_HORIZONTAL_WRAP_CONTENT + * - LAYOUT_HORIZONTAL_FIXED + * - LAYOUT_VERTICAL_MATCH_PARENT + * - LAYOUT_VERTICAL_WRAP_CONTENT + * - LAYOUT_VERTICAL_FIXED + * The LAYOUT_*_FIXED modes will use the intrinsic document size */ public void setRootContentBehavior(int scroll, int alignment, int sizing, int mode) { this.mContentScroll = scroll; @@ -138,8 +141,8 @@ public class CoreDocument { * Given dimensions w x h of where to paint the content, returns the corresponding scale factor * according to the contentSizing information * - * @param w horizontal dimension of the rendering area - * @param h vertical dimension of the rendering area + * @param w horizontal dimension of the rendering area + * @param h vertical dimension of the rendering area * @param scaleOutput will contain the computed scale factor */ public void computeScale(float w, float h, float[] scaleOutput) { @@ -154,37 +157,43 @@ public class CoreDocument { float scale = Math.min(1f, Math.min(scaleX, scaleY)); contentScaleX = scale; contentScaleY = scale; - } break; + } + break; case RootContentBehavior.SCALE_FIT: { float scaleX = w / mWidth; float scaleY = h / mHeight; float scale = Math.min(scaleX, scaleY); contentScaleX = scale; contentScaleY = scale; - } break; + } + break; case RootContentBehavior.SCALE_FILL_WIDTH: { float scale = w / mWidth; contentScaleX = scale; contentScaleY = scale; - } break; + } + break; case RootContentBehavior.SCALE_FILL_HEIGHT: { float scale = h / mHeight; contentScaleX = scale; contentScaleY = scale; - } break; + } + break; case RootContentBehavior.SCALE_CROP: { float scaleX = w / mWidth; float scaleY = h / mHeight; float scale = Math.max(scaleX, scaleY); contentScaleX = scale; contentScaleY = scale; - } break; + } + break; case RootContentBehavior.SCALE_FILL_BOUNDS: { float scaleX = w / mWidth; float scaleY = h / mHeight; contentScaleX = scaleX; contentScaleY = scaleY; - } break; + } + break; default: // nothing } @@ -197,10 +206,10 @@ public class CoreDocument { * Given dimensions w x h of where to paint the content, returns the corresponding translation * according to the contentAlignment information * - * @param w horizontal dimension of the rendering area - * @param h vertical dimension of the rendering area - * @param contentScaleX the horizontal scale we are going to use for the content - * @param contentScaleY the vertical scale we are going to use for the content + * @param w horizontal dimension of the rendering area + * @param h vertical dimension of the rendering area + * @param contentScaleX the horizontal scale we are going to use for the content + * @param contentScaleY the vertical scale we are going to use for the content * @param translateOutput will contain the computed translation */ private void computeTranslate(float w, float h, float contentScaleX, float contentScaleY, @@ -215,26 +224,32 @@ public class CoreDocument { switch (horizontalContentAlignment) { case RootContentBehavior.ALIGNMENT_START: { // nothing - } break; + } + break; case RootContentBehavior.ALIGNMENT_HORIZONTAL_CENTER: { translateX = (w - contentWidth) / 2f; - } break; + } + break; case RootContentBehavior.ALIGNMENT_END: { translateX = w - contentWidth; - } break; + } + break; default: // nothing (same as alignment_start) } switch (verticalContentAlignment) { case RootContentBehavior.ALIGNMENT_TOP: { // nothing - } break; + } + break; case RootContentBehavior.ALIGNMENT_VERTICAL_CENTER: { translateY = (h - contentHeight) / 2f; - } break; + } + break; case RootContentBehavior.ALIGNMENT_BOTTOM: { translateY = h - contentHeight; - } break; + } + break; default: // nothing (same as alignment_top) } @@ -291,7 +306,13 @@ public class CoreDocument { this.mMetadata = metadata; } - public boolean contains(float x, float y) { + /** + * Returns true if x,y coordinate is within bounds + * @param x x-coordinate + * @param y y-coordinate + * @return x,y coordinate is within bounds + */ + public boolean contains(float x, float y) { return x >= mLeft && x < mRight && y >= mTop && y < mBottom; } @@ -341,16 +362,22 @@ public class CoreDocument { public void initializeContext(RemoteContext context) { mRemoteComposeState.reset(); mClickAreas.clear(); - + mRemoteComposeState.setNextId(RemoteComposeState.START_ID); context.mDocument = this; context.mRemoteComposeState = mRemoteComposeState; - // mark context to be in DATA mode, which will skip the painting ops. context.mMode = RemoteContext.ContextMode.DATA; - for (Operation op: mOperations) { + mTimeVariables.updateTime(context); + + for (Operation op : mOperations) { + if (op instanceof VariableSupport) { + ((VariableSupport) op).updateVariables(context); + ((VariableSupport) op).registerListening(context); + } op.apply(context); } context.mMode = RemoteContext.ContextMode.UNSET; + } /////////////////////////////////////////////////////////////////////////////////////////////// @@ -375,7 +402,7 @@ public class CoreDocument { * @param minorVersion minor version number, increased when adding new features * @param patch patch level, increased upon bugfixes */ - void setVersion(int majorVersion, int minorVersion, int patch) { + void setVersion(int majorVersion, int minorVersion, int patch) { mVersion = new Version(majorVersion, minorVersion, patch); } @@ -389,13 +416,13 @@ public class CoreDocument { * the click coordinates will be the one reported; the order of addition of those click areas * is therefore meaningful. * - * @param id the id of the area, which will be reported on click + * @param id the id of the area, which will be reported on click * @param contentDescription the content description (used for accessibility) - * @param left the left coordinate of the click area (in pixels) - * @param top the top coordinate of the click area (in pixels) - * @param right the right coordinate of the click area (in pixels) - * @param bottom the bottom coordinate of the click area (in pixels) - * @param metadata arbitrary metadata associated with the are, also reported on click + * @param left the left coordinate of the click area (in pixels) + * @param top the top coordinate of the click area (in pixels) + * @param right the right coordinate of the click area (in pixels) + * @param bottom the bottom coordinate of the click area (in pixels) + * @param metadata arbitrary metadata associated with the are, also reported on click */ public void addClickArea(int id, String contentDescription, float left, float top, float right, float bottom, String metadata) { @@ -417,7 +444,7 @@ public class CoreDocument { * listeners. */ public void onClick(float x, float y) { - for (ClickAreaRepresentation clickArea: mClickAreas) { + for (ClickAreaRepresentation clickArea : mClickAreas) { if (clickArea.contains(x, y)) { warnClickListeners(clickArea); } @@ -430,7 +457,7 @@ public class CoreDocument { * @param id the click area id */ public void performClick(int id) { - for (ClickAreaRepresentation clickArea: mClickAreas) { + for (ClickAreaRepresentation clickArea : mClickAreas) { if (clickArea.mId == id) { warnClickListeners(clickArea); } @@ -441,17 +468,36 @@ public class CoreDocument { * Warn click listeners when a click area is activated */ private void warnClickListeners(ClickAreaRepresentation clickArea) { - for (ClickCallbacks listener: mClickListeners) { + for (ClickCallbacks listener : mClickListeners) { listener.click(clickArea.mId, clickArea.mMetadata); } } - /////////////////////////////////////////////////////////////////////////////////////////////// + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + for (Operation op : mOperations) { + builder.append(op.toString()); + builder.append("\n"); + } + return builder.toString(); + } + + ////////////////////////////////////////////////////////////////////////// // Painting - /////////////////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////// private final float[] mScaleOutput = new float[2]; private final float[] mTranslateOutput = new float[2]; + private int mRepaintNext = -1; // delay to next repaint -1 = don't 1 = asap + + /** + * Returns > 0 if it needs to repaint + * @return + */ + public int needsRepaint() { + return mRepaintNext; + } /** * Paint the document @@ -475,6 +521,11 @@ public class CoreDocument { context.mPaintContext.translate(mTranslateOutput[0], mTranslateOutput[1]); context.mPaintContext.scale(mScaleOutput[0], mScaleOutput[1]); } + mTimeVariables.updateTime(context); + context.loadFloat(RemoteContext.ID_WINDOW_WIDTH, getWidth()); + context.loadFloat(RemoteContext.ID_WINDOW_HEIGHT, getHeight()); + mRepaintNext = context.updateOps(); + for (Operation op : mOperations) { // operations will only be executed if no theme is set (ie UNSPECIFIED) // or the theme is equal as the one passed in argument to paint. diff --git a/core/java/com/android/internal/widget/remotecompose/core/Operations.java b/core/java/com/android/internal/widget/remotecompose/core/Operations.java index 54b277a2ac58..4b45ab691215 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/Operations.java +++ b/core/java/com/android/internal/widget/remotecompose/core/Operations.java @@ -19,6 +19,7 @@ import com.android.internal.widget.remotecompose.core.operations.BitmapData; import com.android.internal.widget.remotecompose.core.operations.ClickArea; import com.android.internal.widget.remotecompose.core.operations.ClipPath; import com.android.internal.widget.remotecompose.core.operations.ClipRect; +import com.android.internal.widget.remotecompose.core.operations.ColorExpression; import com.android.internal.widget.remotecompose.core.operations.DrawArc; import com.android.internal.widget.remotecompose.core.operations.DrawBitmap; import com.android.internal.widget.remotecompose.core.operations.DrawBitmapInt; @@ -28,9 +29,12 @@ import com.android.internal.widget.remotecompose.core.operations.DrawOval; import com.android.internal.widget.remotecompose.core.operations.DrawPath; import com.android.internal.widget.remotecompose.core.operations.DrawRect; import com.android.internal.widget.remotecompose.core.operations.DrawRoundRect; +import com.android.internal.widget.remotecompose.core.operations.DrawText; +import com.android.internal.widget.remotecompose.core.operations.DrawTextAnchored; import com.android.internal.widget.remotecompose.core.operations.DrawTextOnPath; -import com.android.internal.widget.remotecompose.core.operations.DrawTextRun; import com.android.internal.widget.remotecompose.core.operations.DrawTweenPath; +import com.android.internal.widget.remotecompose.core.operations.FloatConstant; +import com.android.internal.widget.remotecompose.core.operations.FloatExpression; import com.android.internal.widget.remotecompose.core.operations.Header; import com.android.internal.widget.remotecompose.core.operations.MatrixRestore; import com.android.internal.widget.remotecompose.core.operations.MatrixRotate; @@ -42,7 +46,10 @@ import com.android.internal.widget.remotecompose.core.operations.PaintData; import com.android.internal.widget.remotecompose.core.operations.PathData; import com.android.internal.widget.remotecompose.core.operations.RootContentBehavior; import com.android.internal.widget.remotecompose.core.operations.RootContentDescription; +import com.android.internal.widget.remotecompose.core.operations.ShaderData; import com.android.internal.widget.remotecompose.core.operations.TextData; +import com.android.internal.widget.remotecompose.core.operations.TextFromFloat; +import com.android.internal.widget.remotecompose.core.operations.TextMerge; import com.android.internal.widget.remotecompose.core.operations.Theme; import com.android.internal.widget.remotecompose.core.operations.utilities.IntMap; @@ -67,9 +74,10 @@ public class Operations { public static final int DRAW_BITMAP = 44; public static final int DRAW_BITMAP_INT = 66; public static final int DATA_BITMAP = 101; + public static final int DATA_SHADER = 45; public static final int DATA_TEXT = 102; -/////////////////////////////===================== + /////////////////////////////===================== public static final int CLIP_PATH = 38; public static final int CLIP_RECT = 39; public static final int PAINT_VALUES = 40; @@ -91,6 +99,12 @@ public class Operations { public static final int MATRIX_SAVE = 130; public static final int MATRIX_RESTORE = 131; public static final int MATRIX_SET = 132; + public static final int DATA_FLOAT = 80; + public static final int ANIMATED_FLOAT = 81; + public static final int DRAW_TEXT_ANCHOR = 133; + public static final int COLOR_EXPRESSIONS = 134; + public static final int TEXT_FROM_FLOAT = 135; + public static final int TEXT_MERGE = 136; /////////////////////////////////////////====================== public static IntMap<CompanionOperation> map = new IntMap<>(); @@ -114,7 +128,7 @@ public class Operations { map.put(DRAW_RECT, DrawRect.COMPANION); map.put(DRAW_ROUND_RECT, DrawRoundRect.COMPANION); map.put(DRAW_TEXT_ON_PATH, DrawTextOnPath.COMPANION); - map.put(DRAW_TEXT_RUN, DrawTextRun.COMPANION); + map.put(DRAW_TEXT_RUN, DrawText.COMPANION); map.put(DRAW_TWEEN_PATH, DrawTweenPath.COMPANION); map.put(DATA_PATH, PathData.COMPANION); map.put(PAINT_VALUES, PaintData.COMPANION); @@ -126,6 +140,13 @@ public class Operations { map.put(MATRIX_TRANSLATE, MatrixTranslate.COMPANION); map.put(CLIP_PATH, ClipPath.COMPANION); map.put(CLIP_RECT, ClipRect.COMPANION); + map.put(DATA_SHADER, ShaderData.COMPANION); + map.put(DATA_FLOAT, FloatConstant.COMPANION); + map.put(ANIMATED_FLOAT, FloatExpression.COMPANION); + map.put(DRAW_TEXT_ANCHOR, DrawTextAnchored.COMPANION); + map.put(COLOR_EXPRESSIONS, ColorExpression.COMPANION); + map.put(TEXT_FROM_FLOAT, TextFromFloat.COMPANION); + map.put(TEXT_MERGE, TextMerge.COMPANION); } diff --git a/core/java/com/android/internal/widget/remotecompose/core/PaintContext.java b/core/java/com/android/internal/widget/remotecompose/core/PaintContext.java index eece8ad52b60..ecd0efceacf3 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/PaintContext.java +++ b/core/java/com/android/internal/widget/remotecompose/core/PaintContext.java @@ -68,7 +68,35 @@ public abstract class PaintContext { public abstract void drawTextOnPath(int textId, int pathId, float hOffset, float vOffset); - public abstract void drawTextRun(int textID, + /** + * Return the dimensions (left, top, right, bottom). + * Relative to a drawTextRun x=0, y=0; + * + * @param textId + * @param start + * @param end if end is -1 it means the whole string + * @param monospace measure with better support for monospace + * @param bounds the bounds (left, top, right, bottom) + */ + public abstract void getTextBounds(int textId, + int start, + int end, + boolean monospace, + float[]bounds); + + /** + * Draw a text starting ast x,y + * + * @param textId reference to the text + * @param start + * @param end + * @param contextStart + * @param contextEnd + * @param x + * @param y + * @param rtl + */ + public abstract void drawTextRun(int textId, int start, int end, int contextStart, @@ -77,6 +105,14 @@ public abstract class PaintContext { float y, boolean rtl); + /** + * Draw an interpolation between two paths + * @param path1Id + * @param path2Id + * @param tween 0.0 = is path1 1.0 is path2 + * @param start + * @param stop + */ public abstract void drawTweenPath(int path1Id, int path2Id, float tween, @@ -85,21 +121,70 @@ public abstract class PaintContext { public abstract void applyPaint(PaintBundle mPaintData); - public abstract void mtrixScale(float scaleX, float scaleY, float centerX, float centerY); - + /** + * Scale the rendering by scaleX and saleY (1.0 = no scale). + * Scaling is done about centerX,centerY. + * + * @param scaleX + * @param scaleY + * @param centerX + * @param centerY + */ + public abstract void matrixScale(float scaleX, float scaleY, float centerX, float centerY); + + /** + * Translate the rendering + * @param translateX + * @param translateY + */ public abstract void matrixTranslate(float translateX, float translateY); + /** + * Skew the rendering + * @param skewX + * @param skewY + */ public abstract void matrixSkew(float skewX, float skewY); + /** + * Rotate the rendering. + * Note rotates are cumulative. + * @param rotate angle to rotate + * @param pivotX x-coordinate about which to rotate + * @param pivotY y-coordinate about which to rotate + */ public abstract void matrixRotate(float rotate, float pivotX, float pivotY); + /** + * Save the current state of the transform + */ public abstract void matrixSave(); + /** + * Restore the previously saved state of the transform + */ public abstract void matrixRestore(); + /** + * Set the clip to a rectangle. + * Drawing outside the current clip region will have no effect + * @param left + * @param top + * @param right + * @param bottom + */ public abstract void clipRect(float left, float top, float right, float bottom); + /** + * Clip based on a path. + * @param pathId + * @param regionOp + */ public abstract void clipPath(int pathId, int regionOp); + /** + * Reset the paint + */ + public abstract void reset(); } diff --git a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java index c2e81318c09a..52fc3143d721 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java +++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java @@ -19,6 +19,7 @@ import com.android.internal.widget.remotecompose.core.operations.BitmapData; import com.android.internal.widget.remotecompose.core.operations.ClickArea; import com.android.internal.widget.remotecompose.core.operations.ClipPath; import com.android.internal.widget.remotecompose.core.operations.ClipRect; +import com.android.internal.widget.remotecompose.core.operations.ColorExpression; import com.android.internal.widget.remotecompose.core.operations.DrawArc; import com.android.internal.widget.remotecompose.core.operations.DrawBitmap; import com.android.internal.widget.remotecompose.core.operations.DrawBitmapInt; @@ -28,9 +29,12 @@ import com.android.internal.widget.remotecompose.core.operations.DrawOval; import com.android.internal.widget.remotecompose.core.operations.DrawPath; import com.android.internal.widget.remotecompose.core.operations.DrawRect; import com.android.internal.widget.remotecompose.core.operations.DrawRoundRect; +import com.android.internal.widget.remotecompose.core.operations.DrawText; +import com.android.internal.widget.remotecompose.core.operations.DrawTextAnchored; import com.android.internal.widget.remotecompose.core.operations.DrawTextOnPath; -import com.android.internal.widget.remotecompose.core.operations.DrawTextRun; import com.android.internal.widget.remotecompose.core.operations.DrawTweenPath; +import com.android.internal.widget.remotecompose.core.operations.FloatConstant; +import com.android.internal.widget.remotecompose.core.operations.FloatExpression; import com.android.internal.widget.remotecompose.core.operations.Header; import com.android.internal.widget.remotecompose.core.operations.MatrixRestore; import com.android.internal.widget.remotecompose.core.operations.MatrixRotate; @@ -43,8 +47,12 @@ import com.android.internal.widget.remotecompose.core.operations.PathData; import com.android.internal.widget.remotecompose.core.operations.RootContentBehavior; import com.android.internal.widget.remotecompose.core.operations.RootContentDescription; import com.android.internal.widget.remotecompose.core.operations.TextData; +import com.android.internal.widget.remotecompose.core.operations.TextFromFloat; +import com.android.internal.widget.remotecompose.core.operations.TextMerge; import com.android.internal.widget.remotecompose.core.operations.Theme; +import com.android.internal.widget.remotecompose.core.operations.Utils; import com.android.internal.widget.remotecompose.core.operations.paint.PaintBundle; +import com.android.internal.widget.remotecompose.core.operations.utilities.easing.FloatAnimation; import java.io.File; import java.io.FileInputStream; @@ -58,9 +66,20 @@ import java.util.Arrays; * Provides an abstract buffer to encode/decode RemoteCompose operations */ public class RemoteComposeBuffer { + public static final int EASING_CUBIC_STANDARD = FloatAnimation.CUBIC_STANDARD; + public static final int EASING_CUBIC_ACCELERATE = FloatAnimation.CUBIC_ACCELERATE; + public static final int EASING_CUBIC_DECELERATE = FloatAnimation.CUBIC_DECELERATE; + public static final int EASING_CUBIC_LINEAR = FloatAnimation.CUBIC_LINEAR; + public static final int EASING_CUBIC_ANTICIPATE = FloatAnimation.CUBIC_ANTICIPATE; + public static final int EASING_CUBIC_OVERSHOOT = FloatAnimation.CUBIC_OVERSHOOT; + public static final int EASING_CUBIC_CUSTOM = FloatAnimation.CUBIC_CUSTOM; + public static final int EASING_SPLINE_CUSTOM = FloatAnimation.SPLINE_CUSTOM; + public static final int EASING_EASE_OUT_BOUNCE = FloatAnimation.EASE_OUT_BOUNCE; + public static final int EASING_EASE_OUT_ELASTIC = FloatAnimation.EASE_OUT_ELASTIC; WireBuffer mBuffer = new WireBuffer(); Platform mPlatform = null; RemoteComposeState mRemoteComposeState; + private static final boolean DEBUG = false; /** * Provides an abstract buffer to encode/decode RemoteCompose operations @@ -171,7 +190,7 @@ public class RemoteComposeBuffer { * * @param text the string to inject in the buffer */ - int addText(String text) { + public int addText(String text) { int id = mRemoteComposeState.dataGetId(text); if (id == -1) { id = mRemoteComposeState.cache(text); @@ -350,7 +369,6 @@ public class RemoteComposeBuffer { addDrawPath(id); } - /** * Draw the specified path * @@ -426,12 +444,160 @@ public class RemoteComposeBuffer { float y, boolean rtl) { int textId = addText(text); - DrawTextRun.COMPANION.apply( + DrawText.COMPANION.apply( mBuffer, textId, start, end, contextStart, contextEnd, x, y, rtl); } /** + * Draw the text, with origin at (x,y). The origin is interpreted + * based on the Align setting in the paint. + * + * @param textId The text to be drawn + * @param start The index of the first character in text to draw + * @param end (end - 1) is the index of the last character in text to draw + * @param contextStart + * @param contextEnd + * @param x The x-coordinate of the origin of the text being drawn + * @param y The y-coordinate of the baseline of the text being drawn + * @param rtl Draw RTTL + */ + public void addDrawTextRun(int textId, + int start, + int end, + int contextStart, + int contextEnd, + float x, + float y, + boolean rtl) { + DrawText.COMPANION.apply( + mBuffer, textId, start, end, + contextStart, contextEnd, x, y, rtl); + } + + /** + * Draw a text on canvas at relative to position (x, y), + * offset panX and panY. + * <br> + * The panning factors (panX, panY) mapped to the + * resulting bounding box of the text, in such a way that a + * panning factor of (0.0, 0.0) would center the text at (x, y) + * <ul> + * <li> Panning of -1.0, -1.0 - the text above & right of x,y.</li> + * <li>Panning of 1.0, 1.0 - the text is below and to the left</li> + * <li>Panning of 1.0, 0.0 - the test is centered & to the right of x,y</li> + * </ul> + * Setting panY to NaN results in y being the baseline of the text. + * + * @param text text to draw + * @param x Coordinate of the Anchor + * @param y Coordinate of the Anchor + * @param panX justifies text -1.0=right, 0.0=center, 1.0=left + * @param panY position text -1.0=above, 0.0=center, 1.0=below, Nan=baseline + * @param flags 1 = RTL + */ + public void drawTextAnchored(String text, + float x, + float y, + float panX, + float panY, + int flags) { + int textId = addText(text); + DrawTextAnchored.COMPANION.apply( + mBuffer, textId, + x, y, + panX, panY, + flags); + } + + /** + * Add a text and id so that it can be used + * + * @param text + * @return + */ + public int createTextId(String text) { + return addText(text); + } + + /** + * Merge two text (from id's) output one id + * @param id1 left id + * @param id2 right id + * @return new id that merges the two text + */ + public int textMerge(int id1, int id2) { + int textId = addText(id1 + "+" + id2); + TextMerge.COMPANION.apply(mBuffer, textId, id1, id2); + return textId; + } + + public static final int PAD_AFTER_SPACE = TextFromFloat.PAD_AFTER_SPACE; + public static final int PAD_AFTER_NONE = TextFromFloat.PAD_AFTER_NONE; + public static final int PAD_AFTER_ZERO = TextFromFloat.PAD_AFTER_ZERO; + public static final int PAD_PRE_SPACE = TextFromFloat.PAD_PRE_SPACE; + public static final int PAD_PRE_NONE = TextFromFloat.PAD_PRE_NONE; + public static final int PAD_PRE_ZERO = TextFromFloat.PAD_PRE_ZERO; + + /** + * Create a TextFromFloat command which creates text from a Float. + * + * @param value The value to convert + * @param digitsBefore the digits before the decimal point + * @param digitsAfter the digits after the decimal point + * @param flags configure the behaviour using PAD_PRE_* and PAD_AFTER* flags + * @return id of the string that can be passed to drawTextAnchored + */ + public int createTextFromFloat(float value, short digitsBefore, + short digitsAfter, int flags) { + String placeHolder = Utils.floatToString(value) + + "(" + digitsBefore + "," + digitsAfter + "," + flags + ")"; + int id = mRemoteComposeState.dataGetId(placeHolder); + if (id == -1) { + id = mRemoteComposeState.cache(placeHolder); + // TextData.COMPANION.apply(mBuffer, id, text); + } + TextFromFloat.COMPANION.apply(mBuffer, id, value, digitsBefore, + digitsAfter, flags); + return id; + } + + /** + * Draw a text on canvas at relative to position (x, y), + * offset panX and panY. + * <br> + * The panning factors (panX, panY) mapped to the + * resulting bounding box of the text, in such a way that a + * panning factor of (0.0, 0.0) would center the text at (x, y) + * <ul> + * <li> Panning of -1.0, -1.0 - the text above & right of x,y.</li> + * <li>Panning of 1.0, 1.0 - the text is below and to the left</li> + * <li>Panning of 1.0, 0.0 - the test is centered & to the right of x,y</li> + * </ul> + * Setting panY to NaN results in y being the baseline of the text. + * + * @param textId text to draw + * @param x Coordinate of the Anchor + * @param y Coordinate of the Anchor + * @param panX justifies text -1.0=right, 0.0=center, 1.0=left + * @param panY position text -1.0=above, 0.0=center, 1.0=below, Nan=baseline + * @param flags 1 = RTL + */ + public void drawTextAnchored(int textId, + float x, + float y, + float panX, + float panY, + int flags) { + + DrawTextAnchored.COMPANION.apply( + mBuffer, textId, + x, y, + panX, panY, + flags); + } + + /** * draw an interpolation between two paths that have the same pattern * <p> * Warning paths objects are not immutable and this is not taken into consideration @@ -490,6 +656,10 @@ public class RemoteComposeBuffer { return id; } + /** + * Adds a paint Bundle to the doc + * @param paint + */ public void addPaint(PaintBundle paint) { PaintData.COMPANION.apply(mBuffer, paint); } @@ -499,7 +669,9 @@ public class RemoteComposeBuffer { mBuffer.setIndex(0); while (mBuffer.available()) { int opId = mBuffer.readByte(); - System.out.println(">>> " + opId); + if (DEBUG) { + Utils.log(">> " + opId); + } CompanionOperation operation = Operations.map.get(opId); if (operation == null) { throw new RuntimeException("Unknown operation encountered " + opId); @@ -519,7 +691,6 @@ public class RemoteComposeBuffer { Theme.COMPANION.apply(mBuffer, theme); } - static String version() { return "v1.0"; } @@ -654,8 +825,8 @@ public class RemoteComposeBuffer { /** * Add a pre-concat of the current matrix with the specified scale. * - * @param scaleX The amount to scale in X - * @param scaleY The amount to scale in Y + * @param scaleX The amount to scale in X + * @param scaleY The amount to scale in Y */ public void addMatrixScale(float scaleX, float scaleY) { MatrixScale.COMPANION.apply(mBuffer, scaleX, scaleY, Float.NaN, Float.NaN); @@ -673,12 +844,174 @@ public class RemoteComposeBuffer { MatrixScale.COMPANION.apply(mBuffer, scaleX, scaleY, centerX, centerY); } + /** + * sets the clip based on clip id + * @param pathId 0 clears the clip + */ public void addClipPath(int pathId) { ClipPath.COMPANION.apply(mBuffer, pathId); } + /** + * Sets the clip based on clip rec + * @param left + * @param top + * @param right + * @param bottom + */ public void addClipRect(float left, float top, float right, float bottom) { ClipRect.COMPANION.apply(mBuffer, left, top, right, bottom); } + + /** + * Add a float return a NaN number pointing to that float + * @param value + * @return + */ + public float addFloat(float value) { + int id = mRemoteComposeState.cacheFloat(value); + FloatConstant.COMPANION.apply(mBuffer, id, value); + return Utils.asNan(id); + } + + /** + * Add a float that is a computation based on variables + * @param value A RPN style float operation i.e. "4, 3, ADD" outputs 7 + * @return NaN id of the result of the calculation + */ + public float addAnimatedFloat(float... value) { + int id = mRemoteComposeState.cache(value); + FloatExpression.COMPANION.apply(mBuffer, id, value, null); + return Utils.asNan(id); + } + + /** + * Add a float that is a computation based on variables. + * see packAnimation + * @param value A RPN style float operation i.e. "4, 3, ADD" outputs 7 + * @param animation Array of floats that represents animation + * @return NaN id of the result of the calculation + */ + public float addAnimatedFloat(float[] value, float[] animation) { + int id = mRemoteComposeState.cache(value); + FloatExpression.COMPANION.apply(mBuffer, id, value, animation); + return Utils.asNan(id); + } + + /** + * Add a color that represents the tween between two colors + * @param color1 + * @param color2 + * @param tween + * @return id of the color (color ids are short) + */ + public short addColorExpression(int color1, int color2, float tween) { + ColorExpression c = new ColorExpression(0, 0, color1, color2, tween); + short id = (short) mRemoteComposeState.cache(c); + c.mId = id; + c.write(mBuffer); + return id; + } + + /** + * Add a color that represents the tween between two colors where color1 + * is the id of a color + * @param color1 + * @param color2 + * @param tween + * @return id of the color (color ids are short) + */ + public short addColorExpression(short color1, int color2, float tween) { + ColorExpression c = new ColorExpression(0, 1, color1, color2, tween); + short id = (short) mRemoteComposeState.cache(c); + c.mId = id; + c.write(mBuffer); + return id; + } + + /** + * Add a color that represents the tween between two colors where color2 + * is the id of a color + * @param color1 + * @param color2 + * @param tween + * @return id of the color (color ids are short) + */ + public short addColorExpression(int color1, short color2, float tween) { + ColorExpression c = new ColorExpression(0, 2, color1, color2, tween); + short id = (short) mRemoteComposeState.cache(c); + c.mId = id; + c.write(mBuffer); + return id; + } + + /** + * Add a color that represents the tween between two colors where color1 & + * color2 are the ids of colors + * @param color1 + * @param color2 + * @param tween + * @return id of the color (color ids are short) + */ + public short addColorExpression(short color1, short color2, float tween) { + ColorExpression c = new ColorExpression(0, 3, color1, color2, tween); + short id = (short) mRemoteComposeState.cache(c); + c.mId = id; + c.write(mBuffer); + return id; + } + + /** + * Color calculated by Hue saturation and value. + * (as floats they can be variables used to create color transitions) + * @param hue + * @param sat + * @param value + * @return id of the color (color ids are short) + */ + public short addColorExpression(float hue, float sat, float value) { + ColorExpression c = new ColorExpression(0, hue, sat, value); + short id = (short) mRemoteComposeState.cache(c); + c.mId = id; + c.write(mBuffer); + return id; + } + + /** + * Color calculated by Alpha, Hue saturation and value. + * (as floats they can be variables used to create color transitions) + * @param alpha + * @param hue + * @param sat + * @param value + * @return id of the color (color ids are short) + */ + public short addColorExpression(int alpha, float hue, float sat, float value) { + ColorExpression c = new ColorExpression(0, alpha, hue, sat, value); + short id = (short) mRemoteComposeState.cache(c); + c.mId = id; + c.write(mBuffer); + return id; + } + + /** + * create and animation based on description and return as an array of + * floats. see addAnimatedFloat + * @param duration + * @param type + * @param spec + * @param initialValue + * @param wrap + * @return + */ + public static float[] packAnimation(float duration, + int type, + float[] spec, + float initialValue, + float wrap) { + + return FloatAnimation.packToFloatArray(duration, type, spec, initialValue, wrap); + } + } diff --git a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java index 17e8c839a080..66a37e677499 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java +++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java @@ -15,26 +15,53 @@ */ package com.android.internal.widget.remotecompose.core; +import static com.android.internal.widget.remotecompose.core.RemoteContext.ID_CONTINUOUS_SEC; +import static com.android.internal.widget.remotecompose.core.RemoteContext.ID_TIME_IN_MIN; +import static com.android.internal.widget.remotecompose.core.RemoteContext.ID_TIME_IN_SEC; +import static com.android.internal.widget.remotecompose.core.RemoteContext.ID_WINDOW_HEIGHT; +import static com.android.internal.widget.remotecompose.core.RemoteContext.ID_WINDOW_WIDTH; + import com.android.internal.widget.remotecompose.core.operations.utilities.IntMap; +import java.util.ArrayList; import java.util.HashMap; /** * Represents runtime state for a RemoteCompose document + * State includes things like the value of variables */ public class RemoteComposeState { - + public static final int START_ID = 42; + private static final int MAX_FLOATS = 500; private final IntMap<Object> mIntDataMap = new IntMap<>(); private final IntMap<Boolean> mIntWrittenMap = new IntMap<>(); private final HashMap<Object, Integer> mDataIntMap = new HashMap(); + private final float[] mFloatMap = new float[MAX_FLOATS]; // efficient cache + private final int[] mColorMap = new int[MAX_FLOATS]; // efficient cache + private int mNextId = START_ID; - private static int sNextId = 42; + { + for (int i = 0; i < mFloatMap.length; i++) { + mFloatMap[i] = Float.NaN; + } + } - public Object getFromId(int id) { + /** + * Get Object based on id. The system will cache things like bitmaps + * Paths etc. They can be accessed with this command + * @param id + * @return + */ + public Object getFromId(int id) { return mIntDataMap.get(id); } - public boolean containsId(int id) { + /** + * true if the cache contain this id + * @param id + * @return + */ + public boolean containsId(int id) { return mIntDataMap.get(id) != null; } @@ -69,6 +96,65 @@ public class RemoteComposeState { } /** + * Insert an item in the cache + */ + public void update(int id, Object item) { + mDataIntMap.remove(mIntDataMap.get(id)); + mDataIntMap.put(item, id); + mIntDataMap.put(id, item); + } + + /** + * Insert an item in the cache + */ + public int cacheFloat(float item) { + int id = nextId(); + mFloatMap[id] = item; + return id; + } + + /** + * Insert an item in the cache + */ + public void cacheFloat(int id, float item) { + mFloatMap[id] = item; + } + + /** + * Insert an item in the cache + */ + public void updateFloat(int id, float item) { + mFloatMap[id] = item; + } + + /** + * get float + */ + public float getFloat(int id) { + return mFloatMap[id]; + } + + /** + * Get the float value + * + * @param id + * @return + */ + public int getColor(int id) { + return mColorMap[id]; + } + + /** + * Modify the color at id. + * @param id + * @param color + */ + public void updateColor(int id, int color) { + mColorMap[id] = color; + } + + + /** * Method to determine if a cached value has been written to the documents WireBuffer based on * its id. */ @@ -79,22 +165,90 @@ public class RemoteComposeState { /** * Method to mark that a value, represented by its id, has been written to the WireBuffer */ - public void markWritten(int id) { + public void markWritten(int id) { mIntWrittenMap.put(id, true); } /** - * Clear the record of the values that have been written to the WireBuffer. + * Clear the record of the values that have been written to the WireBuffer. */ void reset() { mIntWrittenMap.clear(); } - public static int nextId() { - return sNextId++; + /** + * Get the next available id + * @return + */ + public int nextId() { + return mNextId++; } - public static void setNextId(int id) { - sNextId = id; + + /** + * Set the next id + * @param id + */ + public void setNextId(int id) { + mNextId = id; + } + + IntMap<ArrayList<VariableSupport>> mVarListeners = new IntMap<>(); + ArrayList<VariableSupport> mAllVarListeners = new ArrayList<>(); + + private void add(int id, VariableSupport variableSupport) { + ArrayList<VariableSupport> v = mVarListeners.get(id); + if (v == null) { + v = new ArrayList<VariableSupport>(); + mVarListeners.put(id, v); + } + v.add(variableSupport); + mAllVarListeners.add(variableSupport); + } + + /** + * Commands that listen to variables add themselves. + * @param id + * @param variableSupport + */ + public void listenToVar(int id, VariableSupport variableSupport) { + add(id, variableSupport); + } + + /** + * List of Commands that need to be updated + * @param context + * @return + */ + public int getOpsToUpdate(RemoteContext context) { + for (VariableSupport vs : mAllVarListeners) { + vs.updateVariables(context); + } + if (mVarListeners.get(ID_CONTINUOUS_SEC) != null) { + return 1; + } + if (mVarListeners.get(ID_TIME_IN_SEC) != null) { + return 1000; + } + if (mVarListeners.get(ID_TIME_IN_MIN) != null) { + return 1000 * 60; + } + return -1; + } + + /** + * Set the width of the overall document on screen. + * @param width + */ + public void setWindowWidth(float width) { + updateFloat(ID_WINDOW_WIDTH, width); + } + + /** + * Set the width of the overall document on screen. + * @param height + */ + public void setWindowHeight(float height) { + updateFloat(ID_WINDOW_HEIGHT, height); } } diff --git a/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java b/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java index d16cbc5a1a16..7e721684be0a 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java +++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 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. @@ -15,7 +15,10 @@ */ package com.android.internal.widget.remotecompose.core; +import com.android.internal.widget.remotecompose.core.operations.FloatExpression; +import com.android.internal.widget.remotecompose.core.operations.ShaderData; import com.android.internal.widget.remotecompose.core.operations.Theme; +import com.android.internal.widget.remotecompose.core.operations.Utils; /** * Specify an abstract context used to playback RemoteCompose documents @@ -27,7 +30,7 @@ import com.android.internal.widget.remotecompose.core.operations.Theme; public abstract class RemoteContext { protected CoreDocument mDocument; public RemoteComposeState mRemoteComposeState; - + long mStart = System.nanoTime(); // todo This should be set at a hi level protected PaintContext mPaintContext = null; ContextMode mMode = ContextMode.UNSET; @@ -37,9 +40,40 @@ public abstract class RemoteContext { public float mWidth = 0f; public float mHeight = 0f; + /** + * Load a path under an id. + * Paths can be use in clip drawPath and drawTweenPath + * @param instanceId + * @param floatPath + */ public abstract void loadPathData(int instanceId, float[] floatPath); /** + * Associate a name with a give id. + * + * @param varName + * @param varId + * @param varType + */ + public abstract void loadVariableName(String varName, int varId, int varType); + + /** + * Save a color under a given id + * @param id + * @param color + */ + public abstract void loadColor(int id, int color); + + /** + * gets the time animation clock as float in seconds + * @return a monotonic time in seconds (arbitrary zero point) + */ + public float getAnimationTime() { + return (System.nanoTime() - mStart) * 1E-9f; + } + + + /** * The context can be used in a few different mode, allowing operations to skip being executed: * - UNSET : all operations will get executed * - DATA : only operations dealing with DATA (eg loading a bitmap) should execute @@ -96,6 +130,8 @@ public abstract class RemoteContext { public void header(int majorVersion, int minorVersion, int patchVersion, int width, int height, long capabilities ) { + mRemoteComposeState.setWindowWidth(width); + mRemoteComposeState.setWindowHeight(height); mDocument.setVersion(majorVersion, minorVersion, patchVersion); mDocument.setWidth(width); mDocument.setHeight(height); @@ -137,9 +173,105 @@ public abstract class RemoteContext { /////////////////////////////////////////////////////////////////////////////////////////////// // Data handling /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Save a bitmap under an imageId + * @param imageId + * @param width + * @param height + * @param bitmap + */ public abstract void loadBitmap(int imageId, int width, int height, byte[] bitmap); + + /** + * Save a string under a given id + * @param id + * @param text + */ public abstract void loadText(int id, String text); + /** + * Get a string given an id + * @param id + * @return + */ + public abstract String getText(int id); + + /** + * Load a float + * @param id + * @param value + */ + public abstract void loadFloat(int id, float value); + + /** + * Load an animated float associated with an id + * Todo: Remove? + * @param id + * @param animatedFloat + */ + public abstract void loadAnimatedFloat(int id, FloatExpression animatedFloat); + + /** + * Save a shader under and ID + * @param id + * @param value + */ + public abstract void loadShader(int id, ShaderData value); + + /** + * Get a float given an id + * @param id + * @return + */ + public abstract float getFloat(int id); + + /** + * Get the color given and ID + * @param id + * @return + */ + public abstract int getColor(int id); + + /** + * called to notify system that a command is interested in a variable + * @param id + * @param variableSupport + */ + public abstract void listensTo(int id, VariableSupport variableSupport); + + /** + * Notify commands with variables have changed + * @return + */ + public abstract int updateOps(); + + /** + * Get a shader given the id + * @param id + * @return + */ + public abstract ShaderData getShader(int id); + + public static final int ID_CONTINUOUS_SEC = 1; + public static final int ID_TIME_IN_SEC = 2; + public static final int ID_TIME_IN_MIN = 3; + public static final int ID_TIME_IN_HR = 4; + public static final int ID_WINDOW_WIDTH = 5; + public static final int ID_WINDOW_HEIGHT = 6; + public static final int ID_COMPONENT_WIDTH = 7; + public static final int ID_COMPONENT_HEIGHT = 8; + public static final int ID_CALENDAR_MONTH = 9; + + public static final float FLOAT_CONTINUOUS_SEC = Utils.asNan(ID_CONTINUOUS_SEC); + public static final float FLOAT_TIME_IN_SEC = Utils.asNan(ID_TIME_IN_SEC); + public static final float FLOAT_TIME_IN_MIN = Utils.asNan(ID_TIME_IN_MIN); + public static final float FLOAT_TIME_IN_HR = Utils.asNan(ID_TIME_IN_HR); + public static final float FLOAT_CALENDAR_MONTH = Utils.asNan(ID_CALENDAR_MONTH); + public static final float FLOAT_WINDOW_WIDTH = Utils.asNan(ID_WINDOW_WIDTH); + public static final float FLOAT_WINDOW_HEIGHT = Utils.asNan(ID_WINDOW_HEIGHT); + public static final float FLOAT_COMPONENT_WIDTH = Utils.asNan(ID_COMPONENT_WIDTH); + public static final float FLOAT_COMPONENT_HEIGHT = Utils.asNan(ID_COMPONENT_HEIGHT); /////////////////////////////////////////////////////////////////////////////////////////////// // Click handling /////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/core/java/com/android/internal/widget/remotecompose/core/TimeVariables.java b/core/java/com/android/internal/widget/remotecompose/core/TimeVariables.java new file mode 100644 index 000000000000..e9708b75de27 --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/TimeVariables.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.widget.remotecompose.core; + +import java.time.LocalDateTime; + +/** + * This generates the standard system variables for time. + */ +public class TimeVariables { + /** + * This class populates all time variables in the system + * @param context + */ + public void updateTime(RemoteContext context) { + LocalDateTime dateTime = LocalDateTime.now(); + // This define the time in the format + // seconds run from Midnight=0 quantized to seconds hour 0..3599 + // minutes run from Midnight=0 quantized to minutes 0..1439 + // hours run from Midnight=0 quantized to Hours 0-23 + // CONTINUOUS_SEC is seconds from midnight looping every hour 0-3600 + // CONTINUOUS_SEC is accurate to milliseconds due to float precession + int month = dateTime.getDayOfMonth(); + int hour = dateTime.getHour(); + int minute = dateTime.getMinute(); + int seconds = dateTime.getSecond(); + int currentMinute = hour * 60 + minute; + int currentSeconds = minute * 60 + seconds; + float sec = currentSeconds + dateTime.getNano() * 1E-9f; + + context.loadFloat(RemoteContext.ID_CONTINUOUS_SEC, sec); + context.loadFloat(RemoteContext.ID_TIME_IN_SEC, currentSeconds); + context.loadFloat(RemoteContext.ID_TIME_IN_MIN, currentMinute); + context.loadFloat(RemoteContext.ID_TIME_IN_HR, hour); + context.loadFloat(RemoteContext.ID_CALENDAR_MONTH, month); + + } +} diff --git a/core/java/com/android/internal/widget/remotecompose/core/VariableSupport.java b/core/java/com/android/internal/widget/remotecompose/core/VariableSupport.java new file mode 100644 index 000000000000..d59b1bc65256 --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/VariableSupport.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.widget.remotecompose.core; + +/** + * Interface for operators that interact with variables + * Threw this they register to listen to particular variables + * and are notified when they change + */ +public interface VariableSupport { + /** + * Call to allow an operator to register interest in variables. + * Typically they call context.listensTo(id, this) + * @param context + */ + void registerListening(RemoteContext context); + + /** + * Called to be notified that the variables you are interested have changed. + * @param context + */ + void updateVariables(RemoteContext context); +} diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/BitmapData.java b/core/java/com/android/internal/widget/remotecompose/core/operations/BitmapData.java index 76b714443990..f1863225b766 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/BitmapData.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/BitmapData.java @@ -51,11 +51,12 @@ public class BitmapData implements Operation { @Override public String toString() { - return "BITMAP DATA $imageId"; + return "BITMAP DATA " + mImageId; } public static class Companion implements CompanionOperation { - private Companion() {} + private Companion() { + } @Override public String name() { diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/ClipPath.java b/core/java/com/android/internal/widget/remotecompose/core/operations/ClipPath.java index 8d4a787148ef..e6d5fe7043fd 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/ClipPath.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/ClipPath.java @@ -24,6 +24,11 @@ import com.android.internal.widget.remotecompose.core.WireBuffer; import java.util.List; +/** + * Defines a path that clips a the subsequent drawing commands + * Use MatrixSave and MatrixRestore commands to remove clip + * TODO allow id 0 to mean null? + */ public class ClipPath extends PaintOperation { public static final Companion COMPANION = new Companion(); int mId; @@ -93,5 +98,4 @@ public class ClipPath extends PaintOperation { public void paint(PaintContext context) { context.clipPath(mId, mRegionOp); } -} - +}
\ No newline at end of file diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/ClipRect.java b/core/java/com/android/internal/widget/remotecompose/core/operations/ClipRect.java index 803618a91737..613ecebf9100 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/ClipRect.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/ClipRect.java @@ -15,88 +15,36 @@ */ package com.android.internal.widget.remotecompose.core.operations; -import com.android.internal.widget.remotecompose.core.CompanionOperation; import com.android.internal.widget.remotecompose.core.Operation; import com.android.internal.widget.remotecompose.core.Operations; import com.android.internal.widget.remotecompose.core.PaintContext; -import com.android.internal.widget.remotecompose.core.PaintOperation; -import com.android.internal.widget.remotecompose.core.WireBuffer; -import java.util.List; - -public class ClipRect extends PaintOperation { - public static final Companion COMPANION = new Companion(); - float mLeft; - float mTop; - float mRight; - float mBottom; +/** + * Support clip with a rectangle + */ +public class ClipRect extends DrawBase4 { + public static final Companion COMPANION = + new Companion(Operations.CLIP_RECT) { + @Override + public Operation construct(float x1, + float y1, + float x2, + float y2) { + return new ClipRect(x1, y1, x2, y2); + } + }; public ClipRect( float left, float top, float right, float bottom) { - mLeft = left; - mTop = top; - mRight = right; - mBottom = bottom; - - } - - @Override - public void write(WireBuffer buffer) { - COMPANION.apply(buffer, mLeft, mTop, mRight, mBottom); - } - - @Override - public String toString() { - return "ClipRect " + mLeft + " " + mTop - + " " + mRight + " " + mBottom + ";"; - } - - public static class Companion implements CompanionOperation { - private Companion() { - } - - @Override - public void read(WireBuffer buffer, List<Operation> operations) { - float sLeft = buffer.readFloat(); - float srcTop = buffer.readFloat(); - float srcRight = buffer.readFloat(); - float srcBottom = buffer.readFloat(); - - ClipRect op = new ClipRect(sLeft, srcTop, srcRight, srcBottom); - operations.add(op); - } - - @Override - public String name() { - return "ClipRect"; - } - - @Override - public int id() { - return Operations.CLIP_RECT; - } - - public void apply(WireBuffer buffer, - float left, - float top, - float right, - float bottom) { - buffer.start(Operations.CLIP_RECT); - buffer.writeFloat(left); - buffer.writeFloat(top); - buffer.writeFloat(right); - buffer.writeFloat(bottom); - } + super(left, top, right, bottom); + mName = "ClipRect"; } @Override public void paint(PaintContext context) { - context.clipRect(mLeft, - mTop, - mRight, - mBottom); + context.clipRect(mX1, mY1, mX2, mY2); } } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/ColorExpression.java b/core/java/com/android/internal/widget/remotecompose/core/operations/ColorExpression.java new file mode 100644 index 000000000000..7d28cea35850 --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/ColorExpression.java @@ -0,0 +1,242 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.widget.remotecompose.core.operations; + +import com.android.internal.widget.remotecompose.core.CompanionOperation; +import com.android.internal.widget.remotecompose.core.Operation; +import com.android.internal.widget.remotecompose.core.Operations; +import com.android.internal.widget.remotecompose.core.RemoteContext; +import com.android.internal.widget.remotecompose.core.VariableSupport; +import com.android.internal.widget.remotecompose.core.WireBuffer; + +import java.util.List; + +/** + * Operation to Colors + * Color modes + * mMode = 0 two colors and a tween + * mMode = 1 color1 is a colorID. + * mMode = 2 color2 is a colorID. + * mMode = 3 color1 & color2 are ids + * mMode = 4 H S V mode + */ +public class ColorExpression implements Operation, VariableSupport { + public int mId; + int mMode; + public int mColor1; + public int mColor2; + public float mTween = 0.0f; + + + public float mHue = 0; // only in Mode 4 + public float mSat = 0; + public float mValue = 0; + public float mOutHue = 0; // only in Mode 4 + public float mOutSat = 0; + public float mOutValue = 0; + public int mAlpha = 0xFF; // only used in hsv mode + + public float mOutTween = 0.0f; + public int mOutColor1; + public int mOutColor2; + public static final Companion COMPANION = new Companion(); + public static final int HSV_MODE = 4; + public ColorExpression(int id, float hue, float sat, float value) { + mMode = HSV_MODE; + mAlpha = 0xFF; + mOutHue = mHue = hue; + mOutSat = mSat = sat; + mOutValue = mValue = value; + mColor1 = Float.floatToRawIntBits(hue); + mColor2 = Float.floatToRawIntBits(sat); + mTween = value; + } + public ColorExpression(int id, int alpha, float hue, float sat, float value) { + mMode = HSV_MODE; + mAlpha = alpha; + mOutHue = mHue = hue; + mOutSat = mSat = sat; + mOutValue = mValue = value; + mColor1 = Float.floatToRawIntBits(hue); + mColor2 = Float.floatToRawIntBits(sat); + mTween = value; + } + + public ColorExpression(int id, int mode, int color1, int color2, float tween) { + this.mId = id; + this.mMode = mode & 0xFF; + this.mAlpha = (mode >> 16) & 0xFF; + if (mMode == HSV_MODE) { + mOutHue = mHue = Float.intBitsToFloat(color1); + mOutSat = mSat = Float.intBitsToFloat(color2); + mOutValue = mValue = tween; + } + this.mColor1 = color1; + this.mColor2 = color2; + this.mTween = tween; + this.mOutTween = tween; + this.mOutColor1 = color1; + this.mOutColor2 = color2; + + } + + @Override + public void updateVariables(RemoteContext context) { + if (mMode == 4) { + if (Float.isNaN(mHue)) { + mOutHue = context.getFloat(Utils.idFromNan(mHue)); + } + if (Float.isNaN(mSat)) { + mOutSat = context.getFloat(Utils.idFromNan(mSat)); + } + if (Float.isNaN(mValue)) { + mOutValue = context.getFloat(Utils.idFromNan(mValue)); + } + } + if (Float.isNaN(mTween)) { + mOutTween = context.getFloat(Utils.idFromNan(mTween)); + } + if ((mMode & 1) == 1) { + mOutColor1 = context.getColor(mColor1); + } + if ((mMode & 2) == 2) { + mOutColor2 = context.getColor(mColor2); + } + } + + + @Override + public void registerListening(RemoteContext context) { + if (mMode == 4) { + if (Float.isNaN(mHue)) { + context.listensTo(Utils.idFromNan(mHue), this); + } + if (Float.isNaN(mSat)) { + context.listensTo(Utils.idFromNan(mSat), this); + } + if (Float.isNaN(mValue)) { + context.listensTo(Utils.idFromNan(mValue), this); + } + return; + } + if (Float.isNaN(mTween)) { + context.listensTo(Utils.idFromNan(mTween), this); + } + if ((mMode & 1) == 1) { + context.listensTo(mColor1, this); + } + if ((mMode & 2) == 2) { + context.listensTo(mColor2, this); + } + } + + @Override + public void apply(RemoteContext context) { + if (mMode == 4) { + context.loadColor(mId, (mAlpha << 24) + | (0xFFFFFF & Utils.hsvToRgb(mOutHue, mOutSat, mOutValue))); + return; + } + if (mOutTween == 0.0) { + context.loadColor(mId, mColor1); + } else { + if ((mMode & 1) == 1) { + mOutColor1 = context.getColor(mColor1); + } + if ((mMode & 2) == 2) { + mOutColor2 = context.getColor(mColor2); + } + + context.loadColor(mId, + Utils.interpolateColor(mOutColor1, mOutColor2, mOutTween)); + } + + } + + @Override + public void write(WireBuffer buffer) { + int mode = mMode | (mAlpha << 16); + COMPANION.apply(buffer, mId, mode, mColor1, mColor2, mTween); + } + + @Override + public String toString() { + if (mMode == 4) { + return "ColorExpression[" + mId + "] = hsv (" + Utils.floatToString(mHue) + + ", " + Utils.floatToString(mSat) + + ", " + Utils.floatToString(mValue) + ")"; + } + + String c1 = (mMode & 1) == 1 ? "[" + mColor1 + "]" : Utils.colorInt(mColor1); + String c2 = (mMode & 2) == 2 ? "[" + mColor2 + "]" : Utils.colorInt(mColor2); + return "ColorExpression[" + mId + "] = tween(" + c1 + + ", " + c2 + ", " + + Utils.floatToString(mTween) + ")"; + } + + public static class Companion implements CompanionOperation { + private Companion() { + } + + @Override + public String name() { + return "ColorExpression"; + } + + @Override + public int id() { + return Operations.COLOR_EXPRESSIONS; + } + + /** + * Call to write a ColorExpression object on the buffer + * @param buffer + * @param id of the ColorExpression object + * @param mode if colors are id or actual values + * @param color1 + * @param color2 + * @param tween + */ + public void apply(WireBuffer buffer, + int id, int mode, + int color1, int color2, float tween) { + buffer.start(Operations.COLOR_EXPRESSIONS); + buffer.writeInt(id); + buffer.writeInt(mode); + buffer.writeInt(color1); + buffer.writeInt(color2); + buffer.writeFloat(tween); + + } + + @Override + public void read(WireBuffer buffer, List<Operation> operations) { + int id = buffer.readInt(); + int mode = buffer.readInt(); + int color1 = buffer.readInt(); + int color2 = buffer.readInt(); + float tween = buffer.readFloat(); + + operations.add(new ColorExpression(id, mode, color1, color2, tween)); + } + } + + @Override + public String deepToString(String indent) { + return indent + toString(); + } + +} diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawArc.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawArc.java index e829975cd39b..c1768647bde6 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawArc.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawArc.java @@ -15,107 +15,36 @@ */ package com.android.internal.widget.remotecompose.core.operations; -import com.android.internal.widget.remotecompose.core.CompanionOperation; import com.android.internal.widget.remotecompose.core.Operation; import com.android.internal.widget.remotecompose.core.Operations; import com.android.internal.widget.remotecompose.core.PaintContext; -import com.android.internal.widget.remotecompose.core.PaintOperation; -import com.android.internal.widget.remotecompose.core.WireBuffer; -import java.util.List; - -public class DrawArc extends PaintOperation { - public static final Companion COMPANION = new Companion(); - float mLeft; - float mTop; - float mRight; - float mBottom; - float mStartAngle; - float mSweepAngle; - - public DrawArc( - float left, - float top, - float right, - float bottom, - float startAngle, - float sweepAngle) { - mLeft = left; - mTop = top; - mRight = right; - mBottom = bottom; - mStartAngle = startAngle; - mSweepAngle = sweepAngle; - } - - @Override - public void write(WireBuffer buffer) { - COMPANION.apply(buffer, mLeft, - mTop, - mRight, - mBottom, - mStartAngle, - mSweepAngle); - } - - @Override - public String toString() { - return "DrawArc " + mLeft + " " + mTop - + " " + mRight + " " + mBottom + " " - + "- " + mStartAngle + " " + mSweepAngle + ";"; - } - - public static class Companion implements CompanionOperation { - private Companion() { - } - - @Override - public void read(WireBuffer buffer, List<Operation> operations) { - float sLeft = buffer.readFloat(); - float srcTop = buffer.readFloat(); - float srcRight = buffer.readFloat(); - float srcBottom = buffer.readFloat(); - float mStartAngle = buffer.readFloat(); - float mSweepAngle = buffer.readFloat(); - DrawArc op = new DrawArc(sLeft, srcTop, srcRight, srcBottom, - mStartAngle, mSweepAngle); - operations.add(op); - } - - @Override - public String name() { - return "DrawArc"; - } - - @Override - public int id() { - return Operations.DRAW_ARC; - } - - public void apply(WireBuffer buffer, - float left, - float top, - float right, - float bottom, - float startAngle, - float sweepAngle) { - buffer.start(Operations.DRAW_ARC); - buffer.writeFloat(left); - buffer.writeFloat(top); - buffer.writeFloat(right); - buffer.writeFloat(bottom); - buffer.writeFloat(startAngle); - buffer.writeFloat(sweepAngle); - } +public class DrawArc extends DrawBase6 { + public static final Companion COMPANION = + new Companion(Operations.DRAW_ARC) { + @Override + public Operation construct(float v1, + float v2, + float v3, + float v4, + float v5, + float v6) { + return new DrawArc(v1, v2, v3, v4, v5, v6); + } + }; + + public DrawArc(float v1, + float v2, + float v3, + float v4, + float v5, + float v6) { + super(v1, v2, v3, v4, v5, v6); + mName = "DrawArc"; } @Override public void paint(PaintContext context) { - context.drawArc(mLeft, - mTop, - mRight, - mBottom, - mStartAngle, - mSweepAngle); + context.drawArc(mV1, mV2, mV3, mV4, mV5, mV6); } } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase2.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase2.java new file mode 100644 index 000000000000..0963c1337b70 --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase2.java @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.widget.remotecompose.core.operations; + +import static com.android.internal.widget.remotecompose.core.operations.Utils.floatToString; + +import com.android.internal.widget.remotecompose.core.CompanionOperation; +import com.android.internal.widget.remotecompose.core.Operation; +import com.android.internal.widget.remotecompose.core.Operations; +import com.android.internal.widget.remotecompose.core.PaintOperation; +import com.android.internal.widget.remotecompose.core.RemoteContext; +import com.android.internal.widget.remotecompose.core.VariableSupport; +import com.android.internal.widget.remotecompose.core.WireBuffer; + +import java.util.List; + +/** + * Base class for commands that take 3 float + */ +public abstract class DrawBase2 extends PaintOperation + implements VariableSupport { + public static final Companion COMPANION = + new Companion(Operations.DRAW_CIRCLE) { + @Override + public Operation construct(float x1, float y1) { + // subclass should return new DrawX(x1, y1); + return null; + } + }; + protected String mName = "DrawRectBase"; + float mV1; + float mV2; + float mValue1; + float mValue2; + + public DrawBase2(float v1, float v2) { + mValue1 = v1; + mValue2 = v2; + mV1 = v1; + mV2 = v2; + } + + @Override + public void updateVariables(RemoteContext context) { + mV1 = (Float.isNaN(mValue1)) + ? context.getFloat(Utils.idFromNan(mValue1)) : mValue1; + mV2 = (Float.isNaN(mValue2)) + ? context.getFloat(Utils.idFromNan(mValue2)) : mValue2; + } + + @Override + public void registerListening(RemoteContext context) { + if (Float.isNaN(mValue1)) { + context.listensTo(Utils.idFromNan(mValue1), this); + } + if (Float.isNaN(mValue2)) { + context.listensTo(Utils.idFromNan(mValue2), this); + } + } + + @Override + public void write(WireBuffer buffer) { + COMPANION.apply(buffer, mV1, mV2); + } + + @Override + public String toString() { + return mName + " " + floatToString(mV1) + " " + floatToString(mV2); + } + + public static class Companion implements CompanionOperation { + public final int OP_CODE; + + protected Companion(int code) { + OP_CODE = code; + } + + @Override + public void read(WireBuffer buffer, List<Operation> operations) { + float v1 = buffer.readFloat(); + float v2 = buffer.readFloat(); + + Operation op = construct(v1, v2); + operations.add(op); + } + + /** + * Override to construct a 2 float value operation + * @param x1 + * @param y1 + * @return + */ + public Operation construct(float x1, float y1) { + return null; + } + + @Override + public String name() { + return "DrawRect"; + } + + @Override + public int id() { + return OP_CODE; + } + + /** + * Writes out the operation to the buffer + * @param buffer + * @param x1 + * @param y1 + */ + public void apply(WireBuffer buffer, + float x1, + float y1) { + buffer.start(OP_CODE); + buffer.writeFloat(x1); + buffer.writeFloat(y1); + } + } +} diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase3.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase3.java new file mode 100644 index 000000000000..56b2f1f7bb86 --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase3.java @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.widget.remotecompose.core.operations; + +import static com.android.internal.widget.remotecompose.core.operations.Utils.floatToString; + +import com.android.internal.widget.remotecompose.core.CompanionOperation; +import com.android.internal.widget.remotecompose.core.Operation; +import com.android.internal.widget.remotecompose.core.Operations; +import com.android.internal.widget.remotecompose.core.PaintOperation; +import com.android.internal.widget.remotecompose.core.RemoteContext; +import com.android.internal.widget.remotecompose.core.VariableSupport; +import com.android.internal.widget.remotecompose.core.WireBuffer; + +import java.util.List; + +/** + * Base class for commands that take 3 float + */ +public abstract class DrawBase3 extends PaintOperation + implements VariableSupport { + public static final Companion COMPANION = + new Companion(Operations.DRAW_CIRCLE) { + @Override + public Operation construct(float x1, float y1, float x2) { + // subclass should return new DrawX(x1, y1, x2, y2); + return null; + } + }; + protected String mName = "DrawRectBase"; + float mV1; + float mV2; + float mV3; + float mValue1; + float mValue2; + float mValue3; + + public DrawBase3( + float v1, + float v2, + float v3) { + mValue1 = v1; + mValue2 = v2; + mValue3 = v3; + + mV1 = v1; + mV2 = v2; + mV3 = v3; + } + + @Override + public void updateVariables(RemoteContext context) { + mV1 = (Float.isNaN(mValue1)) + ? context.getFloat(Utils.idFromNan(mValue1)) : mValue1; + mV2 = (Float.isNaN(mValue2)) + ? context.getFloat(Utils.idFromNan(mValue2)) : mValue2; + mV3 = (Float.isNaN(mValue3)) + ? context.getFloat(Utils.idFromNan(mValue3)) : mValue3; + } + + @Override + public void registerListening(RemoteContext context) { + if (Float.isNaN(mValue1)) { + context.listensTo(Utils.idFromNan(mValue1), this); + } + if (Float.isNaN(mValue2)) { + context.listensTo(Utils.idFromNan(mValue2), this); + } + if (Float.isNaN(mValue3)) { + context.listensTo(Utils.idFromNan(mValue3), this); + } + } + + @Override + public void write(WireBuffer buffer) { + COMPANION.apply(buffer, mV1, mV2, mV3); + } + + @Override + public String toString() { + return mName + " " + floatToString(mV1) + " " + floatToString(mV2) + + " " + floatToString(mV3); + } + + public static class Companion implements CompanionOperation { + public final int OP_CODE; + + protected Companion(int code) { + OP_CODE = code; + } + + @Override + public void read(WireBuffer buffer, List<Operation> operations) { + float v1 = buffer.readFloat(); + float v2 = buffer.readFloat(); + float v3 = buffer.readFloat(); + + Operation op = construct(v1, v2, v3); + operations.add(op); + } + + /** + * Construct and Operation from the 3 variables. + * This must be overridden by subclasses + * @param x1 + * @param y1 + * @param x2 + * @return + */ + public Operation construct(float x1, + float y1, + float x2) { + return null; + } + + @Override + public String name() { + return "DrawRect"; + } + + @Override + public int id() { + return OP_CODE; + } + + /** + * Writes out the operation to the buffer + * @param buffer + * @param x1 + * @param y1 + * @param x2 + */ + public void apply(WireBuffer buffer, + float x1, + float y1, + float x2) { + buffer.start(OP_CODE); + buffer.writeFloat(x1); + buffer.writeFloat(y1); + buffer.writeFloat(x2); + + } + } +} diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase4.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase4.java new file mode 100644 index 000000000000..ec35a160079c --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase4.java @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.widget.remotecompose.core.operations; + +import static com.android.internal.widget.remotecompose.core.operations.Utils.floatToString; + +import com.android.internal.widget.remotecompose.core.CompanionOperation; +import com.android.internal.widget.remotecompose.core.Operation; +import com.android.internal.widget.remotecompose.core.Operations; +import com.android.internal.widget.remotecompose.core.PaintOperation; +import com.android.internal.widget.remotecompose.core.RemoteContext; +import com.android.internal.widget.remotecompose.core.VariableSupport; +import com.android.internal.widget.remotecompose.core.WireBuffer; + +import java.util.List; + +/** + * Base class for draw commands that take 4 floats + */ +public abstract class DrawBase4 extends PaintOperation + implements VariableSupport { + public static final Companion COMPANION = + new Companion(Operations.DRAW_RECT) { + @Override + public Operation construct(float x1, float y1, float x2, float y2) { + // return new DrawRectBase(x1, y1, x2, y2); + return null; + } + }; + protected String mName = "DrawRectBase"; + float mX1; + float mY1; + float mX2; + float mY2; + float mX1Value; + float mY1Value; + float mX2Value; + float mY2Value; + + public DrawBase4( + float x1, + float y1, + float x2, + float y2) { + mX1Value = x1; + mY1Value = y1; + mX2Value = x2; + mY2Value = y2; + + mX1 = x1; + mY1 = y1; + mX2 = x2; + mY2 = y2; + } + + @Override + public void updateVariables(RemoteContext context) { + mX1 = (Float.isNaN(mX1Value)) + ? context.getFloat(Utils.idFromNan(mX1Value)) : mX1Value; + mY1 = (Float.isNaN(mY1Value)) + ? context.getFloat(Utils.idFromNan(mY1Value)) : mY1Value; + mX2 = (Float.isNaN(mX2Value)) + ? context.getFloat(Utils.idFromNan(mX2Value)) : mX2Value; + mY2 = (Float.isNaN(mY2Value)) + ? context.getFloat(Utils.idFromNan(mY2Value)) : mY2Value; + } + + @Override + public void registerListening(RemoteContext context) { + if (Float.isNaN(mX1Value)) { + context.listensTo(Utils.idFromNan(mX1Value), this); + } + if (Float.isNaN(mY1Value)) { + context.listensTo(Utils.idFromNan(mY1Value), this); + } + if (Float.isNaN(mX2Value)) { + context.listensTo(Utils.idFromNan(mX2Value), this); + } + if (Float.isNaN(mY2Value)) { + context.listensTo(Utils.idFromNan(mY2Value), this); + } + } + + @Override + public void write(WireBuffer buffer) { + COMPANION.apply(buffer, mX1, mY1, mX2, mY2); + } + + @Override + public String toString() { + return mName + " " + floatToString(mX1Value, mX1) + " " + floatToString(mY1Value, mY1) + + " " + floatToString(mX2Value, mX2) + " " + floatToString(mY2Value, mY2); + } + + public static class Companion implements CompanionOperation { + public final int OP_CODE; + + protected Companion(int code) { + OP_CODE = code; + } + + @Override + public void read(WireBuffer buffer, List<Operation> operations) { + float sLeft = buffer.readFloat(); + float srcTop = buffer.readFloat(); + float srcRight = buffer.readFloat(); + float srcBottom = buffer.readFloat(); + + Operation op = construct(sLeft, srcTop, srcRight, srcBottom); + operations.add(op); + } + + /** + * Construct and Operation from the 3 variables. + * @param x1 + * @param y1 + * @param x2 + * @param y2 + * @return + */ + public Operation construct(float x1, + float y1, + float x2, + float y2) { + return null; + } + + @Override + public String name() { + return "DrawRect"; + } + + @Override + public int id() { + return OP_CODE; + } + + /** + * Writes out the operation to the buffer + * @param buffer + * @param x1 + * @param y1 + * @param x2 + * @param y2 + */ + public void apply(WireBuffer buffer, + float x1, + float y1, + float x2, + float y2) { + buffer.start(OP_CODE); + buffer.writeFloat(x1); + buffer.writeFloat(y1); + buffer.writeFloat(x2); + buffer.writeFloat(y2); + } + } +} diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase6.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase6.java new file mode 100644 index 000000000000..2f4335e7f412 --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase6.java @@ -0,0 +1,202 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.widget.remotecompose.core.operations; + +import static com.android.internal.widget.remotecompose.core.operations.Utils.floatToString; + +import com.android.internal.widget.remotecompose.core.CompanionOperation; +import com.android.internal.widget.remotecompose.core.Operation; +import com.android.internal.widget.remotecompose.core.Operations; +import com.android.internal.widget.remotecompose.core.PaintOperation; +import com.android.internal.widget.remotecompose.core.RemoteContext; +import com.android.internal.widget.remotecompose.core.VariableSupport; +import com.android.internal.widget.remotecompose.core.WireBuffer; + +import java.util.List; + +/** + * Base class for draw commands the take 6 floats + */ +public abstract class DrawBase6 extends PaintOperation + implements VariableSupport { + public static final Companion COMPANION = + new Companion(Operations.DRAW_RECT) { + public Operation construct(float x1, float y1, float x2, float y2) { + // return new DrawRectBase(x1, y1, x2, y2); + return null; + } + }; + protected String mName = "DrawRectBase"; + float mV1; + float mV2; + float mV3; + float mV4; + float mV5; + float mV6; + float mValue1; + float mValue2; + float mValue3; + float mValue4; + float mValue5; + float mValue6; + + public DrawBase6( + float v1, + float v2, + float v3, + float v4, + float v5, + float v6) { + mValue1 = v1; + mValue2 = v2; + mValue3 = v3; + mValue4 = v4; + mValue5 = v5; + mValue6 = v6; + + mV1 = v1; + mV2 = v2; + mV3 = v3; + mV4 = v4; + mV5 = v5; + mV6 = v6; + } + + @Override + public void updateVariables(RemoteContext context) { + mV1 = (Float.isNaN(mValue1)) + ? context.getFloat(Utils.idFromNan(mValue1)) : mValue1; + mV2 = (Float.isNaN(mValue2)) + ? context.getFloat(Utils.idFromNan(mValue2)) : mValue2; + mV3 = (Float.isNaN(mValue3)) + ? context.getFloat(Utils.idFromNan(mValue3)) : mValue3; + mV4 = (Float.isNaN(mValue4)) + ? context.getFloat(Utils.idFromNan(mValue4)) : mValue4; + mV5 = (Float.isNaN(mValue5)) + ? context.getFloat(Utils.idFromNan(mValue5)) : mValue5; + mV6 = (Float.isNaN(mValue6)) + ? context.getFloat(Utils.idFromNan(mValue6)) : mValue6; + } + + @Override + public void registerListening(RemoteContext context) { + if (Float.isNaN(mValue1)) { + context.listensTo(Utils.idFromNan(mValue1), this); + } + if (Float.isNaN(mValue2)) { + context.listensTo(Utils.idFromNan(mValue2), this); + } + if (Float.isNaN(mValue3)) { + context.listensTo(Utils.idFromNan(mValue3), this); + } + if (Float.isNaN(mValue4)) { + context.listensTo(Utils.idFromNan(mValue4), this); + } + if (Float.isNaN(mValue5)) { + context.listensTo(Utils.idFromNan(mValue5), this); + } + if (Float.isNaN(mValue6)) { + context.listensTo(Utils.idFromNan(mValue6), this); + } + } + + @Override + public void write(WireBuffer buffer) { + COMPANION.apply(buffer, mV1, mV2, mV3, mV4, mV5, mV6); + } + + @Override + public String toString() { + return mName + " " + floatToString(mV1) + " " + floatToString(mV2) + + " " + floatToString(mV3) + " " + floatToString(mV4); + } + + public static class Companion implements CompanionOperation { + public final int OP_CODE; + + protected Companion(int code) { + OP_CODE = code; + } + + @Override + public void read(WireBuffer buffer, List<Operation> operations) { + float sv1 = buffer.readFloat(); + float sv2 = buffer.readFloat(); + float sv3 = buffer.readFloat(); + float sv4 = buffer.readFloat(); + float sv5 = buffer.readFloat(); + float sv6 = buffer.readFloat(); + + Operation op = construct(sv1, sv2, sv3, sv4, sv5, sv6); + operations.add(op); + } + + /** + * writes out a the operation to the buffer. + * @param v1 + * @param v2 + * @param v3 + * @param v4 + * @param v5 + * @param v6 + * @return + */ + public Operation construct(float v1, + float v2, + float v3, + float v4, + float v5, + float v6) { + return null; + } + + @Override + public String name() { + return "DrawRect"; + } + + @Override + public int id() { + return OP_CODE; + } + + /** + * Writes out the operation to the buffer + * @param buffer + * @param v1 + * @param v2 + * @param v3 + * @param v4 + * @param v5 + * @param v6 + */ + public void apply(WireBuffer buffer, + float v1, + float v2, + float v3, + float v4, + float v5, + float v6) { + buffer.start(OP_CODE); + buffer.writeFloat(v1); + buffer.writeFloat(v2); + buffer.writeFloat(v3); + buffer.writeFloat(v4); + buffer.writeFloat(v5); + buffer.writeFloat(v6); + } + } +} diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmap.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmap.java index 2e971f533ed2..ca40d12fd3f9 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmap.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmap.java @@ -20,16 +20,22 @@ import com.android.internal.widget.remotecompose.core.Operation; import com.android.internal.widget.remotecompose.core.Operations; import com.android.internal.widget.remotecompose.core.PaintContext; import com.android.internal.widget.remotecompose.core.PaintOperation; +import com.android.internal.widget.remotecompose.core.RemoteContext; +import com.android.internal.widget.remotecompose.core.VariableSupport; import com.android.internal.widget.remotecompose.core.WireBuffer; import java.util.List; -public class DrawBitmap extends PaintOperation { +public class DrawBitmap extends PaintOperation implements VariableSupport { public static final Companion COMPANION = new Companion(); float mLeft; float mTop; float mRight; float mBottom; + float mOutputLeft; + float mOutputTop; + float mOutputRight; + float mOutputBottom; int mId; int mDescriptionId = 0; @@ -49,6 +55,34 @@ public class DrawBitmap extends PaintOperation { } @Override + public void updateVariables(RemoteContext context) { + mOutputLeft = (Float.isNaN(mLeft)) + ? context.getFloat(Utils.idFromNan(mLeft)) : mLeft; + mOutputTop = (Float.isNaN(mTop)) + ? context.getFloat(Utils.idFromNan(mTop)) : mTop; + mOutputRight = (Float.isNaN(mRight)) + ? context.getFloat(Utils.idFromNan(mRight)) : mRight; + mOutputBottom = (Float.isNaN(mBottom)) + ? context.getFloat(Utils.idFromNan(mBottom)) : mBottom; + } + + @Override + public void registerListening(RemoteContext context) { + if (Float.isNaN(mLeft)) { + context.listensTo(Utils.idFromNan(mLeft), this); + } + if (Float.isNaN(mTop)) { + context.listensTo(Utils.idFromNan(mTop), this); + } + if (Float.isNaN(mRight)) { + context.listensTo(Utils.idFromNan(mRight), this); + } + if (Float.isNaN(mBottom)) { + context.listensTo(Utils.idFromNan(mBottom), this); + } + } + + @Override public void write(WireBuffer buffer) { COMPANION.apply(buffer, mId, mLeft, mTop, mRight, mBottom, mDescriptionId); } @@ -105,9 +139,9 @@ public class DrawBitmap extends PaintOperation { @Override public void paint(PaintContext context) { - context.drawBitmap(mId, mLeft, - mTop, - mRight, - mBottom); + context.drawBitmap(mId, mOutputLeft, + mOutputTop, + mOutputRight, + mOutputBottom); } } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawCircle.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawCircle.java index 9ce754da1b1b..3a22e4f72720 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawCircle.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawCircle.java @@ -1,89 +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.internal.widget.remotecompose.core.operations; -import com.android.internal.widget.remotecompose.core.CompanionOperation; import com.android.internal.widget.remotecompose.core.Operation; import com.android.internal.widget.remotecompose.core.Operations; import com.android.internal.widget.remotecompose.core.PaintContext; -import com.android.internal.widget.remotecompose.core.PaintOperation; -import com.android.internal.widget.remotecompose.core.WireBuffer; -import java.util.List; - -public class DrawCircle extends PaintOperation { - public static final Companion COMPANION = new Companion(); - float mCenterX; - float mCenterY; - float mRadius; - - public DrawCircle(float centerX, float centerY, float radius) { - mCenterX = centerX; - mCenterY = centerY; - mRadius = radius; - } - - @Override - public void write(WireBuffer buffer) { - COMPANION.apply(buffer, mCenterX, - mCenterY, - mRadius); - } - - @Override - public String toString() { - return ""; - } - - public static class Companion implements CompanionOperation { - private Companion() { - } - - @Override - public void read(WireBuffer buffer, List<Operation> operations) { - float centerX = buffer.readFloat(); - float centerY = buffer.readFloat(); - float radius = buffer.readFloat(); - - DrawCircle op = new DrawCircle(centerX, centerY, radius); - operations.add(op); - } - - @Override - public String name() { - return ""; - } - - @Override - public int id() { - return 0; - } - - public void apply(WireBuffer buffer, float centerX, float centerY, float radius) { - buffer.start(Operations.DRAW_CIRCLE); - buffer.writeFloat(centerX); - buffer.writeFloat(centerY); - buffer.writeFloat(radius); - } +public class DrawCircle extends DrawBase3 { + public static final Companion COMPANION = + new Companion(Operations.DRAW_CIRCLE) { + @Override + public Operation construct(float x1, + float y1, + float x2 + ) { + return new DrawCircle(x1, y1, x2); + } + }; + + public DrawCircle( + float left, + float top, + float right) { + super(left, top, right); + mName = "DrawCircle"; } @Override public void paint(PaintContext context) { - context.drawCircle(mCenterX, - mCenterY, - mRadius); + context.drawCircle(mV1, mV2, mV3); } } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawLine.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawLine.java index c7a8315a2274..c70c6eaa449d 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawLine.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawLine.java @@ -15,83 +15,28 @@ */ package com.android.internal.widget.remotecompose.core.operations; -import com.android.internal.widget.remotecompose.core.CompanionOperation; import com.android.internal.widget.remotecompose.core.Operation; import com.android.internal.widget.remotecompose.core.Operations; import com.android.internal.widget.remotecompose.core.PaintContext; -import com.android.internal.widget.remotecompose.core.PaintOperation; -import com.android.internal.widget.remotecompose.core.WireBuffer; - -import java.util.List; - -public class DrawLine extends PaintOperation { - public static final Companion COMPANION = new Companion(); - float mX1; - float mY1; - float mX2; - float mY2; - - public DrawLine( - float x1, - float y1, - float x2, - float y2) { - mX1 = x1; - mY1 = y1; - mX2 = x2; - mY2 = y2; - } - - @Override - public void write(WireBuffer buffer) { - COMPANION.apply(buffer, mX1, - mY1, - mX2, - mY2); - } - - @Override - public String toString() { - return "DrawArc " + mX1 + " " + mY1 - + " " + mX2 + " " + mY2 + ";"; - } - - public static class Companion implements CompanionOperation { - private Companion() { - } - - @Override - public void read(WireBuffer buffer, List<Operation> operations) { - float x1 = buffer.readFloat(); - float y1 = buffer.readFloat(); - float x2 = buffer.readFloat(); - float y2 = buffer.readFloat(); - - DrawLine op = new DrawLine(x1, y1, x2, y2); - operations.add(op); - } - - @Override - public String name() { - return "DrawLine"; - } +public class DrawLine extends DrawBase4 { + public static final Companion COMPANION = new Companion(Operations.DRAW_LINE) { @Override - public int id() { - return Operations.DRAW_LINE; + public Operation construct(float x1, + float y1, + float x2, + float y2) { + return new DrawLine(x1, y1, x2, y2); } + }; - public void apply(WireBuffer buffer, - float x1, - float y1, - float x2, - float y2) { - buffer.start(Operations.DRAW_LINE); - buffer.writeFloat(x1); - buffer.writeFloat(y1); - buffer.writeFloat(x2); - buffer.writeFloat(y2); - } + public DrawLine( + float left, + float top, + float right, + float bottom) { + super(left, top, right, bottom); + mName = "DrawLine"; } @Override diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawOval.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawOval.java index 714375335cb2..ba1799422e80 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawOval.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawOval.java @@ -15,88 +15,33 @@ */ package com.android.internal.widget.remotecompose.core.operations; -import com.android.internal.widget.remotecompose.core.CompanionOperation; import com.android.internal.widget.remotecompose.core.Operation; import com.android.internal.widget.remotecompose.core.Operations; import com.android.internal.widget.remotecompose.core.PaintContext; -import com.android.internal.widget.remotecompose.core.PaintOperation; -import com.android.internal.widget.remotecompose.core.WireBuffer; - -import java.util.List; - -public class DrawOval extends PaintOperation { - public static final Companion COMPANION = new Companion(); - float mLeft; - float mTop; - float mRight; - float mBottom; +public class DrawOval extends DrawBase4 { + public static final Companion COMPANION = + new Companion(Operations.DRAW_OVAL) { + @Override + public Operation construct(float x1, + float y1, + float x2, + float y2) { + return new DrawOval(x1, y1, x2, y2); + } + }; public DrawOval( float left, float top, float right, float bottom) { - mLeft = left; - mTop = top; - mRight = right; - mBottom = bottom; - } - - @Override - public void write(WireBuffer buffer) { - COMPANION.apply(buffer, mLeft, mTop, mRight, mBottom); - } - - @Override - public String toString() { - return "DrawOval " + mLeft + " " + mTop - + " " + mRight + " " + mBottom + ";"; - } - - public static class Companion implements CompanionOperation { - private Companion() { - } - - @Override - public void read(WireBuffer buffer, List<Operation> operations) { - float sLeft = buffer.readFloat(); - float srcTop = buffer.readFloat(); - float srcRight = buffer.readFloat(); - float srcBottom = buffer.readFloat(); - - DrawOval op = new DrawOval(sLeft, srcTop, srcRight, srcBottom); - operations.add(op); - } - - @Override - public String name() { - return "DrawOval"; - } - - @Override - public int id() { - return Operations.DRAW_OVAL; - } - - public void apply(WireBuffer buffer, - float left, - float top, - float right, - float bottom) { - buffer.start(Operations.DRAW_OVAL); - buffer.writeFloat(left); - buffer.writeFloat(top); - buffer.writeFloat(right); - buffer.writeFloat(bottom); - } + super(left, top, right, bottom); + mName = "DrawOval"; } @Override public void paint(PaintContext context) { - context.drawOval(mLeft, - mTop, - mRight, - mBottom); + context.drawOval(mX1, mY1, mX2, mY2); } } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawPath.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawPath.java index 7b8a9e95d9cb..6dbc5a628c98 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawPath.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawPath.java @@ -41,7 +41,7 @@ public class DrawPath extends PaintOperation { @Override public String toString() { - return "DrawPath " + ";"; + return "DrawPath " + "[" + mId + "]" + ", " + mStart + ", " + mEnd; } public static class Companion implements CompanionOperation { diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRect.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRect.java index 4775241faa6f..633aed4a4dbe 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRect.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRect.java @@ -15,88 +15,37 @@ */ package com.android.internal.widget.remotecompose.core.operations; -import com.android.internal.widget.remotecompose.core.CompanionOperation; import com.android.internal.widget.remotecompose.core.Operation; import com.android.internal.widget.remotecompose.core.Operations; import com.android.internal.widget.remotecompose.core.PaintContext; -import com.android.internal.widget.remotecompose.core.PaintOperation; -import com.android.internal.widget.remotecompose.core.WireBuffer; -import java.util.List; - -public class DrawRect extends PaintOperation { - public static final Companion COMPANION = new Companion(); - float mLeft; - float mTop; - float mRight; - float mBottom; +/** + * Draw a Rectangle + */ +public class DrawRect extends DrawBase4 { + public static final Companion COMPANION = + new Companion(Operations.DRAW_RECT) { + @Override + public Operation construct(float x1, + float y1, + float x2, + float y2) { + return new DrawRect(x1, y1, x2, y2); + } + }; public DrawRect( float left, float top, float right, float bottom) { - mLeft = left; - mTop = top; - mRight = right; - mBottom = bottom; - } - - @Override - public void write(WireBuffer buffer) { - COMPANION.apply(buffer, mLeft, mTop, mRight, mBottom); - } - - @Override - public String toString() { - return "DrawRect " + mLeft + " " + mTop - + " " + mRight + " " + mBottom + ";"; - } - - public static class Companion implements CompanionOperation { - private Companion() { - } - - @Override - public void read(WireBuffer buffer, List<Operation> operations) { - float sLeft = buffer.readFloat(); - float srcTop = buffer.readFloat(); - float srcRight = buffer.readFloat(); - float srcBottom = buffer.readFloat(); - - DrawRect op = new DrawRect(sLeft, srcTop, srcRight, srcBottom); - operations.add(op); - } - - @Override - public String name() { - return "DrawRect"; - } - - @Override - public int id() { - return Operations.DRAW_RECT; - } - - public void apply(WireBuffer buffer, - float left, - float top, - float right, - float bottom) { - buffer.start(Operations.DRAW_RECT); - buffer.writeFloat(left); - buffer.writeFloat(top); - buffer.writeFloat(right); - buffer.writeFloat(bottom); - } + super(left, top, right, bottom); + mName = "DrawRect"; } @Override public void paint(PaintContext context) { - context.drawRect(mLeft, - mTop, - mRight, - mBottom); + context.drawRect(mX1, mY1, mX2, mY2); } } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRoundRect.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRoundRect.java index 8da16e768b7f..b9d0a6728b95 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRoundRect.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRoundRect.java @@ -15,104 +15,40 @@ */ package com.android.internal.widget.remotecompose.core.operations; -import com.android.internal.widget.remotecompose.core.CompanionOperation; import com.android.internal.widget.remotecompose.core.Operation; import com.android.internal.widget.remotecompose.core.Operations; import com.android.internal.widget.remotecompose.core.PaintContext; -import com.android.internal.widget.remotecompose.core.PaintOperation; -import com.android.internal.widget.remotecompose.core.WireBuffer; -import java.util.List; - -public class DrawRoundRect extends PaintOperation { - public static final Companion COMPANION = new Companion(); - float mLeft; - float mTop; - float mRight; - float mBottom; - float mRadiusX; - float mRadiusY; - - public DrawRoundRect( - float left, - float top, - float right, - float bottom, - float radiusX, - float radiusY) { - mLeft = left; - mTop = top; - mRight = right; - mBottom = bottom; - mRadiusX = radiusX; - mRadiusY = radiusY; - } - - @Override - public void write(WireBuffer buffer) { - COMPANION.apply(buffer, mLeft, mTop, mRight, mBottom, mRadiusX, mRadiusY); - } - - @Override - public String toString() { - return "DrawRoundRect " + mLeft + " " + mTop - + " " + mRight + " " + mBottom - + " (" + mRadiusX + " " + mRadiusY + ");"; - } - - public static class Companion implements CompanionOperation { - private Companion() { - } - - @Override - public void read(WireBuffer buffer, List<Operation> operations) { - float sLeft = buffer.readFloat(); - float srcTop = buffer.readFloat(); - float srcRight = buffer.readFloat(); - float srcBottom = buffer.readFloat(); - float srcRadiusX = buffer.readFloat(); - float srcRadiusY = buffer.readFloat(); - - DrawRoundRect op = new DrawRoundRect(sLeft, srcTop, srcRight, - srcBottom, srcRadiusX, srcRadiusY); - operations.add(op); - } - - @Override - public String name() { - return "DrawOval"; - } - - @Override - public int id() { - return Operations.DRAW_ROUND_RECT; - } - - public void apply(WireBuffer buffer, - float left, - float top, - float right, - float bottom, - float radiusX, - float radiusY) { - buffer.start(Operations.DRAW_ROUND_RECT); - buffer.writeFloat(left); - buffer.writeFloat(top); - buffer.writeFloat(right); - buffer.writeFloat(bottom); - buffer.writeFloat(radiusX); - buffer.writeFloat(radiusY); - } +/** + * Draw a rounded rectangle + */ +public class DrawRoundRect extends DrawBase6 { + public static final Companion COMPANION = + new Companion(Operations.DRAW_ROUND_RECT) { + @Override + public Operation construct(float v1, + float v2, + float v3, + float v4, + float v5, + float v6) { + return new DrawRoundRect(v1, v2, v3, v4, v5, v6); + } + }; + + public DrawRoundRect(float v1, + float v2, + float v3, + float v4, + float v5, + float v6) { + super(v1, v2, v3, v4, v5, v6); + mName = "ClipRect"; } @Override public void paint(PaintContext context) { - context.drawRoundRect(mLeft, - mTop, - mRight, - mBottom, - mRadiusX, - mRadiusY + context.drawRoundRect(mV1, mV2, mV3, mV4, mV5, mV6 ); } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawText.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawText.java new file mode 100644 index 000000000000..f8f8afdf68cd --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawText.java @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.widget.remotecompose.core.operations; + +import com.android.internal.widget.remotecompose.core.CompanionOperation; +import com.android.internal.widget.remotecompose.core.Operation; +import com.android.internal.widget.remotecompose.core.Operations; +import com.android.internal.widget.remotecompose.core.PaintContext; +import com.android.internal.widget.remotecompose.core.PaintOperation; +import com.android.internal.widget.remotecompose.core.WireBuffer; + +import java.util.List; + +/** + * Draw Text + */ +public class DrawText extends PaintOperation { + public static final Companion COMPANION = new Companion(); + int mTextID; + int mStart = 0; + int mEnd = 0; + int mContextStart = 0; + int mContextEnd = 0; + float mX = 0f; + float mY = 0f; + boolean mRtl = false; + + public DrawText(int textID, + int start, + int end, + int contextStart, + int contextEnd, + float x, + float y, + boolean rtl) { + mTextID = textID; + mStart = start; + mEnd = end; + mContextStart = contextStart; + mContextEnd = contextEnd; + mX = x; + mY = y; + mRtl = rtl; + } + + @Override + public void write(WireBuffer buffer) { + COMPANION.apply(buffer, mTextID, mStart, mEnd, mContextStart, mContextEnd, mX, mY, mRtl); + + } + + @Override + public String toString() { + return "DrawTextRun [" + mTextID + "] " + mStart + ", " + mEnd + ", " + mX + ", " + mY; + } + + public static class Companion implements CompanionOperation { + private Companion() { + } + + @Override + public void read(WireBuffer buffer, List<Operation> operations) { + int text = buffer.readInt(); + int start = buffer.readInt(); + int end = buffer.readInt(); + int contextStart = buffer.readInt(); + int contextEnd = buffer.readInt(); + float x = buffer.readFloat(); + float y = buffer.readFloat(); + boolean rtl = buffer.readBoolean(); + DrawText op = new DrawText(text, start, end, contextStart, contextEnd, x, y, rtl); + + operations.add(op); + } + + @Override + public String name() { + return ""; + } + + @Override + public int id() { + return 0; + } + + /** + * Writes out the operation to the buffer + * @param buffer + * @param textID + * @param start + * @param end + * @param contextStart + * @param contextEnd + * @param x + * @param y + * @param rtl + */ + public void apply(WireBuffer buffer, + int textID, + int start, + int end, + int contextStart, + int contextEnd, + float x, + float y, + boolean rtl) { + buffer.start(Operations.DRAW_TEXT_RUN); + buffer.writeInt(textID); + buffer.writeInt(start); + buffer.writeInt(end); + buffer.writeInt(contextStart); + buffer.writeInt(contextEnd); + buffer.writeFloat(x); + buffer.writeFloat(y); + buffer.writeBoolean(rtl); + } + } + + @Override + public void paint(PaintContext context) { + context.drawTextRun(mTextID, mStart, mEnd, mContextStart, mContextEnd, mX, mY, mRtl); + } +} diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextAnchored.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextAnchored.java new file mode 100644 index 000000000000..4f0641f34d84 --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextAnchored.java @@ -0,0 +1,204 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.widget.remotecompose.core.operations; + +import com.android.internal.widget.remotecompose.core.CompanionOperation; +import com.android.internal.widget.remotecompose.core.Operation; +import com.android.internal.widget.remotecompose.core.Operations; +import com.android.internal.widget.remotecompose.core.PaintContext; +import com.android.internal.widget.remotecompose.core.PaintOperation; +import com.android.internal.widget.remotecompose.core.RemoteContext; +import com.android.internal.widget.remotecompose.core.VariableSupport; +import com.android.internal.widget.remotecompose.core.WireBuffer; + +import java.util.List; + +/** + * Draw Text in Anchored to a point + */ +public class DrawTextAnchored extends PaintOperation implements VariableSupport { + public static final Companion COMPANION = new Companion(); + int mTextID; + float mX; + float mY; + float mPanX; + float mPanY; + int mFlags; + float mOutX; + float mOutY; + float mOutPanX; + float mOutPanY; + + public static final int ANCHOR_TEXT_RTL = 1; + public static final int ANCHOR_MONOSPACE_MEASURE = 2; + + public DrawTextAnchored(int textID, + float x, + float y, + float panX, + float panY, + int flags) { + mTextID = textID; + mX = x; + mY = y; + mOutX = mX; + mOutY = mY; + mFlags = flags; + mOutPanX = mPanX = panX; + mOutPanY = mPanY = panY; + } + + @Override + public void updateVariables(RemoteContext context) { + mOutX = (Float.isNaN(mX)) + ? context.getFloat(Utils.idFromNan(mX)) : mX; + mOutY = (Float.isNaN(mY)) + ? context.getFloat(Utils.idFromNan(mY)) : mY; + mOutPanX = (Float.isNaN(mPanX)) + ? context.getFloat(Utils.idFromNan(mPanX)) : mPanX; + mOutPanY = (Float.isNaN(mPanY)) + ? context.getFloat(Utils.idFromNan(mPanY)) : mPanY; + } + + @Override + public void registerListening(RemoteContext context) { + if (Float.isNaN(mX)) { + context.listensTo(Utils.idFromNan(mX), this); + } + if (Float.isNaN(mY)) { + context.listensTo(Utils.idFromNan(mY), this); + } + if (Float.isNaN(mPanX)) { + context.listensTo(Utils.idFromNan(mPanX), this); + } + if (Float.isNaN(mPanY) && Utils.idFromNan(mPanY) > 0) { + context.listensTo(Utils.idFromNan(mPanY), this); + } + } + + @Override + public void write(WireBuffer buffer) { + COMPANION.apply(buffer, mTextID, mX, + mY, + mPanX, + mPanY, + mFlags); + } + + @Override + public String toString() { + return "DrawTextAnchored [" + mTextID + "] " + floatToStr(mX) + ", " + + floatToStr(mY) + ", " + + floatToStr(mPanX) + ", " + floatToStr(mPanY) + ", " + + Integer.toBinaryString(mFlags); + } + + private static String floatToStr(float v) { + if (Float.isNaN(v)) { + return "[" + Utils.idFromNan(v) + "]"; + } + return Float.toString(v); + } + + public static class Companion implements CompanionOperation { + private Companion() { + } + + @Override + public void read(WireBuffer buffer, List<Operation> operations) { + int textID = buffer.readInt(); + float x = buffer.readFloat(); + float y = buffer.readFloat(); + float panX = buffer.readFloat(); + float panY = buffer.readFloat(); + int flags = buffer.readInt(); + + DrawTextAnchored op = new DrawTextAnchored(textID, + x, y, + panX, panY, + flags); + + operations.add(op); + } + + @Override + public String name() { + return ""; + } + + @Override + public int id() { + return 0; + } + + /** + * Writes out the operation to the buffer + * @param buffer + * @param textID + * @param x + * @param y + * @param panX + * @param panY + * @param flags + */ + public void apply(WireBuffer buffer, + int textID, + float x, + float y, + float panX, + float panY, + int flags) { + buffer.start(Operations.DRAW_TEXT_ANCHOR); + buffer.writeInt(textID); + buffer.writeFloat(x); + buffer.writeFloat(y); + buffer.writeFloat(panX); + buffer.writeFloat(panY); + buffer.writeInt(flags); + } + } + + float[] mBounds = new float[4]; + + private float getHorizontalOffset() { + // TODO scale TextSize / BaseTextSize; + float scale = 1.0f; + + float textWidth = scale * (mBounds[2] - mBounds[0]); + float boxWidth = 0; + return (boxWidth - textWidth) * (1 + mOutPanX) / 2.f + - (scale * mBounds[0]); + } + + private float getVerticalOffset() { + // TODO scale TextSize / BaseTextSize; + float scale = 1.0f; + float boxHeight = 0; + float textHeight = scale * (mBounds[3] - mBounds[1]); + return (boxHeight - textHeight) * (1 - mOutPanY) / 2 + - (scale * mBounds[1]); + } + + @Override + public void paint(PaintContext context) { + context.getTextBounds(mTextID, 0, -1, + (mFlags & ANCHOR_MONOSPACE_MEASURE) != 0, mBounds); + float x = mOutX + getHorizontalOffset(); + float y = (Float.isNaN(mOutPanY)) ? mOutY : mOutY + getVerticalOffset(); + context.drawTextRun(mTextID, 0, -1, 0, 1, x, y, + (mFlags & ANCHOR_TEXT_RTL) == 1); + } +} diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextOnPath.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextOnPath.java index 1856e3097ec0..b1a01724c315 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextOnPath.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextOnPath.java @@ -24,6 +24,9 @@ import com.android.internal.widget.remotecompose.core.WireBuffer; import java.util.List; +/** + * Draw text along a path. + */ public class DrawTextOnPath extends PaintOperation { public static final Companion COMPANION = new Companion(); int mPathId; @@ -45,7 +48,8 @@ public class DrawTextOnPath extends PaintOperation { @Override public String toString() { - return "DrawTextOnPath " + " " + mPathId + ";"; + return "DrawTextOnPath [" + mTextId + "] [" + mPathId + "] " + + mHOffset + ", " + mVOffset; } public static class Companion implements CompanionOperation { diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTweenPath.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTweenPath.java index ef0a4ad2eff3..48fc94ee5f27 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTweenPath.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTweenPath.java @@ -58,7 +58,7 @@ public class DrawTweenPath extends PaintOperation { public String toString() { return "DrawTweenPath " + mPath1Id + " " + mPath2Id + " " + mTween + " " + mStart + " " - + "- " + mStop + ";"; + + "- " + mStop; } public static class Companion implements CompanionOperation { diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/FloatConstant.java b/core/java/com/android/internal/widget/remotecompose/core/operations/FloatConstant.java new file mode 100644 index 000000000000..576b53f9fc6c --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/FloatConstant.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.widget.remotecompose.core.operations; + +import com.android.internal.widget.remotecompose.core.CompanionOperation; +import com.android.internal.widget.remotecompose.core.Operation; +import com.android.internal.widget.remotecompose.core.Operations; +import com.android.internal.widget.remotecompose.core.RemoteContext; +import com.android.internal.widget.remotecompose.core.WireBuffer; + +import java.util.List; + +/** + * Operation to deal with Text data + */ +public class FloatConstant implements Operation { + public int mTextId; + public float mValue; + public static final Companion COMPANION = new Companion(); + public static final int MAX_STRING_SIZE = 4000; + + public FloatConstant(int textId, float value) { + this.mTextId = textId; + this.mValue = value; + } + + @Override + public void write(WireBuffer buffer) { + COMPANION.apply(buffer, mTextId, mValue); + } + + @Override + public String toString() { + return "FloatConstant[" + mTextId + "] = " + mValue + ""; + } + + public static class Companion implements CompanionOperation { + private Companion() {} + + @Override + public String name() { + return "FloatExpression"; + } + + @Override + public int id() { + return Operations.DATA_FLOAT; + } + + /** + * Writes out the operation to the buffer + * @param buffer + * @param textId + * @param value + */ + public void apply(WireBuffer buffer, int textId, float value) { + buffer.start(Operations.DATA_FLOAT); + buffer.writeInt(textId); + buffer.writeFloat(value); + } + + @Override + public void read(WireBuffer buffer, List<Operation> operations) { + int textId = buffer.readInt(); + + float value = buffer.readFloat(); + operations.add(new FloatConstant(textId, value)); + } + } + + @Override + public void apply(RemoteContext context) { + context.loadFloat(mTextId, mValue); + } + + @Override + public String deepToString(String indent) { + return indent + toString(); + } +} diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/FloatExpression.java b/core/java/com/android/internal/widget/remotecompose/core/operations/FloatExpression.java new file mode 100644 index 000000000000..354f41b813e0 --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/FloatExpression.java @@ -0,0 +1,206 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.widget.remotecompose.core.operations; + +import com.android.internal.widget.remotecompose.core.CompanionOperation; +import com.android.internal.widget.remotecompose.core.Operation; +import com.android.internal.widget.remotecompose.core.Operations; +import com.android.internal.widget.remotecompose.core.RemoteContext; +import com.android.internal.widget.remotecompose.core.VariableSupport; +import com.android.internal.widget.remotecompose.core.WireBuffer; +import com.android.internal.widget.remotecompose.core.operations.utilities.AnimatedFloatExpression; +import com.android.internal.widget.remotecompose.core.operations.utilities.easing.FloatAnimation; + +import java.util.Arrays; +import java.util.List; + +/** + * Operation to deal with AnimatedFloats + * This is designed to be an optimized calculation for things like + * injecting the width of the component int draw rect + * As well as supporting generalized animation floats. + * The floats represent a RPN style calculator + */ +public class FloatExpression implements Operation, VariableSupport { + public int mId; + public float[] mSrcValue; + public float[] mSrcAnimation; + public FloatAnimation mFloatAnimation; + public float[] mPreCalcValue; + private float mLastChange = Float.NaN; + AnimatedFloatExpression mExp = new AnimatedFloatExpression(); + public static final Companion COMPANION = new Companion(); + public static final int MAX_STRING_SIZE = 4000; + + public FloatExpression(int id, float[] value, float[] animation) { + this.mId = id; + this.mSrcValue = value; + this.mSrcAnimation = animation; + if (mSrcAnimation != null) { + mFloatAnimation = new FloatAnimation(mSrcAnimation); + } + } + + @Override + public void updateVariables(RemoteContext context) { + if (mPreCalcValue == null || mPreCalcValue.length != mSrcValue.length) { + mPreCalcValue = new float[mSrcValue.length]; + } + //Utils.log("updateVariables "); + boolean value_changed = false; + for (int i = 0; i < mSrcValue.length; i++) { + float v = mSrcValue[i]; + if (Float.isNaN(v) && !AnimatedFloatExpression.isMathOperator(v)) { + float newValue = context.getFloat(Utils.idFromNan(v)); + if (mFloatAnimation != null) { + if (mPreCalcValue[i] != newValue) { + mLastChange = context.getAnimationTime(); + value_changed = true; + mPreCalcValue[i] = newValue; + } + } else { + mPreCalcValue[i] = newValue; + } + } else { + mPreCalcValue[i] = mSrcValue[i]; + } + } + if (value_changed && mFloatAnimation != null) { + float v = mExp.eval(Arrays.copyOf(mPreCalcValue, mPreCalcValue.length)); + if (Float.isNaN(mFloatAnimation.getTargetValue())) { + mFloatAnimation.setInitialValue(v); + } else { + mFloatAnimation.setInitialValue(mFloatAnimation.getTargetValue()); + } + mFloatAnimation.setTargetValue(v); + } + } + + @Override + public void registerListening(RemoteContext context) { + for (int i = 0; i < mSrcValue.length; i++) { + float v = mSrcValue[i]; + if (Float.isNaN(v) && !AnimatedFloatExpression.isMathOperator(v)) { + context.listensTo(Utils.idFromNan(v), this); + } + } + } + + @Override + public void apply(RemoteContext context) { + updateVariables(context); + float t = context.getAnimationTime(); + if (Float.isNaN(mLastChange)) { + mLastChange = t; + } + if (mFloatAnimation != null) { + float f = mFloatAnimation.get(t - mLastChange); + context.loadFloat(mId, f); + } else { + context.loadFloat(mId, mExp.eval(Arrays.copyOf(mPreCalcValue, mPreCalcValue.length))); + } + } + + @Override + public void write(WireBuffer buffer) { + COMPANION.apply(buffer, mId, mSrcValue, mSrcAnimation); + } + + @Override + public String toString() { + String[] labels = new String[mSrcValue.length]; + for (int i = 0; i < mSrcValue.length; i++) { + if (Float.isNaN(mSrcValue[i])) { + labels[i] = "[" + Utils.idFromNan(mSrcValue[i]) + "]"; + } + + } + return "FloatExpression[" + mId + "] = (" + + AnimatedFloatExpression.toString(mPreCalcValue, labels) + ")"; + } + + public static class Companion implements CompanionOperation { + private Companion() { + } + + @Override + public String name() { + return "FloatExpression"; + } + + @Override + public int id() { + return Operations.ANIMATED_FLOAT; + } + + /** + * Writes out the operation to the buffer + * @param buffer + * @param id + * @param value + * @param animation + */ + public void apply(WireBuffer buffer, int id, float[] value, float[] animation) { + buffer.start(Operations.ANIMATED_FLOAT); + buffer.writeInt(id); + + int len = value.length; + if (animation != null) { + len |= (animation.length << 16); + } + buffer.writeInt(len); + + for (int i = 0; i < value.length; i++) { + buffer.writeFloat(value[i]); + } + if (animation != null) { + for (int i = 0; i < animation.length; i++) { + buffer.writeFloat(animation[i]); + } + } + + } + + @Override + public void read(WireBuffer buffer, List<Operation> operations) { + int id = buffer.readInt(); + int len = buffer.readInt(); + int valueLen = len & 0xFFFF; + int animLen = (len >> 16) & 0xFFFF; + float[] values = new float[valueLen]; + for (int i = 0; i < values.length; i++) { + values[i] = buffer.readFloat(); + } + + float[] animation; + if (animLen != 0) { + animation = new float[animLen]; + for (int i = 0; i < animation.length; i++) { + animation[i] = buffer.readFloat(); + } + } else { + animation = null; + } + operations.add(new FloatExpression(id, values, animation)); + } + } + + @Override + public String deepToString(String indent) { + return indent + toString(); + } + +} diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRestore.java b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRestore.java index 482e0e22bd57..0dad45ce356b 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRestore.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRestore.java @@ -37,7 +37,7 @@ public class MatrixRestore extends PaintOperation { @Override public String toString() { - return "MatrixRestore;"; + return "MatrixRestore"; } public static class Companion implements CompanionOperation { diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRotate.java b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRotate.java index d6c89e0d2c64..bbf41351a1c5 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRotate.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRotate.java @@ -15,68 +15,29 @@ */ package com.android.internal.widget.remotecompose.core.operations; -import com.android.internal.widget.remotecompose.core.CompanionOperation; import com.android.internal.widget.remotecompose.core.Operation; import com.android.internal.widget.remotecompose.core.Operations; import com.android.internal.widget.remotecompose.core.PaintContext; -import com.android.internal.widget.remotecompose.core.PaintOperation; -import com.android.internal.widget.remotecompose.core.WireBuffer; -import java.util.List; - -public class MatrixRotate extends PaintOperation { - public static final Companion COMPANION = new Companion(); - float mRotate, mPivotX, mPivotY; +public class MatrixRotate extends DrawBase3 { + public static final Companion COMPANION = + new Companion(Operations.MATRIX_ROTATE) { + @Override + public Operation construct(float rotate, + float pivotX, + float pivotY + ) { + return new MatrixRotate(rotate, pivotX, pivotY); + } + }; public MatrixRotate(float rotate, float pivotX, float pivotY) { - mRotate = rotate; - mPivotX = pivotX; - mPivotY = pivotY; - } - - @Override - public void write(WireBuffer buffer) { - COMPANION.apply(buffer, mRotate, mPivotX, mPivotY); - } - - @Override - public String toString() { - return "DrawArc " + mRotate + ", " + mPivotX + ", " + mPivotY + ";"; - } - - public static class Companion implements CompanionOperation { - private Companion() { - } - - @Override - public void read(WireBuffer buffer, List<Operation> operations) { - float rotate = buffer.readFloat(); - float pivotX = buffer.readFloat(); - float pivotY = buffer.readFloat(); - MatrixRotate op = new MatrixRotate(rotate, pivotX, pivotY); - operations.add(op); - } - - @Override - public String name() { - return "Matrix"; - } - - @Override - public int id() { - return Operations.MATRIX_ROTATE; - } - - public void apply(WireBuffer buffer, float rotate, float pivotX, float pivotY) { - buffer.start(Operations.MATRIX_ROTATE); - buffer.writeFloat(rotate); - buffer.writeFloat(pivotX); - buffer.writeFloat(pivotY); - } + super(rotate, pivotX, pivotY); + mName = "MatrixRotate"; } @Override public void paint(PaintContext context) { - context.matrixRotate(mRotate, mPivotX, mPivotY); + context.matrixRotate(mV1, mV2, mV3); } } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixScale.java b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixScale.java index 28aa68dd5884..04b940ba16c8 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixScale.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixScale.java @@ -15,74 +15,30 @@ */ package com.android.internal.widget.remotecompose.core.operations; -import com.android.internal.widget.remotecompose.core.CompanionOperation; import com.android.internal.widget.remotecompose.core.Operation; import com.android.internal.widget.remotecompose.core.Operations; import com.android.internal.widget.remotecompose.core.PaintContext; -import com.android.internal.widget.remotecompose.core.PaintOperation; -import com.android.internal.widget.remotecompose.core.WireBuffer; -import java.util.List; - -public class MatrixScale extends PaintOperation { - public static final Companion COMPANION = new Companion(); - float mScaleX, mScaleY; - float mCenterX, mCenterY; +public class MatrixScale extends DrawBase4 { + public static final Companion COMPANION = + new Companion(Operations.MATRIX_SCALE) { + @Override + public Operation construct(float scaleX, + float scaleY, + float centerX, + float centerY + ) { + return new MatrixScale(scaleX, scaleY, centerX, centerY); + } + }; public MatrixScale(float scaleX, float scaleY, float centerX, float centerY) { - mScaleX = scaleX; - mScaleY = scaleY; - mCenterX = centerX; - mCenterY = centerY; - } - - @Override - public void write(WireBuffer buffer) { - COMPANION.apply(buffer, mScaleX, mScaleY, mCenterX, mCenterY); - } - - @Override - public String toString() { - return "MatrixScale " + mScaleY + ", " + mScaleY + ";"; - } - - public static class Companion implements CompanionOperation { - private Companion() { - } - - @Override - public void read(WireBuffer buffer, List<Operation> operations) { - float scaleX = buffer.readFloat(); - float scaleY = buffer.readFloat(); - float centerX = buffer.readFloat(); - float centerY = buffer.readFloat(); - MatrixScale op = new MatrixScale(scaleX, scaleY, centerX, centerY); - operations.add(op); - } - - @Override - public String name() { - return "Matrix"; - } - - @Override - public int id() { - return Operations.MATRIX_SCALE; - } - - public void apply(WireBuffer buffer, float scaleX, float scaleY, - float centerX, float centerY) { - buffer.start(Operations.MATRIX_SCALE); - buffer.writeFloat(scaleX); - buffer.writeFloat(scaleY); - buffer.writeFloat(centerX); - buffer.writeFloat(centerY); - - } + super(scaleX, scaleY, centerX, centerY); + mName = "MatrixScale"; } @Override public void paint(PaintContext context) { - context.mtrixScale(mScaleX, mScaleY, mCenterX, mCenterY); + context.matrixScale(mX1, mY1, mX2, mY2); } } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixTranslate.java b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixTranslate.java index 32987521e041..4f34e987e064 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixTranslate.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixTranslate.java @@ -15,65 +15,28 @@ */ package com.android.internal.widget.remotecompose.core.operations; -import com.android.internal.widget.remotecompose.core.CompanionOperation; import com.android.internal.widget.remotecompose.core.Operation; import com.android.internal.widget.remotecompose.core.Operations; import com.android.internal.widget.remotecompose.core.PaintContext; -import com.android.internal.widget.remotecompose.core.PaintOperation; -import com.android.internal.widget.remotecompose.core.WireBuffer; -import java.util.List; - -public class MatrixTranslate extends PaintOperation { - public static final Companion COMPANION = new Companion(); - float mTranslateX, mTranslateY; +public class MatrixTranslate extends DrawBase2 { + public static final Companion COMPANION = + new Companion(Operations.MATRIX_TRANSLATE) { + @Override + public Operation construct(float x1, + float y1 + ) { + return new MatrixTranslate(x1, y1); + } + }; public MatrixTranslate(float translateX, float translateY) { - mTranslateX = translateX; - mTranslateY = translateY; - } - - @Override - public void write(WireBuffer buffer) { - COMPANION.apply(buffer, mTranslateX, mTranslateY); - } - - @Override - public String toString() { - return "DrawArc " + mTranslateY + ", " + mTranslateY + ";"; - } - - public static class Companion implements CompanionOperation { - private Companion() { - } - - @Override - public void read(WireBuffer buffer, List<Operation> operations) { - float translateX = buffer.readFloat(); - float translateY = buffer.readFloat(); - MatrixTranslate op = new MatrixTranslate(translateX, translateY); - operations.add(op); - } - - @Override - public String name() { - return "Matrix"; - } - - @Override - public int id() { - return Operations.MATRIX_TRANSLATE; - } - - public void apply(WireBuffer buffer, float translateX, float translateY) { - buffer.start(Operations.MATRIX_TRANSLATE); - buffer.writeFloat(translateX); - buffer.writeFloat(translateY); - } + super(translateX, translateY); + mName = "MatrixTranslate"; } @Override public void paint(PaintContext context) { - context.matrixTranslate(mTranslateX, mTranslateY); + context.matrixTranslate(mV1, mV2); } } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/NamedVariable.java b/core/java/com/android/internal/widget/remotecompose/core/operations/NamedVariable.java new file mode 100644 index 000000000000..0c5b286684d4 --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/NamedVariable.java @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.widget.remotecompose.core.operations; + +import com.android.internal.widget.remotecompose.core.CompanionOperation; +import com.android.internal.widget.remotecompose.core.Operation; +import com.android.internal.widget.remotecompose.core.Operations; +import com.android.internal.widget.remotecompose.core.RemoteContext; +import com.android.internal.widget.remotecompose.core.WireBuffer; + +import java.util.List; + +/** + * Operation to deal with Text data + */ +public class NamedVariable implements Operation { + public int mVarId; + public String mVarName; + public int mVarType; + public static final Companion COMPANION = new Companion(); + public static final int MAX_STRING_SIZE = 4000; + + public NamedVariable(int varId, int varType, String name) { + this.mVarId = varId; + this.mVarType = varType; + this.mVarName = name; + } + + @Override + public void write(WireBuffer buffer) { + COMPANION.apply(buffer, mVarId, mVarType, mVarName); + } + + @Override + public String toString() { + return "VariableName[" + mVarId + "] = \"" + + Utils.trimString(mVarName, 10) + "\" type=" + mVarType; + } + + public static class Companion implements CompanionOperation { + private Companion() { + } + + @Override + public String name() { + return "TextData"; + } + + @Override + public int id() { + return Operations.DATA_TEXT; + } + + /** + * Writes out the operation to the buffer + * @param buffer + * @param varId + * @param varType + * @param text + */ + public void apply(WireBuffer buffer, int varId, int varType, String text) { + buffer.start(Operations.DATA_TEXT); + buffer.writeInt(varId); + buffer.writeInt(varType); + buffer.writeUTF8(text); + } + + @Override + public void read(WireBuffer buffer, List<Operation> operations) { + int varId = buffer.readInt(); + int varType = buffer.readInt(); + String text = buffer.readUTF8(MAX_STRING_SIZE); + operations.add(new NamedVariable(varId, varType, text)); + } + } + + @Override + public void apply(RemoteContext context) { + context.loadVariableName(mVarName, mVarId, mVarType); + } + + @Override + public String deepToString(String indent) { + return indent + toString(); + } +} diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/PaintData.java b/core/java/com/android/internal/widget/remotecompose/core/operations/PaintData.java index e5683ece7919..0807bcdcfebb 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/PaintData.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/PaintData.java @@ -20,12 +20,14 @@ import com.android.internal.widget.remotecompose.core.Operation; import com.android.internal.widget.remotecompose.core.Operations; import com.android.internal.widget.remotecompose.core.PaintContext; import com.android.internal.widget.remotecompose.core.PaintOperation; +import com.android.internal.widget.remotecompose.core.RemoteContext; +import com.android.internal.widget.remotecompose.core.VariableSupport; import com.android.internal.widget.remotecompose.core.WireBuffer; import com.android.internal.widget.remotecompose.core.operations.paint.PaintBundle; import java.util.List; -public class PaintData extends PaintOperation { +public class PaintData extends PaintOperation implements VariableSupport { public PaintBundle mPaintData = new PaintBundle(); public static final Companion COMPANION = new Companion(); public static final int MAX_STRING_SIZE = 4000; @@ -34,6 +36,16 @@ public class PaintData extends PaintOperation { } @Override + public void updateVariables(RemoteContext context) { + mPaintData.updateVariables(context); + } + + @Override + public void registerListening(RemoteContext context) { + mPaintData.registerVars(context, this); + } + + @Override public void write(WireBuffer buffer) { COMPANION.apply(buffer, mPaintData); } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/PathData.java b/core/java/com/android/internal/widget/remotecompose/core/operations/PathData.java index 2646b27b1f51..e467e7b7f31e 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/PathData.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/PathData.java @@ -18,27 +18,50 @@ package com.android.internal.widget.remotecompose.core.operations; import com.android.internal.widget.remotecompose.core.CompanionOperation; import com.android.internal.widget.remotecompose.core.Operation; import com.android.internal.widget.remotecompose.core.Operations; -import com.android.internal.widget.remotecompose.core.PaintContext; import com.android.internal.widget.remotecompose.core.RemoteContext; +import com.android.internal.widget.remotecompose.core.VariableSupport; import com.android.internal.widget.remotecompose.core.WireBuffer; +import java.util.Arrays; import java.util.List; -public class PathData implements Operation { +public class PathData implements Operation, VariableSupport { public static final Companion COMPANION = new Companion(); int mInstanceId; - float[] mRef; float[] mFloatPath; - float[] mRetFloats; + float[] mOutputPath; PathData(int instanceId, float[] floatPath) { mInstanceId = instanceId; mFloatPath = floatPath; + mOutputPath = Arrays.copyOf(mFloatPath, mFloatPath.length); + } + + @Override + public void updateVariables(RemoteContext context) { + for (int i = 0; i < mFloatPath.length; i++) { + float v = mFloatPath[i]; + if (Utils.isVariable(v)) { + mOutputPath[i] = (Float.isNaN(v)) + ? context.getFloat(Utils.idFromNan(v)) : v; + } else { + mOutputPath[i] = v; + } + } + } + + @Override + public void registerListening(RemoteContext context) { + for (int i = 0; i < mFloatPath.length; i++) { + if (Float.isNaN(mFloatPath[i])) { + context.listensTo(Utils.idFromNan(mFloatPath[i]), this); + } + } } @Override public void write(WireBuffer buffer) { - COMPANION.apply(buffer, mInstanceId, mFloatPath); + COMPANION.apply(buffer, mInstanceId, mOutputPath); } @Override @@ -46,29 +69,35 @@ public class PathData implements Operation { return pathString(mFloatPath); } - public float[] getFloatPath(PaintContext context) { - float[] ret = mRetFloats; // Assume retFloats is declared elsewhere - if (ret == null) { - return mFloatPath; // Assume floatPath is declared elsewhere - } - float[] localRef = mRef; // Assume ref is of type Float[] - if (localRef == null) { - for (int i = 0; i < mFloatPath.length; i++) { - ret[i] = mFloatPath[i]; - } - } else { - for (int i = 0; i < mFloatPath.length; i++) { - float lr = localRef[i]; - if (Float.isNaN(lr)) { - ret[i] = Utils.getActualValue(lr); - } else { - ret[i] = mFloatPath[i]; - } - } - } - return ret; + @Override + public String toString() { + return "PathData[" + mInstanceId + "] = " + "\"" + deepToString(" ") + "\""; } + /** + * public float[] getFloatPath(PaintContext context) { + * float[] ret = mRetFloats; // Assume retFloats is declared elsewhere + * if (ret == null) { + * return mFloatPath; // Assume floatPath is declared elsewhere + * } + * float[] localRef = mRef; // Assume ref is of type Float[] + * if (localRef == null) { + * for (int i = 0; i < mFloatPath.length; i++) { + * ret[i] = mFloatPath[i]; + * } + * } else { + * for (int i = 0; i < mFloatPath.length; i++) { + * float lr = localRef[i]; + * if (Float.isNaN(lr)) { + * ret[i] = Utils.getActualValue(lr); + * } else { + * ret[i] = mFloatPath[i]; + * } + * } + * } + * return ret; + * } + */ public static final int MOVE = 10; public static final int LINE = 11; public static final int QUADRATIC = 12; @@ -155,7 +184,7 @@ public class PathData implements Operation { str.append("."); break; default: - str.append("X"); + str.append("[" + id + "]"); break; } } else { @@ -170,7 +199,7 @@ public class PathData implements Operation { @Override public void apply(RemoteContext context) { - context.loadPathData(mInstanceId, mFloatPath); + context.loadPathData(mInstanceId, mOutputPath); } } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentBehavior.java b/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentBehavior.java index 6d924eb70c50..997e8dc791ed 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentBehavior.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentBehavior.java @@ -94,7 +94,6 @@ public class RootContentBehavior implements RemoteComposeOperation { public static final int SCALE_CROP = 5; public static final int SCALE_FILL_BOUNDS = 6; - public static final Companion COMPANION = new Companion(); /** diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentDescription.java b/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentDescription.java index 64c7f3ef2d44..076b28edf981 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentDescription.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentDescription.java @@ -48,7 +48,7 @@ public class RootContentDescription implements RemoteComposeOperation { @Override public String toString() { - return "ROOT_CONTENT_DESCRIPTION " + mContentDescription; + return "RootContentDescription " + mContentDescription; } @Override diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/ShaderData.java b/core/java/com/android/internal/widget/remotecompose/core/operations/ShaderData.java new file mode 100644 index 000000000000..8463ac576774 --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/ShaderData.java @@ -0,0 +1,309 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.widget.remotecompose.core.operations; + +import com.android.internal.widget.remotecompose.core.CompanionOperation; +import com.android.internal.widget.remotecompose.core.Operation; +import com.android.internal.widget.remotecompose.core.Operations; +import com.android.internal.widget.remotecompose.core.RemoteContext; +import com.android.internal.widget.remotecompose.core.VariableSupport; +import com.android.internal.widget.remotecompose.core.WireBuffer; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; + +/** + * Operation to deal with bitmap data + * On getting an Image during a draw call the bitmap is compressed and saved + * in playback the image is decompressed + */ +public class ShaderData implements Operation, VariableSupport { + int mShaderTextId; // the actual text of a shader + int mShaderID; // allows shaders to be referenced by number + HashMap<String, float[]> mUniformRawFloatMap = null; + HashMap<String, float[]> mUniformFloatMap = null; + HashMap<String, int[]> mUniformIntMap = null; + HashMap<String, Integer> mUniformBitmapMap = null; + + public static final int MAX_IMAGE_DIMENSION = 8000; + + public static final Companion COMPANION = new Companion(); + + public ShaderData(int shaderID, + int shaderTextId, + HashMap<String, float[]> floatMap, + HashMap<String, int[]> intMap, + HashMap<String, Integer> bitmapMap) { + mShaderID = shaderID; + mShaderTextId = shaderTextId; + if (floatMap != null) { + mUniformFloatMap = new HashMap<>(); + mUniformRawFloatMap = new HashMap<>(); + + for (String name : floatMap.keySet()) { + mUniformRawFloatMap.put(name, floatMap.get(name)); + mUniformFloatMap.put(name, floatMap.get(name)); + } + } + + if (intMap != null) { + mUniformIntMap = new HashMap<>(); + for (String name : intMap.keySet()) { + mUniformIntMap.put(name, intMap.get(name)); + } + } + if (bitmapMap != null) { + mUniformBitmapMap = new HashMap<>(); + for (String name : bitmapMap.keySet()) { + mUniformBitmapMap.put(name, bitmapMap.get(name)); + } + } + + } + + public int getShaderTextId() { + return mShaderTextId; + } + + /** + * get names of all known floats + * @return + */ + public String[] getUniformFloatNames() { + if (mUniformFloatMap == null) return new String[0]; + return mUniformFloatMap.keySet().toArray(new String[0]); + } + + /** + * Get float values associated with the name + * @param name + * @return + */ + public float[] getUniformFloats(String name) { + return mUniformFloatMap.get(name); + } + + /** + * get the name of all know uniform integers + * @return + */ + public String[] getUniformIntegerNames() { + if (mUniformIntMap == null) return new String[0]; + return mUniformIntMap.keySet().toArray(new String[0]); + } + + /** + * Get Int value associated with the name + * @param name + * @return + */ + public int[] getUniformInts(String name) { + return mUniformIntMap.get(name); + } + + /** + * get list of uniform Bitmaps + * @return + */ + public String[] getUniformBitmapNames() { + if (mUniformBitmapMap == null) return new String[0]; + return mUniformBitmapMap.keySet().toArray(new String[0]); + } + + /** + * Get a bitmap stored under that name + * @param name + * @return + */ + public int getUniformBitmapId(String name) { + return mUniformBitmapMap.get(name); + } + + @Override + public void write(WireBuffer buffer) { + COMPANION.apply(buffer, mShaderID, mShaderTextId, + mUniformFloatMap, mUniformIntMap, mUniformBitmapMap); + } + + @Override + public String toString() { + return "SHADER DATA " + mShaderID; + } + + @Override + public void updateVariables(RemoteContext context) { + for (String name : mUniformRawFloatMap.keySet()) { + float[] value = mUniformRawFloatMap.get(name); + float[] out = null; + for (int i = 0; i < value.length; i++) { + if (Float.isNaN(value[i])) { + if (out == null) { // need to copy + out = Arrays.copyOf(value, value.length); + } + out[i] = context.getFloat(Utils.idFromNan(value[i])); + } + } + mUniformFloatMap.put(name, out == null ? value : out); + } + } + + @Override + public void registerListening(RemoteContext context) { + for (String name : mUniformRawFloatMap.keySet()) { + float[] value = mUniformRawFloatMap.get(name); + for (int i = 0; i < value.length; i++) { + if (Float.isNaN(value[i])) { + context.listensTo(Utils.idFromNan(value[i]), this); + } + } + } + } + + public static class Companion implements CompanionOperation { + private Companion() { + } + + @Override + public String name() { + return "BitmapData"; + } + + @Override + public int id() { + return Operations.DATA_SHADER; + } + + /** + * Writes out the operation to the buffer + * @param buffer + * @param shaderID + * @param shaderTextId + * @param floatMap + * @param intMap + * @param bitmapMap + */ + public void apply(WireBuffer buffer, int shaderID, int shaderTextId, + HashMap<String, float[]> floatMap, + HashMap<String, int[]> intMap, + HashMap<String, Integer> bitmapMap) { + buffer.start(Operations.DATA_SHADER); + buffer.writeInt(shaderID); + + buffer.writeInt(shaderTextId); + int floatSize = (floatMap == null) ? 0 : floatMap.size(); + int intSize = (intMap == null) ? 0 : intMap.size(); + int bitmapSize = (bitmapMap == null) ? 0 : bitmapMap.size(); + int sizes = floatSize | (intSize << 8) | (bitmapSize << 16); + buffer.writeInt(sizes); + + if (floatSize > 0) { + + for (String name : floatMap.keySet()) { + buffer.writeUTF8(name); + float[] values = floatMap.get(name); + buffer.writeInt(values.length); + + for (int i = 0; i < values.length; i++) { + buffer.writeFloat(values[i]); + } + } + } + + if (intSize > 0) { + for (String name : intMap.keySet()) { + buffer.writeUTF8(name); + int[] values = intMap.get(name); + buffer.writeInt(values.length); + for (int i = 0; i < values.length; i++) { + buffer.writeInt(values[i]); + } + } + } + if (bitmapSize > 0) { + for (String name : bitmapMap.keySet()) { + buffer.writeUTF8(name); + int value = bitmapMap.get(name); + buffer.writeInt(value); + } + } + } + + @Override + public void read(WireBuffer buffer, List<Operation> operations) { + int shaderID = buffer.readInt(); + int shaderTextId = buffer.readInt(); + HashMap<String, float[]> floatMap = null; + HashMap<String, int[]> intMap = null; + HashMap<String, Integer> bitmapMap = null; + + int sizes = buffer.readInt(); + + int floatMapSize = sizes & 0xFF; + if (floatMapSize > 0) { + floatMap = new HashMap<>(); + for (int i = 0; i < floatMapSize; i++) { + String name = buffer.readUTF8(); + int len = buffer.readInt(); + float[] val = new float[len]; + + for (int j = 0; j < len; j++) { + val[j] = buffer.readFloat(); + } + + floatMap.put(name, val); + } + } + int intMapSize = (sizes >> 8) & 0xFF; + + if (intMapSize > 0) { + + intMap = new HashMap<>(); + for (int i = 0; i < intMapSize; i++) { + String name = buffer.readUTF8(); + int len = buffer.readInt(); + int[] val = new int[len]; + for (int j = 0; j < len; j++) { + val[j] = buffer.readInt(); + } + intMap.put(name, val); + } + } + int bitmapMapSize = (sizes >> 16) & 0xFF; + + if (bitmapMapSize > 0) { + bitmapMap = new HashMap<>(); + for (int i = 0; i < bitmapMapSize; i++) { + String name = buffer.readUTF8(); + int val = buffer.readInt(); + bitmapMap.put(name, val); + } + } + operations.add(new ShaderData(shaderID, shaderTextId, + floatMap, intMap, bitmapMap)); + } + } + + @Override + public void apply(RemoteContext context) { + context.loadShader(mShaderID, this); + } + + @Override + public String deepToString(String indent) { + return indent + toString(); + } +} diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/TextData.java b/core/java/com/android/internal/widget/remotecompose/core/operations/TextData.java index 5b622ae96d0b..ed1344975256 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/TextData.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TextData.java @@ -44,11 +44,13 @@ public class TextData implements Operation { @Override public String toString() { - return "TEXT DATA " + mTextId + "\"" + mText + "\""; + return "TextData[" + mTextId + "] = \"" + + Utils.trimString(mText, 10) + "\""; } public static class Companion implements CompanionOperation { - private Companion() {} + private Companion() { + } @Override public String name() { diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/TextFromFloat.java b/core/java/com/android/internal/widget/remotecompose/core/operations/TextFromFloat.java new file mode 100644 index 000000000000..65a39a1eba04 --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TextFromFloat.java @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.widget.remotecompose.core.operations; + +import com.android.internal.widget.remotecompose.core.CompanionOperation; +import com.android.internal.widget.remotecompose.core.Operation; +import com.android.internal.widget.remotecompose.core.Operations; +import com.android.internal.widget.remotecompose.core.RemoteContext; +import com.android.internal.widget.remotecompose.core.VariableSupport; +import com.android.internal.widget.remotecompose.core.WireBuffer; +import com.android.internal.widget.remotecompose.core.operations.utilities.StringUtils; + +import java.util.List; + +/** + * Operation convert floats to text + * This command is structured [command][textID][before,after][flags] + * before and after define number of digits before and after the decimal point + */ +public class TextFromFloat implements Operation, VariableSupport { + public int mTextId; + public float mValue; + public float mOutValue; + public short mDigitsBefore; + public short mDigitsAfter; + public int mFlags; + public static final Companion COMPANION = new Companion(); + public static final int MAX_STRING_SIZE = 4000; + char mPre = ' '; + char mAfter = ' '; + // Theses flags define what how to/if fill the space + public static final int PAD_AFTER_SPACE = 0; // pad past point with space + public static final int PAD_AFTER_NONE = 1; // do not pad past last digit + public static final int PAD_AFTER_ZERO = 3; // pad with 0 past last digit + public static final int PAD_PRE_SPACE = 0; // pad before number with spaces + public static final int PAD_PRE_NONE = 4; // pad before number with 0s + public static final int PAD_PRE_ZERO = 12; // do not pad before number + + public TextFromFloat(int textId, float value, short digitsBefore, + short digitsAfter, int flags) { + this.mTextId = textId; + this.mValue = value; + this.mDigitsAfter = digitsAfter; + this.mDigitsBefore = digitsBefore; + this.mFlags = flags; + mOutValue = mValue; + switch (mFlags & 3) { + case PAD_AFTER_SPACE: + mAfter = ' '; + break; + case PAD_AFTER_NONE: + mAfter = 0; + break; + case PAD_AFTER_ZERO: + mAfter = '0'; + break; + } + switch (mFlags & 12) { + case PAD_PRE_SPACE: + mPre = ' '; + break; + case PAD_PRE_NONE: + mPre = 0; + break; + case PAD_PRE_ZERO: + mPre = '0'; + break; + } + } + + @Override + public void write(WireBuffer buffer) { + COMPANION.apply(buffer, mTextId, mValue, mDigitsAfter, mDigitsBefore, mFlags); + } + + @Override + public String toString() { + return "TextFromFloat[" + mTextId + "] = " + + Utils.floatToString(mValue) + " " + mDigitsBefore + + "." + mDigitsAfter + " " + mFlags; + } + + + @Override + public void updateVariables(RemoteContext context) { + if (Float.isNaN(mValue)) { + mOutValue = context.getFloat(Utils.idFromNan(mValue)); + } + + } + + + @Override + public void registerListening(RemoteContext context) { + if (Float.isNaN(mValue)) { + context.listensTo(Utils.idFromNan(mValue), this); + } + } + + public static class Companion implements CompanionOperation { + private Companion() { + } + + @Override + public String name() { + return "TextData"; + } + + @Override + public int id() { + return Operations.TEXT_FROM_FLOAT; + } + + /** + * Writes out the operation to the buffer + * @param buffer + * @param textId + * @param value + * @param digitsBefore + * @param digitsAfter + * @param flags + */ + public void apply(WireBuffer buffer, int textId, + float value, short digitsBefore, + short digitsAfter, int flags) { + buffer.start(Operations.TEXT_FROM_FLOAT); + buffer.writeInt(textId); + buffer.writeFloat(value); + buffer.writeInt((digitsBefore << 16) | digitsAfter); + buffer.writeInt(flags); + + } + + @Override + public void read(WireBuffer buffer, List<Operation> operations) { + int textId = buffer.readInt(); + float value = buffer.readFloat(); + int tmp = buffer.readInt(); + short post = (short) (tmp & 0xFFFF); + short pre = (short) ((tmp >> 16) & 0xFFFF); + + int flags = buffer.readInt(); + operations.add(new TextFromFloat(textId, value, pre, post, flags)); + } + } + + @Override + public void apply(RemoteContext context) { + float v = mOutValue; + String s = StringUtils.floatToString(v, mDigitsBefore, + mDigitsAfter, mPre, mAfter); + context.loadText(mTextId, s); + } + + @Override + public String deepToString(String indent) { + return indent + toString(); + } +} diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/TextMerge.java b/core/java/com/android/internal/widget/remotecompose/core/operations/TextMerge.java new file mode 100644 index 000000000000..a0fc854f38f4 --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TextMerge.java @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.widget.remotecompose.core.operations; + +import com.android.internal.widget.remotecompose.core.CompanionOperation; +import com.android.internal.widget.remotecompose.core.Operation; +import com.android.internal.widget.remotecompose.core.Operations; +import com.android.internal.widget.remotecompose.core.RemoteContext; +import com.android.internal.widget.remotecompose.core.WireBuffer; + +import java.util.List; + +/** + * Operation to deal with Text data + */ +public class TextMerge implements Operation { + public int mTextId; + public int mSrcId1; + public int mSrcId2; + public static final Companion COMPANION = new Companion(); + public static final int MAX_STRING_SIZE = 4000; + + public TextMerge(int textId, int srcId1, int srcId2) { + this.mTextId = textId; + this.mSrcId1 = srcId1; + this.mSrcId2 = srcId2; + } + + @Override + public void write(WireBuffer buffer) { + COMPANION.apply(buffer, mTextId, mSrcId1, mSrcId2); + } + + @Override + public String toString() { + return "TextMerge[" + mTextId + "] = [" + mSrcId1 + " ] + [ " + mSrcId2 + "]"; + } + + public static class Companion implements CompanionOperation { + private Companion() { + } + + @Override + public String name() { + return "TextData"; + } + + @Override + public int id() { + return Operations.TEXT_MERGE; + } + + /** + * Writes out the operation to the buffer + * @param buffer + * @param textId + * @param srcId1 + * @param srcId2 + */ + public void apply(WireBuffer buffer, int textId, int srcId1, int srcId2) { + buffer.start(Operations.TEXT_MERGE); + buffer.writeInt(textId); + buffer.writeInt(srcId1); + buffer.writeInt(srcId2); + } + + @Override + public void read(WireBuffer buffer, List<Operation> operations) { + int textId = buffer.readInt(); + int srcId1 = buffer.readInt(); + int srcId2 = buffer.readInt(); + + operations.add(new TextMerge(textId, srcId1, srcId2)); + } + } + + @Override + public void apply(RemoteContext context) { + String str1 = context.getText(mSrcId1); + String str2 = context.getText(mSrcId2); + context.loadText(mTextId, str1 + str2); + } + + @Override + public String deepToString(String indent) { + return indent + toString(); + } +} diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/Utils.java b/core/java/com/android/internal/widget/remotecompose/core/operations/Utils.java index 00e2f2058e89..fdc68601bf4d 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/Utils.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/Utils.java @@ -15,13 +15,16 @@ */ package com.android.internal.widget.remotecompose.core.operations; +/** + * Utilities to be used across all core operations + */ public class Utils { public static float asNan(int v) { return Float.intBitsToFloat(v | -0x800000); } public static int idFromNan(float value) { - int b = Float.floatToRawIntBits(value); + int b = Float.floatToRawIntBits(value); return b & 0xFFFFF; } @@ -29,13 +32,194 @@ public class Utils { return 0; } - String getFloatString(float value) { + /** + * trim a string to n characters if needing to trim + * end in "..." + * + * @param str + * @param n + * @return + */ + static String trimString(String str, int n) { + if (str.length() > n) { + str = str.substring(0, n - 3) + "..."; + } + return str; + } + + /** + * print the id and the value of a float + * @param idvalue + * @param value + * @return + */ + public static String floatToString(float idvalue, float value) { + if (Float.isNaN(idvalue)) { + return "[" + idFromNan(idvalue) + "]" + floatToString(value); + } + return floatToString(value); + } + + /** + * Convert float to string but render nan id in brackets [n] + * @param value + * @return + */ + public static String floatToString(float value) { if (Float.isNaN(value)) { - int id = idFromNan(value); - if (id > 0) { - return "NaN(" + id + ")"; - } + return "[" + idFromNan(value) + "]"; } - return "" + value; + return Float.toString(value); + } + + /** + * Debugging util to print a message and include the file/line it came from + * @param str + */ + public static void log(String str) { + StackTraceElement s = new Throwable().getStackTrace()[1]; + System.out.println("(" + s.getFileName() + ":" + s.getLineNumber() + ")." + str); + } + + /** + * Debugging util to print the stack + * @param str + * @param n + */ + public static void logStack(String str, int n) { + StackTraceElement[] st = new Throwable().getStackTrace(); + for (int i = 1; i < n + 1; i++) { + StackTraceElement s = st[i]; + String space = new String(new char[i]).replace('\0', ' '); + System.out.println(space + "(" + s.getFileName() + + ":" + s.getLineNumber() + ")." + str); + } + } + + /** + * Is a variable Allowed int calculation and references. + * + * @param v + * @return + */ + public static boolean isVariable(float v) { + if (Float.isNaN(v)) { + int id = idFromNan(v); + return id > 40 || id < 10; + } + return false; + } + + /** + * print a color in the familiar 0xAARRGGBB pattern + * + * @param color + * @return + */ + public static String colorInt(int color) { + String str = "000000000000" + Integer.toHexString(color); + return "0x" + str.substring(str.length() - 8); } + + /** + * Interpolate two colors. + * gamma corrected colors are interpolated in the form c1 * (1-t) + c2 * t + * + * @param c1 + * @param c2 + * @param t + * @return + */ + public static int interpolateColor(int c1, int c2, float t) { + if (Float.isNaN(t) || t == 0.0f) { + return c1; + } else if (t == 1.0f) { + return c2; + } + int a = 0xFF & (c1 >> 24); + int r = 0xFF & (c1 >> 16); + int g = 0xFF & (c1 >> 8); + int b = 0xFF & c1; + float f_r = (float) Math.pow(r / 255.0f, 2.2); + float f_g = (float) Math.pow(g / 255.0f, 2.2); + float f_b = (float) Math.pow(b / 255.0f, 2.2); + float c1fr = f_r; + float c1fg = f_g; + float c1fb = f_b; + float c1fa = a / 255f; + + a = 0xFF & (c2 >> 24); + r = 0xFF & (c2 >> 16); + g = 0xFF & (c2 >> 8); + b = 0xFF & c2; + f_r = (float) Math.pow(r / 255.0f, 2.2); + f_g = (float) Math.pow(g / 255.0f, 2.2); + f_b = (float) Math.pow(b / 255.0f, 2.2); + float c2fr = f_r; + float c2fg = f_g; + float c2fb = f_b; + float c2fa = a / 255f; + f_r = c1fr + t * (c2fr - c1fr); + f_g = c1fg + t * (c2fg - c1fg); + f_b = c1fb + t * (c2fb - c1fb); + float f_a = c1fa + t * (c2fa - c1fa); + + int outr = clamp((int) ((float) Math.pow(f_r, 1.0 / 2.2) * 255.0f)); + int outg = clamp((int) ((float) Math.pow(f_g, 1.0 / 2.2) * 255.0f)); + int outb = clamp((int) ((float) Math.pow(f_b, 1.0 / 2.2) * 255.0f)); + int outa = clamp((int) (f_a * 255.0f)); + + + return (outa << 24 | outr << 16 | outg << 8 | outb); + } + + /** + * Efficient clamping function + * + * @param c + * @return number between 0 and 255 + */ + public static int clamp(int c) { + int n = 255; + c &= ~(c >> 31); + c -= n; + c &= (c >> 31); + c += n; + return c; + } + + /** + * convert hue saturation and value to RGB + * + * @param hue 0..1 + * @param saturation 0..1 0=on the gray scale + * @param value 0..1 0=black + * @return + */ + public static int hsvToRgb(float hue, float saturation, float value) { + int h = (int) (hue * 6); + float f = hue * 6 - h; + int p = (int) (0.5f + 255 * value * (1 - saturation)); + int q = (int) (0.5f + 255 * value * (1 - f * saturation)); + int t = (int) (0.5f + 255 * value * (1 - (1 - f) * saturation)); + int v = (int) (0.5f + 255 * value); + switch (h) { + case 0: + return 0XFF000000 | (v << 16) + (t << 8) + p; + case 1: + return 0XFF000000 | (q << 16) + (v << 8) + p; + case 2: + return 0XFF000000 | (p << 16) + (v << 8) + t; + case 3: + return 0XFF000000 | (p << 16) + (q << 8) + v; + case 4: + return 0XFF000000 | (t << 16) + (p << 8) + v; + case 5: + return 0XFF000000 | (v << 16) + (p << 8) + q; + + } + return 0; + } + + } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintBundle.java b/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintBundle.java index 8abb0bfff338..a7d0ac6330f7 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintBundle.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintBundle.java @@ -15,43 +15,60 @@ */ package com.android.internal.widget.remotecompose.core.operations.paint; +import com.android.internal.widget.remotecompose.core.PaintContext; +import com.android.internal.widget.remotecompose.core.RemoteContext; +import com.android.internal.widget.remotecompose.core.VariableSupport; import com.android.internal.widget.remotecompose.core.WireBuffer; +import com.android.internal.widget.remotecompose.core.operations.Utils; import java.util.Arrays; +/** + * Paint Bundle represents a delta of changes to a paint object + */ public class PaintBundle { int[] mArray = new int[200]; + int[] mOutArray = null; int mPos = 0; - public void applyPaintChange(PaintChanges p) { + /** + * Apply changes to a PaintChanges interface + * @param paintContext + * @param p + */ + public void applyPaintChange(PaintContext paintContext, PaintChanges p) { int i = 0; int mask = 0; + if (mOutArray == null) { + mOutArray = mArray; + } while (i < mPos) { - int cmd = mArray[i++]; + int cmd = mOutArray[i++]; mask = mask | (1 << (cmd - 1)); switch (cmd & 0xFFFF) { case TEXT_SIZE: { - p.setTextSize(Float.intBitsToFloat(mArray[i++])); + p.setTextSize(Float.intBitsToFloat(mOutArray[i++])); break; } case TYPEFACE: int style = (cmd >> 16); int weight = style & 0x3ff; boolean italic = (style >> 10) > 0; - int font_type = mArray[i++]; + int font_type = mOutArray[i++]; p.setTypeFace(font_type, weight, italic); break; + case COLOR_ID: // mOutArray should have already decoded it case COLOR: { - p.setColor(mArray[i++]); + p.setColor(mOutArray[i++]); break; } case STROKE_WIDTH: { - p.setStrokeWidth(Float.intBitsToFloat(mArray[i++])); + p.setStrokeWidth(Float.intBitsToFloat(mOutArray[i++])); break; } case STROKE_MITER: { - p.setStrokeMiter(Float.intBitsToFloat(mArray[i++])); + p.setStrokeMiter(Float.intBitsToFloat(mOutArray[i++])); break; } case STROKE_CAP: { @@ -63,6 +80,7 @@ public class PaintBundle { break; } case SHADER: { + p.setShader(mOutArray[i++]); break; } case STROKE_JOIN: { @@ -81,17 +99,16 @@ public class PaintBundle { p.setFilterBitmap(!((cmd >> 16) == 0)); break; } - case GRADIENT: { - i = callSetGradient(cmd, mArray, i, p); + i = callSetGradient(cmd, mOutArray, i, p); break; } case COLOR_FILTER: { - p.setColorFilter(mArray[i++], cmd >> 16); + p.setColorFilter(mOutArray[i++], cmd >> 16); break; } case ALPHA: { - p.setAlpha(Float.intBitsToFloat(mArray[i++])); + p.setAlpha(Float.intBitsToFloat(mOutArray[i++])); break; } } @@ -106,7 +123,6 @@ public class PaintBundle { switch (id) { case TEXT_SIZE: return "TEXT_SIZE"; - case COLOR: return "COLOR"; case STROKE_WIDTH: @@ -133,7 +149,6 @@ public class PaintBundle { return "ALPHA"; case COLOR_FILTER: return "COLOR_FILTER"; - } return "????" + id + "????"; } @@ -154,6 +169,14 @@ public class PaintBundle { return str + "]"; } + private static String asFloatStr(int value) { + float fValue = Float.intBitsToFloat(value); + if (Float.isNaN(fValue)) { + return "[" + Utils.idFromNan(fValue) + "]"; + } + return Float.toString(fValue); + } + @Override public String toString() { StringBuilder ret = new StringBuilder("\n"); @@ -164,7 +187,8 @@ public class PaintBundle { switch (type) { case TEXT_SIZE: { - ret.append(" TextSize(" + Float.intBitsToFloat(mArray[i++])); + ret.append(" TextSize(" + + asFloatStr(mArray[i++])); } break; @@ -181,14 +205,18 @@ public class PaintBundle { ret.append(" Color(" + colorInt(mArray[i++])); } break; + case COLOR_ID: { + ret.append(" ColorId([" + mArray[i++] + "]"); + } + break; case STROKE_WIDTH: { ret.append(" StrokeWidth(" - + (Float.intBitsToFloat(mArray[i++]))); + + (asFloatStr(mArray[i++]))); } break; case STROKE_MITER: { ret.append(" StrokeMiter(" - + (Float.intBitsToFloat(mArray[i++]))); + + (asFloatStr(mArray[i++]))); } break; case STROKE_CAP: { @@ -207,11 +235,12 @@ public class PaintBundle { } break; case SHADER: { + ret.append(" Shader(" + mArray[i++]); } break; case ALPHA: { ret.append(" Alpha(" - + (Float.intBitsToFloat(mArray[i++]))); + + (asFloatStr(mArray[i++]))); } break; case IMAGE_FILTER_QUALITY: { @@ -244,7 +273,6 @@ public class PaintBundle { return ret.toString(); } - int callPrintGradient(int cmd, int[] array, int i, StringBuilder p) { int ret = i; int type = (cmd >> 16); @@ -258,26 +286,25 @@ public class PaintBundle { colors = new int[len]; for (int j = 0; j < colors.length; j++) { colors[j] = array[ret++]; - } } len = array[ret++]; - float[] stops = null; + String[] stops = null; if (len > 0) { - stops = new float[len]; + stops = new String[len]; for (int j = 0; j < stops.length; j++) { - stops[j] = Float.intBitsToFloat(array[ret++]); + stops[j] = asFloatStr(array[ret++]); } } p.append(" colors = " + colorInt(colors) + ",\n"); p.append(" stops = " + Arrays.toString(stops) + ",\n"); p.append(" start = "); - p.append("[" + Float.intBitsToFloat(array[ret++])); - p.append(", " + Float.intBitsToFloat(array[ret++]) + "],\n"); + p.append("[" + asFloatStr(array[ret++])); + p.append(", " + asFloatStr(array[ret++]) + "],\n"); p.append(" end = "); - p.append("[" + Float.intBitsToFloat(array[ret++])); - p.append(", " + Float.intBitsToFloat(array[ret++]) + "],\n"); + p.append("[" + asFloatStr(array[ret++])); + p.append(", " + asFloatStr(array[ret++]) + "],\n"); int tileMode = array[ret++]; p.append(" tileMode = " + tileMode + "\n "); } @@ -295,21 +322,21 @@ public class PaintBundle { } } len = array[ret++]; - float[] stops = null; + String[] stops = null; if (len > 0) { - stops = new float[len]; + stops = new String[len]; for (int j = 0; j < stops.length; j++) { - stops[j] = Float.intBitsToFloat(array[ret++]); + stops[j] = asFloatStr(array[ret++]); } } p.append(" colors = " + colorInt(colors) + ",\n"); p.append(" stops = " + Arrays.toString(stops) + ",\n"); p.append(" center = "); - p.append("[" + Float.intBitsToFloat(array[ret++])); - p.append(", " + Float.intBitsToFloat(array[ret++]) + "],\n"); + p.append("[" + asFloatStr(array[ret++])); + p.append(", " + asFloatStr(array[ret++]) + "],\n"); p.append(" radius ="); - p.append(" " + Float.intBitsToFloat(array[ret++]) + ",\n"); + p.append(" " + asFloatStr(array[ret++]) + ",\n"); int tileMode = array[ret++]; p.append(" tileMode = " + tileMode + "\n "); } @@ -327,20 +354,19 @@ public class PaintBundle { } } len = array[ret++]; - float[] stops = null; + String[] stops = null; if (len > 0) { - stops = new float[len]; + stops = new String[len]; for (int j = 0; j < stops.length; j++) { - stops[j] = Float.intBitsToFloat(array[ret++]); + stops[j] = asFloatStr(array[ret++]); } } - p.append(" colors = " + colorInt(colors) + ",\n"); p.append(" stops = " + Arrays.toString(stops) + ",\n"); p.append(" center = "); - p.append("[" + Float.intBitsToFloat(array[ret++])); - p.append(", " + Float.intBitsToFloat(array[ret++]) + "],\n "); - + p.append("[" + asFloatStr(array[ret++])); + p.append(", " + + asFloatStr(array[ret++]) + "],\n "); } break; default: { @@ -376,7 +402,6 @@ public class PaintBundle { return ret; } - switch (gradientType) { case LINEAR_GRADIENT: { @@ -433,7 +458,7 @@ public class PaintBundle { public static final int COLOR = 4; // int public static final int STROKE_WIDTH = 5; // float public static final int STROKE_MITER = 6; - public static final int STROKE_CAP = 7; // int + public static final int STROKE_CAP = 7; // int public static final int STYLE = 8; // int public static final int SHADER = 9; // int public static final int IMAGE_FILTER_QUALITY = 10; // int @@ -445,7 +470,7 @@ public class PaintBundle { public static final int TYPEFACE = 16; public static final int FILTER_BITMAP = 17; public static final int BLEND_MODE = 18; - + public static final int COLOR_ID = 19; // int public static final int BLEND_MODE_CLEAR = 0; public static final int BLEND_MODE_SRC = 1; @@ -634,8 +659,8 @@ public class PaintBundle { /** * @param fontType 0 = default 1 = sans serif 2 = serif 3 = monospace - * @param weight 100-1000 - * @param italic tur + * @param weight 100-1000 + * @param italic tur */ public void setTextStyle(int fontType, int weight, boolean italic) { int style = (weight & 0x3FF) | (italic ? 2048 : 0); // pack the weight and italic @@ -658,6 +683,10 @@ public class PaintBundle { mPos++; } + /** + * Set the Color based on Color + * @param color + */ public void setColor(int color) { mArray[mPos] = COLOR; mPos++; @@ -666,6 +695,18 @@ public class PaintBundle { } /** + * Set the Color based on ID + * @param color + */ + public void setColorId(int color) { + mArray[mPos] = COLOR_ID; + mPos++; + mArray[mPos] = color; + mPos++; + } + + + /** * Set the paint's Cap. * * @param cap set the paint's line cap style, used whenever the paint's @@ -676,16 +717,29 @@ public class PaintBundle { mPos++; } + /** + * Set the style STROKE and/or FILL + * @param style + */ public void setStyle(int style) { mArray[mPos] = STYLE | (style << 16); mPos++; } - public void setShader(int shader, String shaderString) { - mArray[mPos] = SHADER | (shader << 16); + /** + * Set the shader id to use + * @param shaderId + */ + public void setShader(int shaderId) { + mArray[mPos] = SHADER; + mPos++; + mArray[mPos] = shaderId; mPos++; } + /** + * Set the Alpha value + */ public void setAlpha(float alpha) { mArray[mPos] = ALPHA; mPos++; @@ -729,7 +783,6 @@ public class PaintBundle { * destination pixels * (content of the render target). * - * * @param blendmode The blend mode to be installed in the paint */ public void setBlendMode(int blendmode) { @@ -825,5 +878,216 @@ public class PaintBundle { return "null"; } -} + /** + * Check all the floats for Nan(id) floats and call listenTo + * @param context + * @param support + */ + public void registerVars(RemoteContext context, VariableSupport support) { + int i = 0; + while (i < mPos) { + int cmd = mArray[i++]; + int type = cmd & 0xFFFF; + switch (type) { + case STROKE_MITER: + case STROKE_WIDTH: + case ALPHA: + case TEXT_SIZE: + float v = Float.intBitsToFloat(mArray[i++]); + if (Float.isNaN(v)) { + context.listensTo(Utils.idFromNan(v), support); + } + break; + case COLOR_ID: + context.listensTo(mArray[i++], support); + break; + case COLOR: + + case TYPEFACE: + case SHADER: + case COLOR_FILTER: + i++; + break; + case STROKE_JOIN: + case FILTER_BITMAP: + case STROKE_CAP: + case STYLE: + case IMAGE_FILTER_QUALITY: + case BLEND_MODE: + case ANTI_ALIAS: + break; + + case GRADIENT: { + // TODO gradients should be handled correctly + i = callPrintGradient(cmd, mArray, i, new StringBuilder()); + } + } + } + } + + /** + * Update variables if any are float ids + * @param context + */ + public void updateVariables(RemoteContext context) { + if (mOutArray == null) { + mOutArray = Arrays.copyOf(mArray, mArray.length); + } else { + System.arraycopy(mArray, 0, mOutArray, 0, mArray.length); + } + int i = 0; + while (i < mPos) { + int cmd = mArray[i++]; + int type = cmd & 0xFFFF; + switch (type) { + case STROKE_MITER: + case STROKE_WIDTH: + case ALPHA: + case TEXT_SIZE: + mOutArray[i] = fixFloatVar(mArray[i], context); + i++; + break; + case COLOR_ID: + mOutArray[i] = fixColor(mArray[i], context); + i++; + break; + case COLOR: + case TYPEFACE: + case SHADER: + case COLOR_FILTER: + i++; + break; + case STROKE_JOIN: + case FILTER_BITMAP: + case STROKE_CAP: + case STYLE: + case IMAGE_FILTER_QUALITY: + case BLEND_MODE: + case ANTI_ALIAS: + break; + + case GRADIENT: { + // TODO gradients should be handled correctly + i = updateFloatsInGradient(cmd, mOutArray, mArray, i, context); + } + } + } + } + + private int fixFloatVar(int val, RemoteContext context) { + float v = Float.intBitsToFloat(val); + if (Float.isNaN(v)) { + int id = Utils.idFromNan(v); + return Float.floatToRawIntBits(context.getFloat(id)); + } + return val; + } + + private int fixColor(int colorId, RemoteContext context) { + int n = context.getColor(colorId); + return n; + } + + int updateFloatsInGradient(int cmd, int[] out, int[] array, + int i, + RemoteContext context) { + int ret = i; + int type = (cmd >> 16); + switch (type) { + case 0: { + int len = array[ret++]; + if (len > 0) { + for (int j = 0; j < len; j++) { + ret++; + } + } + len = array[ret++]; + + if (len > 0) { + for (int j = 0; j < len; j++) { + out[ret] = fixFloatVar(array[ret], context); + ret++; + } + } + + out[ret] = fixFloatVar(array[ret], context); + ret++; + out[ret] = fixFloatVar(array[ret], context); + ret++; + + // end + out[ret] = fixFloatVar(array[ret], context); + ret++; + out[ret] = fixFloatVar(array[ret], context); + ret++; + ret++; // tileMode + } + + break; + case 1: { + // RadialGradient + int len = array[ret++]; + if (len > 0) { + for (int j = 0; j < len; j++) { + ret++; + } + } + len = array[ret++]; + if (len > 0) { + for (int j = 0; j < len; j++) { + out[ret] = fixFloatVar(array[ret], context); + ret++; + } + } + + + // center + out[ret] = fixFloatVar(array[ret], context); + ret++; + out[ret] = fixFloatVar(array[ret], context); + ret++; + // radius + out[ret] = fixFloatVar(array[ret], context); + ret++; + ret++; // tileMode + + } + + break; + case 2: { + // SweepGradient + int len = array[ret++]; + int[] colors = null; + if (len > 0) { + colors = new int[len]; + for (int j = 0; j < colors.length; j++) { + colors[j] = array[ret++]; + + } + } + len = array[ret++]; + float[] stops = null; + if (len > 0) { + stops = new float[len]; + for (int j = 0; j < stops.length; j++) { + out[ret] = fixFloatVar(array[ret], context); + ret++; + } + } + + // center + out[ret] = fixFloatVar(array[ret], context); + ret++; + out[ret] = fixFloatVar(array[ret], context); + ret++; + } + break; + default: { + System.err.println("gradient type unknown"); + } + } + + return ret; + } +}
\ No newline at end of file diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintChangeAdapter.java b/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintChangeAdapter.java index 994bf6d7e327..28fe63a03c67 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintChangeAdapter.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintChangeAdapter.java @@ -27,7 +27,6 @@ public class PaintChangeAdapter implements PaintChanges { } - @Override public void setStrokeWidth(float width) { @@ -49,7 +48,7 @@ public class PaintChangeAdapter implements PaintChanges { } @Override - public void setShader(int shader, String shaderString) { + public void setShader(int shader) { } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintChanges.java b/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintChanges.java index 87e58ac35930..d5dc3889add3 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintChanges.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintChanges.java @@ -15,9 +15,14 @@ */ package com.android.internal.widget.remotecompose.core.operations.paint; +/** + * Interface to a paint object + * For more details see Android Paint + */ public interface PaintChanges { - + // MASK to be set/cleared + int CLEAR_TEXT_SIZE = 1 << (PaintBundle.TEXT_SIZE - 1); int CLEAR_TEXT_STYLE = 1 << (PaintBundle.TYPEFACE - 1); int CLEAR_COLOR = 1 << (PaintBundle.COLOR - 1); int CLEAR_STROKE_WIDTH = 1 << (PaintBundle.STROKE_WIDTH - 1); @@ -32,21 +37,101 @@ public interface PaintChanges { int CLEAR_COLOR_FILTER = 1 << (PaintBundle.COLOR_FILTER - 1); int VALID_BITS = 0x1FFF; // only the first 13 bit are valid now - + /** + * Set the size of text + * @param size + */ void setTextSize(float size); + + /** + * Set the width of lines + * @param width + */ void setStrokeWidth(float width); + + /** + * Set the color to use + * @param color + */ void setColor(int color); + + /** + * Set the Stroke Cap + * @param cap + */ void setStrokeCap(int cap); + + /** + * Set the Stroke style FILL and/or STROKE + * @param style + */ void setStyle(int style); - void setShader(int shader, String shaderString); + + /** + * Set the id of the shader to use + * @param shader + */ + void setShader(int shader); + + /** + * Set the way image is interpolated + * @param quality + */ void setImageFilterQuality(int quality); + + /** + * Set the alpha to draw under + * @param a + */ void setAlpha(float a); + + /** + * Set the Stroke Miter + * @param miter + */ void setStrokeMiter(float miter); + + /** + * Set the Stroke Join + * @param join + */ void setStrokeJoin(int join); + + /** + * Should bitmaps be interpolated + * @param filter + */ void setFilterBitmap(boolean filter); + + /** + * Set the blend mode can be porterduff + others + * @param mode + */ void setBlendMode(int mode); + + /** + * Set the AntiAlias. Typically true + * Set to off when you need pixilated look (e.g. QR codes) + * @param aa + */ void setAntiAlias(boolean aa); + + /** + * Clear some sub set of the settings + * @param mask + */ void clear(long mask); + + /** + * Set a linear gradient fill + * @param colorsArray + * @param stopsArray + * @param startX + * @param startY + * @param endX + * @param endY + * @param tileMode + */ void setLinearGradient( int[] colorsArray, float[] stopsArray, @@ -57,6 +142,15 @@ public interface PaintChanges { int tileMode ); + /** + * Set a radial gradient fill + * @param colorsArray + * @param stopsArray + * @param centerX + * @param centerY + * @param radius + * @param tileMode + */ void setRadialGradient( int[] colorsArray, float[] stopsArray, @@ -66,6 +160,13 @@ public interface PaintChanges { int tileMode ); + /** + * Set a sweep gradient fill + * @param colorsArray + * @param stopsArray + * @param centerX + * @param centerY + */ void setSweepGradient( int[] colorsArray, float[] stopsArray, @@ -73,9 +174,19 @@ public interface PaintChanges { float centerY ); - + /** + * Set Color filter mod + * @param color + * @param mode + */ void setColorFilter(int color, int mode); + /** + * Set TypeFace 0,1,2 + * TODO above should point to a string to be decoded + * @param fontType + * @param weight + * @param italic + */ void setTypeFace(int fontType, int weight, boolean italic); -} - +}
\ No newline at end of file diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/AnimatedFloatExpression.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/AnimatedFloatExpression.java new file mode 100644 index 000000000000..616048d424ec --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/AnimatedFloatExpression.java @@ -0,0 +1,452 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.widget.remotecompose.core.operations.utilities; + +/** + * high performance floating point expression evaluator used in animation + */ +public class AnimatedFloatExpression { + static IntMap<String> sNames = new IntMap<>(); + public static final int OFFSET = 0x100; + public static final float ADD = asNan(OFFSET + 1); + public static final float SUB = asNan(OFFSET + 2); + public static final float MUL = asNan(OFFSET + 3); + public static final float DIV = asNan(OFFSET + 4); + public static final float MOD = asNan(OFFSET + 5); + public static final float MIN = asNan(OFFSET + 6); + public static final float MAX = asNan(OFFSET + 7); + public static final float POW = asNan(OFFSET + 8); + public static final float SQRT = asNan(OFFSET + 9); + public static final float ABS = asNan(OFFSET + 10); + public static final float SIGN = asNan(OFFSET + 11); + public static final float COPY_SIGN = asNan(OFFSET + 12); + public static final float EXP = asNan(OFFSET + 13); + public static final float FLOOR = asNan(OFFSET + 14); + public static final float LOG = asNan(OFFSET + 15); + public static final float LN = asNan(OFFSET + 16); + public static final float ROUND = asNan(OFFSET + 17); + public static final float SIN = asNan(OFFSET + 18); + public static final float COS = asNan(OFFSET + 19); + public static final float TAN = asNan(OFFSET + 20); + public static final float ASIN = asNan(OFFSET + 21); + public static final float ACOS = asNan(OFFSET + 22); + + public static final float ATAN = asNan(OFFSET + 23); + + public static final float ATAN2 = asNan(OFFSET + 24); + public static final float MAD = asNan(OFFSET + 25); + public static final float IFELSE = asNan(OFFSET + 26); + + public static final float CLAMP = asNan(OFFSET + 27); + public static final float CBRT = asNan(OFFSET + 28); + public static final float DEG = asNan(OFFSET + 29); + public static final float RAD = asNan(OFFSET + 30); + public static final float CEIL = asNan(OFFSET + 31); + + + public static final float LAST_OP = 31; + + + public static final float VAR1 = asNan(OFFSET + 27); + public static final float VAR2 = asNan(OFFSET + 28); + + // TODO CLAMP, CBRT, DEG, RAD, EXPM1, CEIL, FLOOR + private static final float FP_PI = (float) Math.PI; + private static final float FP_TO_RAD = 57.29577951f; // 180/PI + private static final float FP_TO_DEG = 0.01745329252f; // 180/PI + + float[] mStack; + float[] mLocalStack = new float[128]; + float[] mVar; + + /** + * is float a math operator + * @param v + * @return + */ + public static boolean isMathOperator(float v) { + if (Float.isNaN(v)) { + int pos = fromNaN(v); + return pos > OFFSET && pos <= OFFSET + LAST_OP; + } + return false; + } + + interface Op { + int eval(int sp); + } + + /** + * Evaluate a float expression + * @param exp + * @param var + * @return + */ + public float eval(float[] exp, float... var) { + mStack = exp; + mVar = var; + int sp = -1; + for (int i = 0; i < mStack.length; i++) { + float v = mStack[i]; + if (Float.isNaN(v)) { + sp = mOps[fromNaN(v) - OFFSET].eval(sp); + } else { + mStack[++sp] = v; + } + } + return mStack[sp]; + } + + /** + * Evaluate a float expression + * @param exp + * @param len + * @param var + * @return + */ + public float eval(float[] exp, int len, float... var) { + System.arraycopy(exp, 0, mLocalStack, 0, len); + mStack = mLocalStack; + mVar = var; + int sp = -1; + for (int i = 0; i < len; i++) { + float v = mStack[i]; + if (Float.isNaN(v)) { + sp = mOps[fromNaN(v) - OFFSET].eval(sp); + } else { + mStack[++sp] = v; + } + } + return mStack[sp]; + } + + /** + * Evaluate a float expression + * @param exp + * @param var + * @return + */ + public float evalDB(float[] exp, float... var) { + mStack = exp; + mVar = var; + int sp = -1; + for (float v : exp) { + if (Float.isNaN(v)) { + System.out.print(" " + sNames.get((fromNaN(v) - OFFSET))); + sp = mOps[fromNaN(v) - OFFSET].eval(sp); + } else { + System.out.print(" " + v); + mStack[++sp] = v; + } + } + return mStack[sp]; + } + + Op[] mOps = { + null, + (sp) -> { // ADD + mStack[sp - 1] = mStack[sp - 1] + mStack[sp]; + return sp - 1; + }, + (sp) -> { // SUB + mStack[sp - 1] = mStack[sp - 1] - mStack[sp]; + return sp - 1; + }, + (sp) -> { // MUL + mStack[sp - 1] = mStack[sp - 1] * mStack[sp]; + return sp - 1; + }, + (sp) -> { // DIV + mStack[sp - 1] = mStack[sp - 1] / mStack[sp]; + return sp - 1; + }, + (sp) -> { // MOD + mStack[sp - 1] = mStack[sp - 1] % mStack[sp]; + return sp - 1; + }, + (sp) -> { // MIN + mStack[sp - 1] = (float) Math.min(mStack[sp - 1], mStack[sp]); + return sp - 1; + }, + (sp) -> { // MAX + mStack[sp - 1] = (float) Math.max(mStack[sp - 1], mStack[sp]); + return sp - 1; + }, + (sp) -> { // POW + mStack[sp - 1] = (float) Math.pow(mStack[sp - 1], mStack[sp]); + return sp - 1; + }, + (sp) -> { // SQRT + mStack[sp] = (float) Math.sqrt(mStack[sp]); + return sp; + }, + (sp) -> { // ABS + mStack[sp] = (float) Math.abs(mStack[sp]); + return sp; + }, + (sp) -> { // SIGN + mStack[sp] = (float) Math.signum(mStack[sp]); + return sp; + }, + (sp) -> { // copySign + mStack[sp - 1] = (float) Math.copySign(mStack[sp - 1], mStack[sp]); + return sp - 1; + }, + (sp) -> { // EXP + mStack[sp] = (float) Math.exp(mStack[sp]); + return sp; + }, + (sp) -> { // FLOOR + mStack[sp] = (float) Math.floor(mStack[sp]); + return sp; + }, + (sp) -> { // LOG + mStack[sp] = (float) Math.log10(mStack[sp]); + return sp; + }, + (sp) -> { // LN + mStack[sp] = (float) Math.log(mStack[sp]); + return sp; + }, + (sp) -> { // ROUND + mStack[sp] = (float) Math.round(mStack[sp]); + return sp; + }, + (sp) -> { // SIN + mStack[sp] = (float) Math.sin(mStack[sp]); + return sp; + }, + (sp) -> { // COS + mStack[sp] = (float) Math.cos(mStack[sp]); + return sp; + }, + (sp) -> { // TAN + mStack[sp] = (float) Math.tan(mStack[sp]); + return sp; + }, + (sp) -> { // ASIN + mStack[sp] = (float) Math.asin(mStack[sp]); + return sp; + }, + (sp) -> { // ACOS + mStack[sp] = (float) Math.acos(mStack[sp]); + return sp; + }, + (sp) -> { // ATAN + mStack[sp] = (float) Math.atan(mStack[sp]); + return sp; + }, + (sp) -> { // ATAN2 + mStack[sp - 1] = (float) Math.atan2(mStack[sp - 1], mStack[sp]); + return sp - 1; + }, + (sp) -> { // MAD + mStack[sp - 2] = mStack[sp] + mStack[sp - 1] * mStack[sp - 2]; + return sp - 2; + }, + (sp) -> { // Ternary conditional + mStack[sp - 2] = (mStack[sp] > 0) + ? mStack[sp - 1] : mStack[sp - 2]; + return sp - 2; + }, + (sp) -> { // CLAMP(min,max, val) + mStack[sp - 2] = Math.min(Math.max(mStack[sp - 2], mStack[sp]), + mStack[sp - 1]); + return sp - 2; + }, + (sp) -> { // CBRT cuberoot + mStack[sp] = (float) Math.pow(mStack[sp], 1 / 3.); + return sp; + }, + (sp) -> { // DEG + mStack[sp] = mStack[sp] * FP_TO_RAD; + return sp; + }, + (sp) -> { // RAD + mStack[sp] = mStack[sp] * FP_TO_DEG; + return sp; + }, + (sp) -> { // CEIL + mStack[sp] = (float) Math.ceil(mStack[sp]); + return sp; + }, + (sp) -> { // first var = + mStack[sp] = mVar[0]; + return sp; + }, + (sp) -> { // second var y? + mStack[sp] = mVar[1]; + return sp; + }, + (sp) -> { // 3rd var z? + mStack[sp] = mVar[2]; + return sp; + }, + }; + + static { + int k = 0; + sNames.put(k++, "NOP"); + sNames.put(k++, "+"); + sNames.put(k++, "-"); + sNames.put(k++, "*"); + sNames.put(k++, "/"); + sNames.put(k++, "%"); + sNames.put(k++, "min"); + sNames.put(k++, "max"); + sNames.put(k++, "pow"); + sNames.put(k++, "sqrt"); + sNames.put(k++, "abs"); + sNames.put(k++, "sign"); + sNames.put(k++, "copySign"); + sNames.put(k++, "exp"); + sNames.put(k++, "floor"); + sNames.put(k++, "log"); + sNames.put(k++, "ln"); + sNames.put(k++, "round"); + sNames.put(k++, "sin"); + sNames.put(k++, "cos"); + sNames.put(k++, "tan"); + sNames.put(k++, "asin"); + sNames.put(k++, "acos"); + sNames.put(k++, "atan"); + sNames.put(k++, "atan2"); + sNames.put(k++, "mad"); + sNames.put(k++, "ifElse"); + sNames.put(k++, "clamp"); + sNames.put(k++, "cbrt"); + sNames.put(k++, "deg"); + sNames.put(k++, "rad"); + sNames.put(k++, "ceil"); + sNames.put(k++, "a[0]"); + sNames.put(k++, "a[1]"); + sNames.put(k++, "a[2]"); + } + + /** + * given a float command return its math name (e.g sin, cos etc.) + * @param f + * @return + */ + public static String toMathName(float f) { + int id = fromNaN(f) - OFFSET; + return sNames.get(id); + } + + /** + * Convert an expression encoded as an array of floats int ot a string + * @param exp + * @param labels + * @return + */ + public static String toString(float[] exp, String[] labels) { + StringBuilder s = new StringBuilder(); + for (int i = 0; i < exp.length; i++) { + float v = exp[i]; + if (Float.isNaN(v)) { + if (isMathOperator(v)) { + s.append(toMathName(v)); + } else { + s.append("["); + s.append(fromNaN(v)); + s.append("]"); + } + } else { + if (labels[i] != null) { + s.append(labels[i]); + } + s.append(v); + } + s.append(" "); + } + return s.toString(); + } + + static String toString(float[] exp, int sp) { + String[] str = new String[exp.length]; + if (Float.isNaN(exp[sp])) { + int id = fromNaN(exp[sp]) - OFFSET; + switch (NO_OF_OPS[id]) { + case -1: + return "nop"; + case 1: + return sNames.get(id) + "(" + toString(exp, sp + 1) + ") "; + case 2: + if (infix(id)) { + return "(" + toString(exp, sp + 1) + + sNames.get(id) + " " + + toString(exp, sp + 2) + ") "; + } else { + return sNames.get(id) + "(" + + toString(exp, sp + 1) + ", " + + toString(exp, sp + 2) + ")"; + } + case 3: + if (infix(id)) { + return "((" + toString(exp, sp + 1) + ") ? " + + toString(exp, sp + 2) + ":" + + toString(exp, sp + 3) + ")"; + } else { + return sNames.get(id) + + "(" + toString(exp, sp + 1) + + ", " + toString(exp, sp + 2) + + ", " + toString(exp, sp + 3) + ")"; + } + } + } + return Float.toString(exp[sp]); + } + + static final int[] NO_OF_OPS = { + -1, // no op + 2, 2, 2, 2, 2, // + - * / % + 2, 2, 2, // min max, power + 1, 1, 1, 1, 1, 1, 1, 1, //sqrt,abs,CopySign,exp,floor,log,ln + 1, 1, 1, 1, 1, 1, 1, 2, // round,sin,cos,tan,asin,acos,atan,atan2 + 3, 3, 3, 1, 1, 1, 1, + 0, 0, 0 // mad, ?:, + // a[0],a[1],a[2] + }; + + /** + * to be used by parser to determine if command is infix + * @param n + * @return + */ + static boolean infix(int n) { + return ((n < 6) || (n == 25) || (n == 26)); + } + + /** + * Convert an id into a NaN object + * @param v + * @return + */ + public static float asNan(int v) { + return Float.intBitsToFloat(v | -0x800000); + } + + /** + * Get ID from a NaN float + * @param v + * @return + */ + public static int fromNaN(float v) { + int b = Float.floatToRawIntBits(v); + return b & 0xFFFFF; + } + +} diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/ColorUtils.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/ColorUtils.java new file mode 100644 index 000000000000..0ea28a8bb900 --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/ColorUtils.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.widget.remotecompose.core.operations.utilities; + +/** + * These are tools to use long Color as variables + * long colors are stored a 0xXXXXXXXX XXXXXX?? + * in SRGB the colors are stored 0xAARRGGBB,00000000 + * SRGB color sapce is color space 0 + * Our Color will use color float with a + * Current android supports + * SRGB, LINEAR_SRGB, EXTENDED_SRGB, LINEAR_EXTENDED_SRGB, BT709, BT2020, + * DCI_P3, DISPLAY_P3, NTSC_1953, SMPTE_C, ADOBE_RGB, PRO_PHOTO_RGB, ACES, + * ACESCG, CIE_XYZ, CIE_LAB, BT2020_HLG, BT2020_PQ 0..17 respectively + * + * Our color space will be 62 (MAX_ID-1). (0x3E) + * Storing the default value in SRGB format and having the + * id of the color between the ARGB values and the 62 i.e. + * 0xAARRGGBB 00 00 00 3E + * + */ +public class ColorUtils { + public static int RC_COLOR = 62; + + long packRCColor(int defaultARGB, int id) { + long l = defaultARGB; + return (l << 32) | id << 8 | RC_COLOR; + } + + boolean isRCColor(long color) { + return ((color & 0x3F) == 62); + } + + int getID(long color) { + if (isRCColor(color)) { + return (int) ((color & 0xFFFFFF00) >> 8); + } + return -1; + } + + /** + * get default color from long color + * @param color + * @return + */ + public int getDefaultColor(long color) { + if (isRCColor(color)) { + return (int) (color >> 32); + } + if (((color & 0xFF) == 0)) { + return (int) (color >> 32); + } + return 0; + } +} diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntMap.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntMap.java index 8051ef1ab37c..0512fa6be710 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntMap.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntMap.java @@ -50,7 +50,6 @@ public class IntMap<T> { return insert(key, value); } - public T get(int key) { int index = findKey(key); if (index == -1) { diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/NanMap.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/NanMap.java new file mode 100644 index 000000000000..f4cd504d650f --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/NanMap.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.widget.remotecompose.core.operations.utilities; + +import com.android.internal.widget.remotecompose.core.operations.Utils; + +/** + * This defines the major id maps and ranges used by remote compose + * Generally ids ranging from 0 ... FFF (4095) are for ids + * 0x1000-0x1100 are used for path operations in PathData + * 0x1100-0x1200 are used for math operations in Animated float + * 0x + */ +public class NanMap { + + public static final int MOVE = 0x1000; + public static final int LINE = 0x1001; + public static final int QUADRATIC = 0x1002; + public static final int CONIC = 0x1003; + public static final int CUBIC = 0x1004; + public static final int CLOSE = 0x1005; + public static final int DONE = 0x1006; + public static final float MOVE_NAN = Utils.asNan(MOVE); + public static final float LINE_NAN = Utils.asNan(LINE); + public static final float QUADRATIC_NAN = Utils.asNan(QUADRATIC); + public static final float CONIC_NAN = Utils.asNan(CONIC); + public static final float CUBIC_NAN = Utils.asNan(CUBIC); + public static final float CLOSE_NAN = Utils.asNan(CLOSE); + public static final float DONE_NAN = Utils.asNan(DONE); + + /** + * + */ + public static final float ADD = asNan(0x1100); + public static final float SUB = asNan(0x1101); + public static final float MUL = asNan(0x1102); + public static final float DIV = asNan(0x1103); + public static final float MOD = asNan(0x1104); + public static final float MIN = asNan(0x1105); + public static final float MAX = asNan(0x1106); + public static final float POW = asNan(0x1107); + + + /** + * Get ID from Nan float + * @param v + * @return + */ + public static int fromNaN(float v) { + int b = Float.floatToRawIntBits(v); + return b & 0xFFFFF; + } + + /** + * Given id return as a Nan float + * @param v + * @return + */ + public static float asNan(int v) { + return Float.intBitsToFloat(v | 0xFF800000); + } +} diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/StringUtils.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/StringUtils.java new file mode 100644 index 000000000000..8dd5405ddf12 --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/StringUtils.java @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.widget.remotecompose.core.operations.utilities; + +import java.util.Arrays; + +/** + * Utilities for string manipulation + */ +public class StringUtils { + /** + * Converts a float into a string. + * Providing a defined number of characters before and after the + * decimal point. + * + * @param value The value to convert to string + * @param beforeDecimalPoint digits before the decimal point + * @param afterDecimalPoint digits after the decimal point + * @param pre character to pad width 0 = no pad typically ' ' or '0' + * @param post character to pad width 0 = no pad typically ' ' or '0' + * @return + */ + public static String floatToString(float value, + int beforeDecimalPoint, + int afterDecimalPoint, + char pre, char post) { + + int integerPart = (int) value; + float fractionalPart = value % 1; + + // Convert integer part to string and pad with spaces + String integerPartString = String.valueOf(integerPart); + int iLen = integerPartString.length(); + if (iLen < beforeDecimalPoint) { + int spacesToPad = beforeDecimalPoint - iLen; + if (pre != 0) { + char[] pad = new char[spacesToPad]; + Arrays.fill(pad, pre); + integerPartString = new String(pad) + integerPartString; + } + + + } else if (iLen > beforeDecimalPoint) { + integerPartString = integerPartString.substring(iLen - beforeDecimalPoint); + } + if (afterDecimalPoint == 0) { + return integerPartString; + } + // Convert fractional part to string and pad with zeros + + for (int i = 0; i < afterDecimalPoint; i++) { + fractionalPart *= 10; + } + + fractionalPart = Math.round(fractionalPart); + + for (int i = 0; i < afterDecimalPoint; i++) { + fractionalPart *= .1; + } + + String fact = Float.toString(fractionalPart); + fact = fact.substring(2, Math.min(fact.length(), afterDecimalPoint + 2)); + int trim = fact.length(); + for (int i = fact.length() - 1; i >= 0; i--) { + if (fact.charAt(i) != '0') { + break; + } + trim--; + } + if (trim != fact.length()) { + fact = fact.substring(0, trim); + } + int len = fact.length(); + if (post != 0 && len < afterDecimalPoint) { + char[] c = new char[afterDecimalPoint - len]; + Arrays.fill(c, post); + fact = fact + new String(c); + } + + return integerPartString + "." + fact; + } + +} diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/BounceCurve.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/BounceCurve.java new file mode 100644 index 000000000000..c3cd5ae9c79d --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/BounceCurve.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.widget.remotecompose.core.operations.utilities.easing; + +/** + * Provide a specific bouncing easing function + */ +public class BounceCurve extends Easing { + private static final float N1 = 7.5625f; + private static final float D1 = 2.75f; + + BounceCurve(int type) { + mType = type; + } + + @Override + public float get(float x) { + float t = x; + if (t < 0) { + return 0f; + } + if (t < 1 / D1) { + return 1 / (1 + 1 / D1) * (N1 * t * t + t); + } else if (t < 2 / D1) { + t -= 1.5f / D1; + return N1 * t * t + 0.75f; + } else if (t < 2.5 / D1) { + t -= 2.25f / D1; + return N1 * t * t + 0.9375f; + } else if (t <= 1) { + t -= 2.625f / D1; + return N1 * t * t + 0.984375f; + } + return 1f; + } + + @Override + public float getDiff(float x) { + if (x < 0) { + return 0f; + } + if (x < 1 / D1) { + return 2 * N1 * x / (1 + 1 / D1) + 1 / (1 + 1 / D1); + } else if (x < 2 / D1) { + return 2 * N1 * (x - 1.5f / D1); + } else if (x < 2.5 / D1) { + return 2 * N1 * (x - 2.25f / D1); + } else if (x <= 1) { + return 2 * N1 * (x - 2.625f / D1); + } + return 0f; + } + +} diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/CubicEasing.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/CubicEasing.java new file mode 100644 index 000000000000..fd1ee036e475 --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/CubicEasing.java @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.widget.remotecompose.core.operations.utilities.easing; + +class CubicEasing extends Easing { + float mType = 0; + float mX1 = 0f; + float mY1 = 0f; + float mX2 = 0f; + float mY2 = 0f; + + private static final float[] STANDARD = {0.4f, 0.0f, 0.2f, 1f}; + private static final float[] ACCELERATE = {0.4f, 0.05f, 0.8f, 0.7f}; + private static final float[] DECELERATE = {0.0f, 0.0f, 0.2f, 0.95f}; + private static final float[] LINEAR = {1f, 1f, 0f, 0f}; + private static final float[] ANTICIPATE = {0.36f, 0f, 0.66f, -0.56f}; + private static final float[] OVERSHOOT = {0.34f, 1.56f, 0.64f, 1f}; + + CubicEasing(int type) { + mType = type; + config(type); + } + + CubicEasing(float x1, float y1, float x2, float y2) { + setup(x1, y1, x2, y2); + } + + public void config(int type) { + + switch (type) { + case CUBIC_STANDARD: + setup(STANDARD); + break; + case CUBIC_ACCELERATE: + setup(ACCELERATE); + break; + case CUBIC_DECELERATE: + setup(DECELERATE); + break; + case CUBIC_LINEAR: + setup(LINEAR); + break; + case CUBIC_ANTICIPATE: + setup(ANTICIPATE); + break; + case CUBIC_OVERSHOOT: + setup(OVERSHOOT); + break; + } + mType = type; + } + + void setup(float[] values) { + setup(values[0], values[1], values[2], values[3]); + } + + void setup(float x1, float y1, float x2, float y2) { + mX1 = x1; + mY1 = y1; + mX2 = x2; + mY2 = y2; + } + + private float getX(float t) { + float t1 = 1 - t; + // no need for because start at 0,0 float f0 = (1 - t) * (1 - t) * (1 - t) + float f1 = 3 * t1 * t1 * t; + float f2 = 3 * t1 * t * t; + float f3 = t * t * t; + return mX1 * f1 + mX2 * f2 + f3; + } + + private float getY(float t) { + float t1 = 1 - t; + // no need for testing because start at 0,0 float f0 = (1 - t) * (1 - t) * (1 - t) + float f1 = 3 * t1 * t1 * t; + float f2 = 3 * t1 * t * t; + float f3 = t * t * t; + return mY1 * f1 + mY2 * f2 + f3; + } + + private float getDiffX(float t) { + float t1 = 1 - t; + return 3 * t1 * t1 * mX1 + 6 * t1 * t * (mX2 - mX1) + 3 * t * t * (1 - mX2); + } + + private float getDiffY(float t) { + float t1 = 1 - t; + return 3 * t1 * t1 * mY1 + 6 * t1 * t * (mY2 - mY1) + 3 * t * t * (1 - mY2); + } + + /** + * binary search for the region and linear interpolate the answer + */ + public float getDiff(float x) { + float t = 0.5f; + float range = 0.5f; + while (range > D_ERROR) { + float tx = getX(t); + range *= 0.5; + if (tx < x) { + t += range; + } else { + t -= range; + } + } + float x1 = getX(t - range); + float x2 = getX(t + range); + float y1 = getY(t - range); + float y2 = getY(t + range); + return (y2 - y1) / (x2 - x1); + } + + /** + * binary search for the region and linear interpolate the answer + */ + public float get(float x) { + if (x <= 0.0f) { + return 0f; + } + if (x >= 1.0f) { + return 1.0f; + } + float t = 0.5f; + float range = 0.5f; + while (range > ERROR) { + float tx = getX(t); + range *= 0.5f; + if (tx < x) { + t += range; + } else { + t -= range; + } + } + float x1 = getX(t - range); + float x2 = getX(t + range); + float y1 = getY(t - range); + float y2 = getY(t + range); + return (y2 - y1) * (x - x1) / (x2 - x1) + y1; + } + + private static final float ERROR = 0.01f; + private static final float D_ERROR = 0.0001f; +} diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/Easing.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/Easing.java new file mode 100644 index 000000000000..4ed955069d78 --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/Easing.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.widget.remotecompose.core.operations.utilities.easing; + +/** + * The standard interface to Easing functions + */ +public abstract class Easing { + int mType; + /** + * get the value at point x + */ + public abstract float get(float x); + + /** + * get the slope of the easing function at at x + */ + public abstract float getDiff(float x); + + public int getType() { + return mType; + } + + public static final int CUBIC_STANDARD = 1; + public static final int CUBIC_ACCELERATE = 2; + public static final int CUBIC_DECELERATE = 3; + public static final int CUBIC_LINEAR = 4; + public static final int CUBIC_ANTICIPATE = 5; + public static final int CUBIC_OVERSHOOT = 6; + public static final int CUBIC_CUSTOM = 11; + public static final int SPLINE_CUSTOM = 12; + public static final int EASE_OUT_BOUNCE = 13; + public static final int EASE_OUT_ELASTIC = 14; + +} diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/ElasticOutCurve.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/ElasticOutCurve.java new file mode 100644 index 000000000000..e26958302e3c --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/ElasticOutCurve.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.widget.remotecompose.core.operations.utilities.easing; + +/** + * Provide a bouncing Easing function + */ +public class ElasticOutCurve extends Easing { + private static final float F_PI = (float) Math.PI; + private static final float C4 = 2 * F_PI / 3; + private static final float TWENTY_PI = 20 * F_PI; + private static final float LOG_8 = (float) Math.log(8.0f); + + @Override + public float get(float x) { + if (x <= 0) { + return 0.0f; + } + if (x >= 1) { + return 1.0f; + } else + return (float) (Math.pow(2.0f, -10 * x) + * Math.sin((x * 10 - 0.75f) * C4) + 1); + } + + @Override + public float getDiff(float x) { + if (x < 0 || x > 1) { + return 0.0f; + } else + return (float) ((5 * Math.pow(2.0f, (1 - 10 * x)) + * (LOG_8 * Math.cos(TWENTY_PI * x / 3) + 2 + * F_PI * Math.sin(TWENTY_PI * x / 3)) + / 3)); + } +} diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/FloatAnimation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/FloatAnimation.java new file mode 100644 index 000000000000..4f484de1045e --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/FloatAnimation.java @@ -0,0 +1,259 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.widget.remotecompose.core.operations.utilities.easing; + +/** + * Support Animation of the FloatExpression + */ +public class FloatAnimation extends Easing { + float[] mSpec; + // mSpec[0] = duration + // int(mSpec[1]) = num_of_param << 16 | type + // mSpec[2..1+num_of_param] params + // mSpec[2+num_of_param] starting Value + Easing mEasingCurve; + private int mType = CUBIC_STANDARD; + private float mDuration = 1; + private float mWrap = Float.NaN; + private float mInitialValue = Float.NaN; + private float mTargetValue = Float.NaN; + private float mScale = 1; + float mOffset = 0; + + @Override + public String toString() { + + String str = "type " + mType; + if (!Float.isNaN(mInitialValue)) { + str += " " + mInitialValue; + } + if (!Float.isNaN(mTargetValue)) { + str += " -> " + mTargetValue; + } + if (!Float.isNaN(mWrap)) { + str += " % " + mWrap; + } + + return str; + } + + public FloatAnimation() { + } + + public FloatAnimation(float... description) { + setAnimationDescription(description); + } + + public FloatAnimation(int type, + float duration, + float[] description, + float initialValue, + float wrap) { + setAnimationDescription(packToFloatArray(duration, + type, description, initialValue, wrap)); + } + + /** + * packs spec into a float array + * + * @param duration + * @param type + * @param spec + * @param initialValue + * @return + */ + public static float[] packToFloatArray(float duration, + int type, + float[] spec, + float initialValue, + float wrap) { + int count = 0; + + if (!Float.isNaN(initialValue)) { + count++; + } + if (spec != null) { + count++; + } + if (spec != null || type != CUBIC_STANDARD) { + count++; + count += (spec == null) ? 0 : spec.length; + } + if (duration != 1 || count > 0) { + count++; + } + if (!Float.isNaN(initialValue)) { + count++; + } + if (!Float.isNaN(wrap)) { + count++; + } + float[] ret = new float[count]; + int pos = 0; + int specLen = (spec == null) ? 0 : spec.length; + + if (ret.length > 0) { + ret[pos++] = duration; + + } + if (ret.length > 1) { + int wrapBit = (Float.isNaN(wrap)) ? 0 : 1; + int initBit = (Float.isNaN(initialValue)) ? 0 : 2; + int bits = type | ((wrapBit | initBit) << 8); + ret[pos++] = Float.intBitsToFloat(specLen << 16 | bits); + } + + if (specLen > 0) { + System.arraycopy(spec, 0, ret, pos, spec.length); + pos += spec.length; + } + if (!Float.isNaN(initialValue)) { + ret[pos++] = initialValue; + } + if (!Float.isNaN(wrap)) { + ret[pos] = wrap; + } + return ret; + } + + /** + * Create an animation based on a float encoding of the animation + * @param description + */ + public void setAnimationDescription(float[] description) { + mSpec = description; + mDuration = (mSpec.length == 0) ? 1 : mSpec[0]; + int len = 0; + if (mSpec.length > 1) { + int num_type = Float.floatToRawIntBits(mSpec[1]); + mType = num_type & 0xFF; + boolean wrap = ((num_type >> 8) & 0x1) > 0; + boolean init = ((num_type >> 8) & 0x2) > 0; + len = (num_type >> 16) & 0xFFFF; + int off = 2 + len; + if (init) { + mInitialValue = mSpec[off++]; + } + if (wrap) { + mWrap = mSpec[off]; + } + } + create(mType, description, 2, len); + } + + private void create(int type, float[] params, int offset, int len) { + switch (type) { + case CUBIC_STANDARD: + case CUBIC_ACCELERATE: + case CUBIC_DECELERATE: + case CUBIC_LINEAR: + case CUBIC_ANTICIPATE: + case CUBIC_OVERSHOOT: + mEasingCurve = new CubicEasing(type); + break; + case CUBIC_CUSTOM: + mEasingCurve = new CubicEasing(params[offset + 0], + params[offset + 1], + params[offset + 2], + params[offset + 3] + ); + break; + case EASE_OUT_BOUNCE: + mEasingCurve = new BounceCurve(type); + break; + case EASE_OUT_ELASTIC: + mEasingCurve = new ElasticOutCurve(); + break; + case SPLINE_CUSTOM: + mEasingCurve = new StepCurve(params, offset, len); + break; + } + } + + /** + * Get the duration the interpolate is to take + * @return duration in seconds + */ + public float getDuration() { + return mDuration; + } + + /** + * Set the initial Value + * @param value + */ + public void setInitialValue(float value) { + + if (Float.isNaN(mWrap)) { + mInitialValue = value; + } else { + mInitialValue = value % mWrap; + } + setScaleOffset(); + } + + /** + * Set the target value to interpolate to + * @param value + */ + public void setTargetValue(float value) { + if (Float.isNaN(mWrap)) { + mTargetValue = value; + } else { + if (Math.abs((value % mWrap) + mWrap - mInitialValue) + < Math.abs((value % mWrap) - mInitialValue)) { + mTargetValue = (value % mWrap) + mWrap; + + } else { + mTargetValue = value % mWrap; + } + } + setScaleOffset(); + } + + public float getTargetValue() { + return mTargetValue; + } + + private void setScaleOffset() { + if (!Float.isNaN(mInitialValue) && !Float.isNaN(mTargetValue)) { + mScale = (mTargetValue - mInitialValue); + mOffset = mInitialValue; + } else { + mScale = 1; + mOffset = 0; + } + } + + /** + * get the value at time t in seconds since start + */ + public float get(float t) { + return mEasingCurve.get(t / mDuration) + * (mTargetValue - mInitialValue) + mInitialValue; + } + + /** + * get the slope of the easing function at at x + */ + public float getDiff(float t) { + return mEasingCurve.getDiff(t / mDuration) * (mTargetValue - mInitialValue); + } + + public float getInitialValue() { + return mInitialValue; + } +} diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/GeneralEasing.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/GeneralEasing.java new file mode 100644 index 000000000000..693deafc5b00 --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/GeneralEasing.java @@ -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.internal.widget.remotecompose.core.operations.utilities.easing; + +/** + * Provides and interface to create easing functions + */ +public class GeneralEasing extends Easing{ + float[] mEasingData = new float[0]; + Easing mEasingCurve = new CubicEasing(CUBIC_STANDARD); + + /** + * Set the curve based on the float encoding of it + * @param data + */ + public void setCurveSpecification(float[] data) { + mEasingData = data; + createEngine(); + } + + public float[] getCurveSpecification() { + return mEasingData; + } + + void createEngine() { + int type = Float.floatToRawIntBits(mEasingData[0]); + switch (type) { + case CUBIC_STANDARD: + case CUBIC_ACCELERATE: + case CUBIC_DECELERATE: + case CUBIC_LINEAR: + case CUBIC_ANTICIPATE: + case CUBIC_OVERSHOOT: + mEasingCurve = new CubicEasing(type); + break; + case CUBIC_CUSTOM: + mEasingCurve = new CubicEasing(mEasingData[1], + mEasingData[2], + mEasingData[3], + mEasingData[5] + ); + break; + case EASE_OUT_BOUNCE: + mEasingCurve = new BounceCurve(type); + break; + } + } + + /** + * get the value at point x + */ + public float get(float x) { + return mEasingCurve.get(x); + } + + /** + * get the slope of the easing function at at x + */ + public float getDiff(float x) { + return mEasingCurve.getDiff(x); + } + + public int getType() { + return mEasingCurve.getType(); + } + + +} diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/MonotonicCurveFit.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/MonotonicCurveFit.java new file mode 100644 index 000000000000..23930b92a282 --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/MonotonicCurveFit.java @@ -0,0 +1,370 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.widget.remotecompose.core.operations.utilities.easing; + +import java.util.Arrays; + +/** + * This performs a spline interpolation in multiple dimensions + * + * + */ +public class MonotonicCurveFit { + private static final String TAG = "MonotonicCurveFit"; + private double[] mT; + private double[][] mY; + private double[][] mTangent; + private boolean mExtrapolate = true; + double[] mSlopeTemp; + + /** + * create a collection of curves + * @param time the point along the curve + * @param y the parameter at those points + */ + public MonotonicCurveFit(double[] time, double[][] y) { + final int n = time.length; + final int dim = y[0].length; + mSlopeTemp = new double[dim]; + double[][] slope = new double[n - 1][dim]; // could optimize this out + double[][] tangent = new double[n][dim]; + for (int j = 0; j < dim; j++) { + for (int i = 0; i < n - 1; i++) { + double dt = time[i + 1] - time[i]; + slope[i][j] = (y[i + 1][j] - y[i][j]) / dt; + if (i == 0) { + tangent[i][j] = slope[i][j]; + } else { + tangent[i][j] = (slope[i - 1][j] + slope[i][j]) * 0.5f; + } + } + tangent[n - 1][j] = slope[n - 2][j]; + } + + for (int i = 0; i < n - 1; i++) { + for (int j = 0; j < dim; j++) { + if (slope[i][j] == 0.) { + tangent[i][j] = 0.; + tangent[i + 1][j] = 0.; + } else { + double a = tangent[i][j] / slope[i][j]; + double b = tangent[i + 1][j] / slope[i][j]; + double h = Math.hypot(a, b); + if (h > 9.0) { + double t = 3. / h; + tangent[i][j] = t * a * slope[i][j]; + tangent[i + 1][j] = t * b * slope[i][j]; + } + } + } + } + mT = time; + mY = y; + mTangent = tangent; + } + + /** + * Get the position of all curves at time t + * @param t + * @param v + */ + public void getPos(double t, double[] v) { + final int n = mT.length; + final int dim = mY[0].length; + if (mExtrapolate) { + if (t <= mT[0]) { + getSlope(mT[0], mSlopeTemp); + for (int j = 0; j < dim; j++) { + v[j] = mY[0][j] + (t - mT[0]) * mSlopeTemp[j]; + } + return; + } + if (t >= mT[n - 1]) { + getSlope(mT[n - 1], mSlopeTemp); + for (int j = 0; j < dim; j++) { + v[j] = mY[n - 1][j] + (t - mT[n - 1]) * mSlopeTemp[j]; + } + return; + } + } else { + if (t <= mT[0]) { + for (int j = 0; j < dim; j++) { + v[j] = mY[0][j]; + } + return; + } + if (t >= mT[n - 1]) { + for (int j = 0; j < dim; j++) { + v[j] = mY[n - 1][j]; + } + return; + } + } + + for (int i = 0; i < n - 1; i++) { + if (t == mT[i]) { + for (int j = 0; j < dim; j++) { + v[j] = mY[i][j]; + } + } + if (t < mT[i + 1]) { + double h = mT[i + 1] - mT[i]; + double x = (t - mT[i]) / h; + for (int j = 0; j < dim; j++) { + double y1 = mY[i][j]; + double y2 = mY[i + 1][j]; + double t1 = mTangent[i][j]; + double t2 = mTangent[i + 1][j]; + v[j] = interpolate(h, x, y1, y2, t1, t2); + } + return; + } + } + } + + /** + * Get the position of all curves at time t + * @param t + * @param v + */ + public void getPos(double t, float[] v) { + final int n = mT.length; + final int dim = mY[0].length; + if (mExtrapolate) { + if (t <= mT[0]) { + getSlope(mT[0], mSlopeTemp); + for (int j = 0; j < dim; j++) { + v[j] = (float) (mY[0][j] + (t - mT[0]) * mSlopeTemp[j]); + } + return; + } + if (t >= mT[n - 1]) { + getSlope(mT[n - 1], mSlopeTemp); + for (int j = 0; j < dim; j++) { + v[j] = (float) (mY[n - 1][j] + (t - mT[n - 1]) * mSlopeTemp[j]); + } + return; + } + } else { + if (t <= mT[0]) { + for (int j = 0; j < dim; j++) { + v[j] = (float) mY[0][j]; + } + return; + } + if (t >= mT[n - 1]) { + for (int j = 0; j < dim; j++) { + v[j] = (float) mY[n - 1][j]; + } + return; + } + } + + for (int i = 0; i < n - 1; i++) { + if (t == mT[i]) { + for (int j = 0; j < dim; j++) { + v[j] = (float) mY[i][j]; + } + } + if (t < mT[i + 1]) { + double h = mT[i + 1] - mT[i]; + double x = (t - mT[i]) / h; + for (int j = 0; j < dim; j++) { + double y1 = mY[i][j]; + double y2 = mY[i + 1][j]; + double t1 = mTangent[i][j]; + double t2 = mTangent[i + 1][j]; + v[j] = (float) interpolate(h, x, y1, y2, t1, t2); + } + return; + } + } + } + + /** + * Get the position of the jth curve at time t + * @param t + * @param j + * @return + */ + public double getPos(double t, int j) { + final int n = mT.length; + if (mExtrapolate) { + if (t <= mT[0]) { + return mY[0][j] + (t - mT[0]) * getSlope(mT[0], j); + } + if (t >= mT[n - 1]) { + return mY[n - 1][j] + (t - mT[n - 1]) * getSlope(mT[n - 1], j); + } + } else { + if (t <= mT[0]) { + return mY[0][j]; + } + if (t >= mT[n - 1]) { + return mY[n - 1][j]; + } + } + + for (int i = 0; i < n - 1; i++) { + if (t == mT[i]) { + return mY[i][j]; + } + if (t < mT[i + 1]) { + double h = mT[i + 1] - mT[i]; + double x = (t - mT[i]) / h; + double y1 = mY[i][j]; + double y2 = mY[i + 1][j]; + double t1 = mTangent[i][j]; + double t2 = mTangent[i + 1][j]; + return interpolate(h, x, y1, y2, t1, t2); + + } + } + return 0; // should never reach here + } + + /** + * Get the slope of all the curves at position t + * @param t + * @param v + */ + public void getSlope(double t, double[] v) { + final int n = mT.length; + int dim = mY[0].length; + if (t <= mT[0]) { + t = mT[0]; + } else if (t >= mT[n - 1]) { + t = mT[n - 1]; + } + + for (int i = 0; i < n - 1; i++) { + if (t <= mT[i + 1]) { + double h = mT[i + 1] - mT[i]; + double x = (t - mT[i]) / h; + for (int j = 0; j < dim; j++) { + double y1 = mY[i][j]; + double y2 = mY[i + 1][j]; + double t1 = mTangent[i][j]; + double t2 = mTangent[i + 1][j]; + v[j] = diff(h, x, y1, y2, t1, t2) / h; + } + break; + } + } + return; + } + + /** + * Get the slope of the j curve at position t + * @param t + * @param j + * @return + */ + public double getSlope(double t, int j) { + final int n = mT.length; + + if (t < mT[0]) { + t = mT[0]; + } else if (t >= mT[n - 1]) { + t = mT[n - 1]; + } + for (int i = 0; i < n - 1; i++) { + if (t <= mT[i + 1]) { + double h = mT[i + 1] - mT[i]; + double x = (t - mT[i]) / h; + double y1 = mY[i][j]; + double y2 = mY[i + 1][j]; + double t1 = mTangent[i][j]; + double t2 = mTangent[i + 1][j]; + return diff(h, x, y1, y2, t1, t2) / h; + } + } + return 0; // should never reach here + } + + public double[] getTimePoints() { + return mT; + } + + /** + * Cubic Hermite spline + */ + private static double interpolate(double h, + double x, + double y1, + double y2, + double t1, + double t2) { + double x2 = x * x; + double x3 = x2 * x; + return -2 * x3 * y2 + 3 * x2 * y2 + 2 * x3 * y1 - 3 * x2 * y1 + y1 + + h * t2 * x3 + h * t1 * x3 - h * t2 * x2 - 2 * h * t1 * x2 + + h * t1 * x; + } + + /** + * Cubic Hermite spline slope differentiated + */ + private static double diff(double h, double x, double y1, double y2, double t1, double t2) { + double x2 = x * x; + return -6 * x2 * y2 + 6 * x * y2 + 6 * x2 * y1 - 6 * x * y1 + 3 * h * t2 * x2 + + 3 * h * t1 * x2 - 2 * h * t2 * x - 4 * h * t1 * x + h * t1; + } + + /** + * This builds a monotonic spline to be used as a wave function + */ + public static MonotonicCurveFit buildWave(String configString) { + // done this way for efficiency + String str = configString; + double[] values = new double[str.length() / 2]; + int start = configString.indexOf('(') + 1; + int off1 = configString.indexOf(',', start); + int count = 0; + while (off1 != -1) { + String tmp = configString.substring(start, off1).trim(); + values[count++] = Double.parseDouble(tmp); + off1 = configString.indexOf(',', start = off1 + 1); + } + off1 = configString.indexOf(')', start); + String tmp = configString.substring(start, off1).trim(); + values[count++] = Double.parseDouble(tmp); + + return buildWave(Arrays.copyOf(values, count)); + } + + private static MonotonicCurveFit buildWave(double[] values) { + int length = values.length * 3 - 2; + int len = values.length - 1; + double gap = 1.0 / len; + double[][] points = new double[length][1]; + double[] time = new double[length]; + for (int i = 0; i < values.length; i++) { + double v = values[i]; + points[i + len][0] = v; + time[i + len] = i * gap; + if (i > 0) { + points[i + len * 2][0] = v + 1; + time[i + len * 2] = i * gap + 1; + + points[i - 1][0] = v - 1 - gap; + time[i - 1] = i * gap + -1 - gap; + } + } + + return new MonotonicCurveFit(time, points); + } +} diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/StepCurve.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/StepCurve.java new file mode 100644 index 000000000000..6ed6548405d2 --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/StepCurve.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.widget.remotecompose.core.operations.utilities.easing; + + +/** + * This class translates a series of floating point values into a continuous + * curve for use in an easing function including quantize functions + * it is used with the "spline(0,0.3,0.3,0.5,...0.9,1)" it should start at 0 and end at one 1 + */ +public class StepCurve extends Easing { + private static final boolean DEBUG = false; + MonotonicCurveFit mCurveFit; + + public StepCurve(float[] params, int offset, int len) { + mCurveFit = genSpline(params, offset, len); + } + + private static MonotonicCurveFit genSpline(float[] values, int off, int arrayLen) { + int length = arrayLen * 3 - 2; + int len = arrayLen - 1; + double gap = 1.0 / len; + double[][] points = new double[length][1]; + double[] time = new double[length]; + for (int i = 0; i < arrayLen; i++) { + double v = values[i + off]; + points[i + len][0] = v; + time[i + len] = i * gap; + if (i > 0) { + points[i + len * 2][0] = v + 1; + time[i + len * 2] = i * gap + 1; + + points[i - 1][0] = v - 1 - gap; + time[i - 1] = i * gap + -1 - gap; + } + } + + MonotonicCurveFit ms = new MonotonicCurveFit(time, points); + + return ms; + } + + @Override + public float getDiff(float x) { + return (float) mCurveFit.getSlope(x, 0); + } + + + @Override + public float get(float x) { + return (float) mCurveFit.getPos(x, 0); + } +} diff --git a/core/java/com/android/internal/widget/remotecompose/player/RemoteComposeDocument.java b/core/java/com/android/internal/widget/remotecompose/player/RemoteComposeDocument.java index bcda27a40f64..d1c4d46f2c22 100644 --- a/core/java/com/android/internal/widget/remotecompose/player/RemoteComposeDocument.java +++ b/core/java/com/android/internal/widget/remotecompose/player/RemoteComposeDocument.java @@ -79,6 +79,15 @@ public class RemoteComposeDocument { } /** + * The delay in milliseconds to next repaint -1 = not needed 0 = asap + * + * @return delay in milliseconds to next repaint or -1 + */ + public int needsRepaint() { + return mDocument.needsRepaint(); + } + + /** * Returns true if the document can be displayed given this version of the player * * @param majorVersion the max major version supported by the player @@ -89,5 +98,10 @@ public class RemoteComposeDocument { return mDocument.canBeDisplayed(majorVersion, minorVersion, capabilities); } + @Override + public String toString() { + return "Document{\n" + + mDocument + '}'; + } } diff --git a/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidPaintContext.java b/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidPaintContext.java index d0d6e6982a16..ecb68bb21fb3 100644 --- a/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidPaintContext.java +++ b/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidPaintContext.java @@ -26,6 +26,7 @@ import android.graphics.PorterDuffColorFilter; import android.graphics.RadialGradient; import android.graphics.Rect; import android.graphics.RectF; +import android.graphics.RuntimeShader; import android.graphics.Shader; import android.graphics.SweepGradient; import android.graphics.Typeface; @@ -33,6 +34,8 @@ import android.graphics.Typeface; import com.android.internal.widget.remotecompose.core.PaintContext; import com.android.internal.widget.remotecompose.core.RemoteContext; import com.android.internal.widget.remotecompose.core.operations.ClipPath; +import com.android.internal.widget.remotecompose.core.operations.ShaderData; +import com.android.internal.widget.remotecompose.core.operations.Utils; import com.android.internal.widget.remotecompose.core.operations.paint.PaintBundle; import com.android.internal.widget.remotecompose.core.operations.paint.PaintChanges; @@ -43,6 +46,7 @@ import com.android.internal.widget.remotecompose.core.operations.paint.PaintChan public class AndroidPaintContext extends PaintContext { Paint mPaint = new Paint(); Canvas mCanvas; + Rect mTmpRect = new Rect(); // use in calculation of bounds public AndroidPaintContext(RemoteContext context, Canvas canvas) { super(context); @@ -177,6 +181,22 @@ public class AndroidPaintContext extends PaintContext { } @Override + public void getTextBounds(int textId, int start, int end, boolean monospace, float[] bounds) { + String str = getText(textId); + if (end == -1) { + end = str.length(); + } + + mPaint.getTextBounds(str, start, end, mTmpRect); + + bounds[0] = mTmpRect.left; + bounds[1] = mTmpRect.top; + bounds[2] = monospace ? (mPaint.measureText(str, start, end) - mTmpRect.left) + : mTmpRect.right; + bounds[3] = mTmpRect.bottom; + } + + @Override public void drawTextRun(int textID, int start, int end, @@ -185,7 +205,16 @@ public class AndroidPaintContext extends PaintContext { float x, float y, boolean rtl) { - String textToPaint = getText(textID).substring(start, end); + + String textToPaint = getText(textID); + if (end == -1) { + if (start != 0) { + textToPaint = textToPaint.substring(start); + } + } else { + textToPaint = textToPaint.substring(start, end); + } + mCanvas.drawText(textToPaint, x, y, mPaint); } @@ -308,7 +337,7 @@ public class AndroidPaintContext extends PaintContext { @Override public void applyPaint(PaintBundle mPaintData) { - mPaintData.applyPaintChange(new PaintChanges() { + mPaintData.applyPaintChange((PaintContext) this, new PaintChanges() { @Override public void setTextSize(float size) { mPaint.setTextSize(size); @@ -361,10 +390,8 @@ public class AndroidPaintContext extends PaintContext { } } - } - @Override public void setStrokeWidth(float width) { mPaint.setStrokeWidth(width); @@ -386,13 +413,37 @@ public class AndroidPaintContext extends PaintContext { } @Override - public void setShader(int shader, String shaderString) { - + public void setShader(int shaderId) { + // TODO this stuff should check the shader creation + if (shaderId == 0) { + mPaint.setShader(null); + return; + } + ShaderData data = getShaderData(shaderId); + RuntimeShader shader = new RuntimeShader(getText(data.getShaderTextId())); + String[] names = data.getUniformFloatNames(); + for (int i = 0; i < names.length; i++) { + String name = names[i]; + float[] val = data.getUniformFloats(name); + shader.setFloatUniform(name, val); + } + names = data.getUniformIntegerNames(); + for (int i = 0; i < names.length; i++) { + String name = names[i]; + int[] val = data.getUniformInts(name); + shader.setIntUniform(name, val); + } + names = data.getUniformBitmapNames(); + for (int i = 0; i < names.length; i++) { + String name = names[i]; + int val = data.getUniformBitmapId(name); + } + mPaint.setShader(shader); } @Override public void setImageFilterQuality(int quality) { - System.out.println(">>>>>>>>>>>> "); + Utils.log(" quality =" + quality); } @Override @@ -420,7 +471,6 @@ public class AndroidPaintContext extends PaintContext { mPaint.setFilterBitmap(filter); } - @Override public void setAntiAlias(boolean aa) { mPaint.setAntiAlias(aa); @@ -437,7 +487,6 @@ public class AndroidPaintContext extends PaintContext { case PaintBundle.COLOR_FILTER: mPaint.setColorFilter(null); - System.out.println(">>>>>>>>>>>>> CLEAR!!!!"); break; } } @@ -446,12 +495,11 @@ public class AndroidPaintContext extends PaintContext { } } - Shader.TileMode[] mTilesModes = new Shader.TileMode[]{ + Shader.TileMode[] mTileModes = new Shader.TileMode[]{ Shader.TileMode.CLAMP, Shader.TileMode.REPEAT, Shader.TileMode.MIRROR}; - @Override public void setLinearGradient(int[] colors, float[] stops, @@ -463,7 +511,7 @@ public class AndroidPaintContext extends PaintContext { mPaint.setShader(new LinearGradient(startX, startY, endX, - endY, colors, stops, mTilesModes[tileMode])); + endY, colors, stops, mTileModes[tileMode])); } @@ -475,7 +523,7 @@ public class AndroidPaintContext extends PaintContext { float radius, int tileMode) { mPaint.setShader(new RadialGradient(centerX, centerY, radius, - colors, stops, mTilesModes[tileMode])); + colors, stops, mTileModes[tileMode])); } @Override @@ -490,7 +538,6 @@ public class AndroidPaintContext extends PaintContext { @Override public void setColorFilter(int color, int mode) { PorterDuff.Mode pmode = origamiToPorterDuffMode(mode); - System.out.println("setting color filter to " + pmode.name()); if (pmode != null) { mPaint.setColorFilter( new PorterDuffColorFilter(color, pmode)); @@ -500,10 +547,10 @@ public class AndroidPaintContext extends PaintContext { } @Override - public void mtrixScale(float scaleX, - float scaleY, - float centerX, - float centerY) { + public void matrixScale(float scaleX, + float scaleY, + float centerX, + float centerY) { if (Float.isNaN(centerX)) { mCanvas.scale(scaleX, scaleY); } else { @@ -556,6 +603,11 @@ public class AndroidPaintContext extends PaintContext { } } + @Override + public void reset() { + mPaint.reset(); + } + private Path getPath(int path1Id, int path2Id, float tween, @@ -599,5 +651,9 @@ public class AndroidPaintContext extends PaintContext { private String getText(int id) { return (String) mContext.mRemoteComposeState.getFromId(id); } + + private ShaderData getShaderData(int id) { + return (ShaderData) mContext.mRemoteComposeState.getFromId(id); + } } diff --git a/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidRemoteContext.java b/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidRemoteContext.java index 270e96f11942..6e4893bc0ee6 100644 --- a/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidRemoteContext.java +++ b/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidRemoteContext.java @@ -20,10 +20,15 @@ import android.graphics.BitmapFactory; import android.graphics.Canvas; import com.android.internal.widget.remotecompose.core.RemoteContext; +import com.android.internal.widget.remotecompose.core.VariableSupport; +import com.android.internal.widget.remotecompose.core.operations.FloatExpression; +import com.android.internal.widget.remotecompose.core.operations.ShaderData; + +import java.util.HashMap; /** * An implementation of Context for Android. - * + * <p> * This is used to play the RemoteCompose operations on Android. */ class AndroidRemoteContext extends RemoteContext { @@ -33,6 +38,7 @@ class AndroidRemoteContext extends RemoteContext { mPaintContext = new AndroidPaintContext(this, canvas); } else { // need to make sure to update the canvas for the current one + mPaintContext.reset(); ((AndroidPaintContext) mPaintContext).setCanvas(canvas); } mWidth = canvas.getWidth(); @@ -50,13 +56,32 @@ class AndroidRemoteContext extends RemoteContext { } } + static class VarName { + String mName; + int mId; + int mType; + + VarName(String name, int id, int type) { + mName = name; + mId = id; + mType = type; + } + } + + HashMap<String, VarName> mVarNameHashMap = new HashMap<>(); + + @Override + public void loadVariableName(String varName, int varId, int varType) { + mVarNameHashMap.put(varName, new VarName(varName, varId, varType)); + } + /** * Decode a byte array into an image and cache it using the given imageId * - * @oaram imageId the id of the image - * @param width with of image to be loaded + * @param width with of image to be loaded * @param height height of image to be loaded * @param bitmap a byte array containing the image information + * @oaram imageId the id of the image */ @Override public void loadBitmap(int imageId, int width, int height, byte[] bitmap) { @@ -70,14 +95,66 @@ class AndroidRemoteContext extends RemoteContext { public void loadText(int id, String text) { if (!mRemoteComposeState.containsId(id)) { mRemoteComposeState.cache(id, text); + } else { + mRemoteComposeState.update(id, text); } } + @Override + public String getText(int id) { + return (String) mRemoteComposeState.getFromId(id); + } + + @Override + public void loadFloat(int id, float value) { + mRemoteComposeState.updateFloat(id, value); + } + + + @Override + public void loadColor(int id, int color) { + mRemoteComposeState.updateColor(id, color); + } + + @Override + public void loadAnimatedFloat(int id, FloatExpression animatedFloat) { + mRemoteComposeState.cache(id, animatedFloat); + } + + @Override + public void loadShader(int id, ShaderData value) { + mRemoteComposeState.cache(id, value); + } + + @Override + public float getFloat(int id) { + return (float) mRemoteComposeState.getFloat(id); + } + + @Override + public int getColor(int id) { + return mRemoteComposeState.getColor(id); + } + + @Override + public void listensTo(int id, VariableSupport variableSupport) { + mRemoteComposeState.listenToVar(id, variableSupport); + } + + @Override + public int updateOps() { + return mRemoteComposeState.getOpsToUpdate(this); + } + + @Override + public ShaderData getShader(int id) { + return (ShaderData) mRemoteComposeState.getFromId(id); + } + /////////////////////////////////////////////////////////////////////////////////////////////// // Click handling /////////////////////////////////////////////////////////////////////////////////////////////// - @Override public void addClickArea(int id, int contentDescriptionId, @@ -87,7 +164,7 @@ class AndroidRemoteContext extends RemoteContext { float bottom, int metadataId) { String contentDescription = (String) mRemoteComposeState.getFromId(contentDescriptionId); - String metadata = (String) mRemoteComposeState.getFromId(metadataId); + String metadata = (String) mRemoteComposeState.getFromId(metadataId); mDocument.addClickArea(id, contentDescription, left, top, right, bottom, metadata); } } diff --git a/core/java/com/android/internal/widget/remotecompose/player/platform/ClickAreaView.java b/core/java/com/android/internal/widget/remotecompose/player/platform/ClickAreaView.java index 672dae32d8e0..329178abe8b5 100644 --- a/core/java/com/android/internal/widget/remotecompose/player/platform/ClickAreaView.java +++ b/core/java/com/android/internal/widget/remotecompose/player/platform/ClickAreaView.java @@ -20,7 +20,6 @@ import android.graphics.Canvas; import android.graphics.Paint; import android.view.View; - /** * Implementation for the click handling */ @@ -40,7 +39,6 @@ class ClickAreaView extends View { setContentDescription(contentDescription); } - public void setDebug(boolean value) { if (mDebug != value) { mDebug = value; diff --git a/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java b/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java index a3bb73e22c59..97d23c84b5bd 100644 --- a/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java +++ b/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java @@ -85,6 +85,7 @@ public class RemoteComposeCanvas extends FrameLayout implements View.OnAttachSta mDocument.initializeContext(mARContext); setContentDescription(mDocument.getDocument().getContentDescription()); requestLayout(); + invalidate(); } AndroidRemoteContext mARContext = new AndroidRemoteContext(); @@ -119,8 +120,7 @@ public class RemoteComposeCanvas extends FrameLayout implements View.OnAttachSta removeAllViews(); } - - public interface ClickCallbacks { + public interface ClickCallbacks { void click(int id, String metadata); } @@ -213,6 +213,9 @@ public class RemoteComposeCanvas extends FrameLayout implements View.OnAttachSta setMeasuredDimension(w, h); } + private int mCount; + private long mTime = System.nanoTime(); + @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); @@ -224,6 +227,17 @@ public class RemoteComposeCanvas extends FrameLayout implements View.OnAttachSta mARContext.mWidth = getWidth(); mARContext.mHeight = getHeight(); mDocument.paint(mARContext, mTheme); + if (mDebug) { + mCount++; + if (System.nanoTime() - mTime > 1000000000L) { + System.out.println(" count " + mCount + " fps"); + mCount = 0; + mTime = System.nanoTime(); + } + } + if (mDocument.needsRepaint() > 0) { + invalidate(); + } } } diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index e831a7d229d6..536583840d8f 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -358,7 +358,8 @@ public: jobject stats = env->NewObject(gTransactionStatsClassInfo.clazz, gTransactionStatsClassInfo.ctor, - latchTime, presentFence.get()); + latchTime, + static_cast<jlong>(reinterpret_cast<uintptr_t>(presentFence.get()))); env->CallVoidMethod(mTransactionCompletedListenerObject, gConsumerClassInfo.accept, stats); env->DeleteLocalRef(stats); DieIfException(env, "Uncaught exception in TransactionCompletedListener."); diff --git a/core/proto/android/providers/settings/system.proto b/core/proto/android/providers/settings/system.proto index 123306924c2b..6a0ec1dcb3f2 100644 --- a/core/proto/android/providers/settings/system.proto +++ b/core/proto/android/providers/settings/system.proto @@ -126,6 +126,7 @@ message SystemSettingsProto { option (android.msg_privacy).dest = DEST_EXPLICIT; optional SettingProto pointer_fill_style = 1 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto pointer_scale = 3 [ (android.privacy).dest = DEST_AUTOMATIC ]; } optional Pointer pointer = 37; optional SettingProto pointer_speed = 18 [ (android.privacy).dest = DEST_AUTOMATIC ]; diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 09ffdf3d4e4c..c71f9bde6bf1 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -3249,16 +3249,20 @@ <permission android:name="android.permission.INTERACT_ACROSS_PROFILES" android:protectionLevel="signature|appop" /> - <!-- Allows applications to access profiles with ACCESS_HIDDEN_PROFILES user property - <p>Protection level: normal - @FlaggedApi("android.multiuser.enable_permission_to_access_hidden_profiles") --> + <!-- Allows applications to access profiles with + {@code android.content.pm.UserProperties#PROFILE_API_VISIBILITY_HIDDEN} user property, e.g. + {@link android.os.UserManager#USER_TYPE_PROFILE_PRIVATE}. + <p>Protection level: normal + @FlaggedApi("android.multiuser.enable_permission_to_access_hidden_profiles") --> <permission android:name="android.permission.ACCESS_HIDDEN_PROFILES" android:label="@string/permlab_accessHiddenProfile" android:description="@string/permdesc_accessHiddenProfile" android:protectionLevel="normal" /> - <!-- @SystemApi @hide Allows privileged applications to get details about hidden profile - users. + <!-- @SystemApi @hide Allows privileged applications to get details about profiles with + {@code android.content.pm.UserProperties#PROFILE_API_VISIBILITY_HIDDEN} user property, e.g. + {@link android.os.UserManager#USER_TYPE_PROFILE_PRIVATE}. Removes extra requirements such + as having {@link android.app.role.RoleManager#ROLE_HOME} role for LauncherApps APIs. @FlaggedApi("android.multiuser.enable_permission_to_access_hidden_profiles") --> <permission android:name="android.permission.ACCESS_HIDDEN_PROFILES_FULL" @@ -3322,13 +3326,18 @@ <!-- Allows an application to manage device policy relating to time. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call - APIs protected by this permission on users different to the calling user.--> + APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. + --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_TIME" android:protectionLevel="internal|role" /> <!-- Allows an application to set the grant state of runtime permissions on packages. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS" android:protectionLevel="internal|role" /> @@ -3336,6 +3345,8 @@ <!-- Allows an application to manage the identity of the managing organization. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY" android:protectionLevel="internal|role" /> @@ -3344,6 +3355,8 @@ active policy. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_SUPPORT_MESSAGE" android:protectionLevel="internal|role" /> @@ -3351,6 +3364,8 @@ <!-- Allows an application to manage backup service policy. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_BACKUP_SERVICE" android:protectionLevel="internal|role" /> @@ -3358,6 +3373,8 @@ <!-- Allows an application to manage lock task policy. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_LOCK_TASK" android:protectionLevel="internal|role" /> @@ -3365,6 +3382,8 @@ <!-- Allows an application to manage policy regarding modifying applications. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_APPS_CONTROL" android:protectionLevel="internal|role" /> @@ -3372,6 +3391,8 @@ <!-- Allows an application to manage installing from unknown sources policy. <p>MANAGE_SECURITY_CRITICAL_DEVICE_POLICY_ACROSS_USERS is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_INSTALL_UNKNOWN_SOURCES" android:protectionLevel="internal|role" /> @@ -3379,6 +3400,8 @@ <!-- Allows an application to manage application restrictions. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_APP_RESTRICTIONS" android:protectionLevel="internal|role" /> @@ -3386,6 +3409,8 @@ <!-- Allows an application to manage calling policy. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_CALLS" android:protectionLevel="internal|role" /> @@ -3393,6 +3418,8 @@ <!-- Allows an application to manage debugging features policy. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_DEBUGGING_FEATURES" android:protectionLevel="internal|role" /> @@ -3400,6 +3427,8 @@ <!-- Allows an application to manage policy preventing users from modifying users. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_MODIFY_USERS" android:protectionLevel="internal|role" /> @@ -3407,6 +3436,8 @@ <!-- Allows an application to manage safe boot policy. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_SAFE_BOOT" android:protectionLevel="internal|role" /> @@ -3415,6 +3446,8 @@ enable and disable the microphone. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_MICROPHONE" android:protectionLevel="internal|role" /> @@ -3423,6 +3456,8 @@ enable and disable the camera. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_CAMERA" android:protectionLevel="internal|role" /> @@ -3430,6 +3465,8 @@ <!-- Allows an application to manage policy related to keyguard. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_KEYGUARD" android:protectionLevel="internal|role" /> @@ -3437,6 +3474,8 @@ <!-- Allows an application to set policy related to account management. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_ACCOUNT_MANAGEMENT" android:protectionLevel="internal|role" /> @@ -3444,6 +3483,8 @@ <!-- Allows an application to set policy related to hiding and suspending packages. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_PACKAGE_STATE" android:protectionLevel="internal|role" /> @@ -3452,17 +3493,24 @@ challenge on current user. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_RESET_PASSWORD" android:protectionLevel="internal|role" /> - <!-- Allows an application to set policy related to the status bar.--> + <!-- Allows an application to set policy related to the status bar. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. + --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_STATUS_BAR" android:protectionLevel="internal|role" /> <!-- Allows an application to set policy related to bluetooth. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_BLUETOOTH" android:protectionLevel="internal|role" /> @@ -3470,6 +3518,8 @@ <!-- Allows an application to set policy related to fun. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_FUN" android:protectionLevel="internal|role" /> @@ -3477,6 +3527,8 @@ <!-- Allows an application to set policy related to airplane mode. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_AIRPLANE_MODE" android:protectionLevel="internal|role" /> @@ -3484,6 +3536,8 @@ <!-- Allows an application to set policy related to mobile networks. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_MOBILE_NETWORK" android:protectionLevel="internal|role" /> @@ -3491,6 +3545,8 @@ <!-- Allows an application to set policy related to physical media. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_PHYSICAL_MEDIA" android:protectionLevel="internal|role" /> @@ -3498,6 +3554,8 @@ <!-- Allows an application to set policy related to sms. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_SMS" android:protectionLevel="internal|role" /> @@ -3505,6 +3563,8 @@ <!-- Allows an application to set policy related to usb file transfers. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_USB_FILE_TRANSFER" android:protectionLevel="internal|role" /> @@ -3512,6 +3572,8 @@ <!-- Allows an application to set policy related to lock credentials. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS" android:protectionLevel="internal|role" /> @@ -3519,6 +3581,8 @@ <!-- Allows an application to set policy related to Wifi. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_WIFI" android:protectionLevel="internal|role" /> @@ -3526,6 +3590,8 @@ <!-- Allows an application to set policy related to screen capture. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_SCREEN_CAPTURE" android:protectionLevel="internal|role" /> @@ -3533,6 +3599,8 @@ <!-- Allows an application to set policy related to input methods. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_INPUT_METHODS" android:protectionLevel="internal|role" /> @@ -3541,6 +3609,8 @@ private DNS. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_RESTRICT_PRIVATE_DNS" android:protectionLevel="internal|role" /> @@ -3548,6 +3618,8 @@ <!-- Allows an application to set policy related to the default sms application. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_DEFAULT_SMS" android:protectionLevel="internal|role" /> @@ -3555,6 +3627,8 @@ <!-- Allows an application to set policy related to profiles. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_PROFILES" android:protectionLevel="internal|role" /> @@ -3563,6 +3637,8 @@ cross-profile copy and paste). <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_PROFILE_INTERACTION" android:protectionLevel="internal|role" /> @@ -3570,6 +3646,8 @@ <!-- Allows an application to set policy related to VPNs. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_VPN" android:protectionLevel="internal|role" /> @@ -3577,6 +3655,8 @@ <!-- Allows an application to set policy related to audio output. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_AUDIO_OUTPUT" android:protectionLevel="internal|role" /> @@ -3584,6 +3664,8 @@ <!-- Allows an application to set policy related to the display. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_DISPLAY" android:protectionLevel="internal|role" /> @@ -3591,6 +3673,8 @@ <!-- Allows an application to set policy related to location. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_LOCATION" android:protectionLevel="internal|role" /> @@ -3598,6 +3682,8 @@ <!-- Allows an application to set policy related to factory reset. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_FACTORY_RESET" android:protectionLevel="internal|role" /> @@ -3605,6 +3691,8 @@ <!-- Allows an application to set policy related to the wallpaper. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_WALLPAPER" android:protectionLevel="internal|role" /> @@ -3612,6 +3700,8 @@ <!-- Allows an application to set policy related to the usage of the contents of the screen. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_SCREEN_CONTENT" android:protectionLevel="internal|role" /> @@ -3619,6 +3709,8 @@ <!-- Allows an application to set policy related to system dialogs. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_SYSTEM_DIALOGS" android:protectionLevel="internal|role" /> @@ -3626,6 +3718,8 @@ <!-- Allows an application to set policy related to users running in the background. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_RUN_IN_BACKGROUND" android:protectionLevel="internal|role" /> @@ -3633,6 +3727,8 @@ <!-- Allows an application to set policy related to printing. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_PRINTING" android:protectionLevel="internal|role" /> @@ -3641,12 +3737,16 @@ nearby streaming). <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_NEARBY_COMMUNICATION" android:protectionLevel="internal|role" /> <!-- Allows an application to set policy related to <a href="https://www.threadgroup.org">Thread</a> network. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. @FlaggedApi("com.android.net.thread.platform.flags.thread_user_restriction_enabled") --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_THREAD_NETWORK" @@ -3654,6 +3754,8 @@ <!-- Allows an application to set policy related to sending assist content to a privileged app such as the Assistant app. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. @FlaggedApi("android.app.admin.flags.assist_content_user_restriction_enabled") --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_ASSIST_CONTENT" @@ -3662,6 +3764,8 @@ <!-- Allows an application to set policy related to windows. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_WINDOWS" android:protectionLevel="internal|role" /> @@ -3669,6 +3773,8 @@ <!-- Allows an application to set policy related to locale. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_LOCALE" android:protectionLevel="internal|role" /> @@ -3676,6 +3782,8 @@ <!-- Allows an application to set policy related to autofill. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_AUTOFILL" android:protectionLevel="internal|role" /> @@ -3683,6 +3791,8 @@ <!-- Allows an application to set policy related to users. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_USERS" android:protectionLevel="internal|role" /> @@ -3690,6 +3800,8 @@ <!-- Allows an application to set policy related to certificates. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_CERTIFICATES" android:protectionLevel="internal|role" /> @@ -3697,6 +3809,8 @@ <!-- Allows an application to set policy related to override APNs. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_OVERRIDE_APN" android:protectionLevel="internal|role" /> @@ -3704,6 +3818,8 @@ <!-- Allows an application to set policy related to security logging. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_SECURITY_LOGGING" android:protectionLevel="internal|role" /> @@ -3719,6 +3835,8 @@ <!-- Allows an application to set policy related to system updates. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_SYSTEM_UPDATES" android:protectionLevel="internal|role" /> @@ -3726,6 +3844,8 @@ <!-- Allows an application query system updates. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_QUERY_SYSTEM_UPDATES" android:protectionLevel="internal|role" /> @@ -3733,6 +3853,8 @@ <!-- Allows an application to set policy related to private DNS. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_PRIVATE_DNS" android:protectionLevel="internal|role" /> @@ -3740,6 +3862,8 @@ <!-- Allows an application to set policy related to settings. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_SETTINGS" android:protectionLevel="internal|role" /> @@ -3747,17 +3871,24 @@ <!-- Allows an application to set policy related to network logging. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_NETWORK_LOGGING" android:protectionLevel="internal|role" /> - <!-- Allows an application to set policy related to usb data signalling.--> + <!-- Allows an application to set policy related to usb data signalling. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. + --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_USB_DATA_SIGNALLING" android:protectionLevel="internal|role" /> <!-- Allows an application to set policy related to suspending personal apps. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_SUSPEND_PERSONAL_APPS" android:protectionLevel="internal|role" /> @@ -3765,13 +3896,17 @@ <!-- Allows an application to set policy related to keeping uninstalled packages. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_KEEP_UNINSTALLED_PACKAGES" android:protectionLevel="internal|role" /> <!-- Allows an application to manage policy related to accessibility. - <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call - APIs protected by this permission on users different to the calling user. + <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to + call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_ACCESSIBILITY" android:protectionLevel="internal|role" /> @@ -3779,6 +3914,8 @@ <!-- Allows an application to manage policy related to common criteria mode. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_COMMON_CRITERIA_MODE" android:protectionLevel="internal|role" /> @@ -3786,6 +3923,8 @@ <!-- Allows an application to manage policy related to metered data. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_METERED_DATA" android:protectionLevel="internal|role" /> @@ -3793,6 +3932,8 @@ <!-- Allows an application to set a network-independent global HTTP proxy. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_PROXY" android:protectionLevel="internal|role" /> @@ -3800,6 +3941,8 @@ <!-- Allows an application to request bugreports with user consent. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_BUGREPORT" android:protectionLevel="internal|role" /> @@ -3807,6 +3950,8 @@ <!-- Allows an application to manage policy related to application user data. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_APP_USER_DATA" android:protectionLevel="internal|role" /> @@ -3815,6 +3960,8 @@ permission. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_LOCK" android:protectionLevel="internal|role" /> @@ -3830,6 +3977,8 @@ <!-- Allows an application to manage policy related to system apps. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_SYSTEM_APPS" android:protectionLevel="internal|role" /> @@ -3837,16 +3986,23 @@ <!-- Allows an application to manage policy related to wiping data. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_WIPE_DATA" android:protectionLevel="internal|role" /> <!-- Allows an application to manage policy related to the Memory Tagging Extension (MTE). + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_MTE" android:protectionLevel="internal|role" /> - <!-- Allows an application to manage policy related to device identifiers. --> + <!-- Allows an application to manage policy related to device identifiers. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. + --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_DEVICE_IDENTIFIERS" android:protectionLevel="internal|role" /> @@ -3859,24 +4015,33 @@ <!-- Allows an application to set policy related to subscriptions downloaded by an admin. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call - APIs protected by this permission on users different to the calling user. - @FlaggedApi("android.app.admin.flags.esim_management_enabled") --> + APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. + @FlaggedApi("android.app.admin.flags.esim_management_enabled") + --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_MANAGED_SUBSCRIPTIONS" android:protectionLevel="internal|role" /> <!-- Allows an application to manage policy related to block package uninstallation. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. @FlaggedApi("android.app.admin.flags.dedicated_device_control_api_enabled") --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_BLOCK_UNINSTALL" android:protectionLevel="internal|role" /> <!-- Allows an application to manage policy related to camera toggle. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. @FlaggedApi("android.app.admin.flags.dedicated_device_control_api_enabled") --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_CAMERA_TOGGLE" android:protectionLevel="internal|role" /> <!-- Allows an application to manage policy related to microphone toggle. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. @FlaggedApi("android.app.admin.flags.dedicated_device_control_api_enabled") --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_MICROPHONE_TOGGLE" @@ -3885,16 +4050,21 @@ <!-- Allows an application to set device policies outside the current user that are critical for securing data within the current user. <p>Holding this permission allows the use of other held MANAGE_DEVICE_POLICY_* - permissions across all users on the device provided they are required for securing data - within the current user.--> + permissions across all users on the device provided they are required for securing data + within the current user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. + --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL" android:protectionLevel="internal|role" /> <!-- Allows an application to set device policies outside the current user that are required for securing device ownership without accessing user data. <p>Holding this permission allows the use of other held MANAGE_DEVICE_POLICY_* - permissions across all users on the device provided they do not grant access to user - data. --> + permissions across all users on the device provided they do not grant access to user data. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. + --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_ACROSS_USERS" android:protectionLevel="internal|role" /> @@ -3902,7 +4072,10 @@ <p>Fuller form of {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS} that removes the restriction on accessing user data. <p>Holding this permission allows the use of any other held MANAGE_DEVICE_POLICY_* - permissions across all users on the device.--> + permissions across all users on the device. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. + --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL" android:protectionLevel="internal|role" /> @@ -8194,6 +8367,17 @@ <permission android:name="android.permission.SETUP_FSVERITY" android:protectionLevel="signature|privileged"/> + <!-- + @TestApi + Signature permission reserved for testing. This should never be used to + gate any actual functionality. + <p> + Protection level: signature + @hide + --> + <permission android:name="android.permission.RESERVED_FOR_TESTING_SIGNATURE" + android:protectionLevel="signature"/> + <!-- Attribution for Geofencing service. --> <attribution android:tag="GeofencingService" android:label="@string/geofencing_service"/> <!-- Attribution for Country Detector. --> diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml index 704d44278a78..18425fe0163c 100644 --- a/core/res/res/values-af/strings.xml +++ b/core/res/res/values-af/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privaat ruimte"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Kloon"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Gemeenskaplik"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Privaat ruimte"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Sensitiewe kennisgewinginhoud is versteek"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Appinhoud is weens sekuriteit van skermdeling verberg"</string> diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml index d1e18dacd5b4..965160e4b3a2 100644 --- a/core/res/res/values-am/strings.xml +++ b/core/res/res/values-am/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"የግል ቦታ"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"አባዛ"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"የጋራ"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"የግል ቦታ"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"አደገኛ የማሳወቂያ ይዘት ተደብቋል"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"ለደኅንነት ሲባል የመተግበሪያ ይዘት ከማያ ገጽ ማጋራት ተደብቋል"</string> diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml index 5ef18af8129a..3e396e0ce86f 100644 --- a/core/res/res/values-ar/strings.xml +++ b/core/res/res/values-ar/strings.xml @@ -2411,8 +2411,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"المساحة الخاصة"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"نسخة طبق الأصل"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"ملف شخصي مشترك"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"المساحة الخاصّة"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"تم إخفاء المحتوى الحساس في الإشعار"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"تم إخفاء محتوى التطبيق بعد تفعيل ميزة \"مشاركة الشاشة\" للحفاظ على أمانك"</string> diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml index 5443f12881b4..5f9f16a61945 100644 --- a/core/res/res/values-as/strings.xml +++ b/core/res/res/values-as/strings.xml @@ -1865,7 +1865,7 @@ <string name="mediasize_japanese_you4" msgid="5552111912684384833">"You4"</string> <string name="mediasize_japanese_l" msgid="1326765321473431817">"L"</string> <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"অজ্ঞাত প\'ৰ্ট্ৰেইট"</string> - <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"অজ্ঞাত লেণ্ডস্কেইপ"</string> + <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"অজ্ঞাত লেণ্ডস্কে’প"</string> <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"বাতিল কৰা হ’ল"</string> <string name="write_fail_reason_cannot_write" msgid="432118118378451508">"সমল লিখাত আসোঁৱাহ"</string> <string name="reason_unknown" msgid="5599739807581133337">"অজ্ঞাত"</string> @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"প্ৰাইভেট স্পে’চ"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"ক্ল’ন"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"সম্প্ৰদায়ৰ সৈতে জড়িত"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"প্ৰাইভেট স্পে’চ"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"সংবেদনশীল জাননী লুকুওৱা হৈছে"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"সুৰক্ষাৰ বাবে এপৰ সমল স্ক্ৰীণ শ্বেয়াৰ কৰাৰ পৰা লুকুৱাই ৰখা হৈছে"</string> diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml index b9ec7428a4e6..c4327e79a45f 100644 --- a/core/res/res/values-az/strings.xml +++ b/core/res/res/values-az/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Məxfi sahə"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Kommunal"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Şəxsi sahə"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Həssas bildiriş kontenti gizlədildi"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Güvənlik üçün tətbiq kontenti ekran paylaşımından gizlədildi"</string> diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml index 858075ba4893..2b81e1cdce7c 100644 --- a/core/res/res/values-b+sr+Latn/strings.xml +++ b/core/res/res/values-b+sr+Latn/strings.xml @@ -2408,8 +2408,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privatan prostor"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klonirano"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Zajedničko"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Privatan prostor"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Osetljiv sadržaj obaveštenja je skriven"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Sadržaj aplikacije je skriven za deljenje sadržaja ekrana zbog bezbednosti"</string> diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml index 2739cb212353..7d1f6bee5816 100644 --- a/core/res/res/values-be/strings.xml +++ b/core/res/res/values-be/strings.xml @@ -2409,8 +2409,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Прыватная прастора"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Клон"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Супольны"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Прыватная прастора"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Канфідэнцыяльнае змесціва ў апавяшчэннях схавана"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Змесціва праграмы выключана з абагульвання экрана ў мэтах бяспекі"</string> diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml index 0d4cadcae10b..a2f03646bb66 100644 --- a/core/res/res/values-bg/strings.xml +++ b/core/res/res/values-bg/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Частно пространство"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Клониране"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Общи"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Частно пространство"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Деликатното съдържание в известието е скрито"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Съдържанието на приложението е скрито от функцията за споделяне на екрана от съображения за сигурност"</string> diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml index 03fba4b39d94..e21b5c05965f 100644 --- a/core/res/res/values-bn/strings.xml +++ b/core/res/res/values-bn/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"প্রাইভেট স্পেস"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"ক্লোন"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"কমিউনাল"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"প্রাইভেট স্পেস"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"সংবেদনশীল বিজ্ঞপ্তির কন্টেন্ট লুকানো আছে"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"নিরাপত্তার জন্য স্ক্রিন শেয়ার করা থেকে লুকানো অ্যাপের কন্টেন্ট"</string> diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml index 6ce10c0c8907..2f285f13f4e5 100644 --- a/core/res/res/values-bs/strings.xml +++ b/core/res/res/values-bs/strings.xml @@ -2408,8 +2408,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privatni prostor"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Opće"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Privatni prostor"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Sakriven je osjetljiv sadržaj obavještenja"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Sadržaj aplikacije je sakriven od dijeljenja ekrana radi sigurnosti"</string> diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml index 4b1af3444446..48ba090c65c3 100644 --- a/core/res/res/values-ca/strings.xml +++ b/core/res/res/values-ca/strings.xml @@ -2408,8 +2408,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Espai privat"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clon"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Comunitari"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Espai privat"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"S\'ha amagat contingut sensible de les notificacions"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Contingut de l\'aplicació amagat de la compartició de pantalla per motius de seguretat"</string> diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml index cc4bd3cfeb9a..64ccaf540411 100644 --- a/core/res/res/values-cs/strings.xml +++ b/core/res/res/values-cs/strings.xml @@ -2409,8 +2409,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Soukromý prostor"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Komunální"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Soukromý prostor"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Obsah citlivých oznámení je skrytý"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Obsah aplikace je z bezpečnostních důvodů při sdílení obrazovky skryt"</string> diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml index 665ea1750b14..a499afc01987 100644 --- a/core/res/res/values-da/strings.xml +++ b/core/res/res/values-da/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privat område"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Fælles"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Privat område"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Følsomt indhold i notifikationen er skjult"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Af sikkerhedsmæssige årsager vises appindhold ikke ved skærmdeling"</string> diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml index cd637e12b70a..464a5373bcf1 100644 --- a/core/res/res/values-de/strings.xml +++ b/core/res/res/values-de/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Vertrauliches Profil"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Gemeinsam genutzt"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Vertrauliches Profil"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Vertrauliche Benachrichtigungsinhalte ausgeblendet"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"App-Inhalte werden aus Sicherheitsgründen bei der Bildschirmfreigabe ausgeblendet"</string> diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml index 09ecc2c5f22f..98ce03c16c69 100644 --- a/core/res/res/values-el/strings.xml +++ b/core/res/res/values-el/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Ιδιωτικός χώρος"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Κλώνος"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Κοινόχρηστο"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Ιδιωτικός χώρος"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Έγινε απόκρυψη της ειδοποίησης ευαίσθητου περιεχομένου"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Για λόγους ασφάλειας, έγινε απόκρυψη του περιεχομένου της εφαρμογής από την κοινή χρήση οθόνης"</string> diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml index 20e391d173eb..1d2fc4d5e89c 100644 --- a/core/res/res/values-en-rAU/strings.xml +++ b/core/res/res/values-en-rAU/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Private space"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Communal"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Private space"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Sensitive notification content hidden"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"App content hidden from screen share for security"</string> diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml index 7f33f6a83c21..c03bb3cb53e3 100644 --- a/core/res/res/values-en-rCA/strings.xml +++ b/core/res/res/values-en-rCA/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Private space"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Communal"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Private space"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Sensitive notification content hidden"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"App content hidden from screen share for security"</string> diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml index b7fedbe45d78..436d7aeafea4 100644 --- a/core/res/res/values-en-rGB/strings.xml +++ b/core/res/res/values-en-rGB/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Private space"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Communal"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Private space"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Sensitive notification content hidden"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"App content hidden from screen share for security"</string> diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml index b83a7cfb877b..d34ed3f6e01e 100644 --- a/core/res/res/values-en-rIN/strings.xml +++ b/core/res/res/values-en-rIN/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Private space"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Communal"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Private space"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Sensitive notification content hidden"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"App content hidden from screen share for security"</string> diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml index d358e5e37481..c2c107c65b5f 100644 --- a/core/res/res/values-en-rXC/strings.xml +++ b/core/res/res/values-en-rXC/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Private space"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Communal"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Private space"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Sensitive notification content hidden"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"App content hidden from screen share for security"</string> diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml index 45ea7ee341ae..df7deacdf10d 100644 --- a/core/res/res/values-es-rUS/strings.xml +++ b/core/res/res/values-es-rUS/strings.xml @@ -2408,8 +2408,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Espacio privado"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clon"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Compartido"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Espacio privado"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Se ocultó contenido sensible de la notificación"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Se ocultó el contenido de la app durante el uso compartido de la pantalla por motivos de seguridad"</string> diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml index 19c5f842acc7..e22430ff072d 100644 --- a/core/res/res/values-es/strings.xml +++ b/core/res/res/values-es/strings.xml @@ -717,7 +717,7 @@ <string name="face_acquired_too_right" msgid="6245286514593540859">"Mueve el teléfono hacia la izquierda"</string> <string name="face_acquired_too_left" msgid="9201762240918405486">"Mueve el teléfono hacia la derecha"</string> <string name="face_acquired_poor_gaze" msgid="4427153558773628020">"Mira de forma más directa al dispositivo."</string> - <string name="face_acquired_not_detected" msgid="1057966913397548150">"No se puede detectar tu cara. Sujeta el teléfono a la altura de los ojos."</string> + <string name="face_acquired_not_detected" msgid="1057966913397548150">"No se detecta tu cara. Sujeta el teléfono a la altura de los ojos."</string> <string name="face_acquired_too_much_motion" msgid="8199691445085189528">"El teléfono se mueve demasiado. Mantenlo quieto."</string> <string name="face_acquired_recalibrate" msgid="8724013080976469746">"Vuelve a registrar tu cara."</string> <string name="face_acquired_too_different" msgid="4505278456634706967">"Cara no reconocida. Inténtalo de nuevo."</string> @@ -2408,8 +2408,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Espacio privado"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clon"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Común"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Espacio privado"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Contenido sensible de la notificación oculto"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Contenido de la aplicación oculto en pantalla compartida por motivos de seguridad"</string> diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml index 46c7341f2725..979079d05762 100644 --- a/core/res/res/values-et/strings.xml +++ b/core/res/res/values-et/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privaatne ruum"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Kloon"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Ühine"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Privaatne ruum"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Märguande delikaatne sisu peideti"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Rakenduse sisu on ekraani jagamises turvalisuse huvides peidetud"</string> diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml index 3c47f68bad97..06855f5539ee 100644 --- a/core/res/res/values-eu/strings.xml +++ b/core/res/res/values-eu/strings.xml @@ -190,7 +190,7 @@ <string name="contentServiceTooManyDeletesNotificationDesc" msgid="4562226280528716090">"<xliff:g id="CONTENT_TYPE">%s</xliff:g> gehiegi ezabatzen saiatu zara."</string> <string name="low_memory" product="tablet" msgid="5557552311566179924">"Tabletaren memoria beteta dago. Tokia egiteko, ezabatu fitxategi batzuk."</string> <string name="low_memory" product="watch" msgid="3479447988234030194">"Erlojuaren memoria beteta dago. Tokia egiteko, ezabatu fitxategi batzuk."</string> - <string name="low_memory" product="tv" msgid="6663680413790323318">"Android TV gailuaren memoria beteta dago. Tokia egiteko, ezabatu fitxategi batzuk."</string> + <string name="low_memory" product="tv" msgid="6663680413790323318">"Android TV gailuko biltegia beteta dago. Tokia egiteko, ezabatu fitxategi batzuk."</string> <string name="low_memory" product="default" msgid="2539532364144025569">"Telefonoaren memoria beteta dago. Tokia egiteko, ezabatu fitxategi batzuk."</string> <string name="ssl_ca_cert_warning" msgid="7233573909730048571">"{count,plural, =1{Autoritate ziurtagiri-emaile bat dago instalatuta}other{Autoritate ziurtagiri-emaile bat baino gehiago daude instalatuta}}"</string> <string name="ssl_ca_cert_noti_by_unknown" msgid="4961102218216815242">"Hirugarren alderdi ezezagun baten arabera"</string> @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Eremu pribatua"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klona"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Partekatua"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Eremu pribatua"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Jakinarazpenaren kontuzko edukia ezkutatu da"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Aplikazioko edukia ezkutatu egin da pantaila partekatzeko eginbidetik, segurtasuna bermatzeko"</string> diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml index 3c50b070f9d3..b0e6e6ecd0a5 100644 --- a/core/res/res/values-fa/strings.xml +++ b/core/res/res/values-fa/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"فضای خصوصی"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"همسانهسازی"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"همگانی"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"فضای خصوصی"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"محتوای اعلان حساس پنهان شده است"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"بهدلایل امنیتی، محتوای برنامه از دید همرسانی صفحهنمایش پنهان شد"</string> diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml index 0e159f2a83a6..7e2fa9c7b14b 100644 --- a/core/res/res/values-fi/strings.xml +++ b/core/res/res/values-fi/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Yksityinen tila"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klooni"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Yhteinen"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Yksityinen tila"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Arkaluontoisen ilmoituksen sisältö piilotettu"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Sovelluksen sisältö piilotettu näytön jakamiselta turvallisuussyistä"</string> diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml index 7fca5e67a7bd..a21aefb2169e 100644 --- a/core/res/res/values-fr-rCA/strings.xml +++ b/core/res/res/values-fr-rCA/strings.xml @@ -2408,8 +2408,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Espace privé"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Commun"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Espace privé"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Le contenu confidentiel de la notification est masqué"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Le contenu de l\'application est masqué du Partage d\'écran par mesure de sécurité"</string> diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml index e08f4269eebd..486124e310b4 100644 --- a/core/res/res/values-fr/strings.xml +++ b/core/res/res/values-fr/strings.xml @@ -2408,8 +2408,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Espace privé"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Commun"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Espace privé"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Le contenu sensible de la notification a été masqué"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Le contenu de l\'appli est masqué lors du partage d\'écran pour des raisons de sécurité"</string> diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml index 1eec828b8ad1..9994a3e74a50 100644 --- a/core/res/res/values-gl/strings.xml +++ b/core/res/res/values-gl/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Espazo privado"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clonado"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Compartido"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Espazo privado"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Contido confidencial da notificación oculto"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Por motivos de seguranza, ocultouse o contido da aplicación para que no se mostre na pantalla compartida"</string> diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml index 303f00125ee1..3206242c0e47 100644 --- a/core/res/res/values-gu/strings.xml +++ b/core/res/res/values-gu/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"ખાનગી સ્પેસ"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"ક્લોન"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"કૉમ્યુનલ"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"ખાનગી સ્પેસ"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"સંવેદનશીલ માહિતીવાળા નોટિફિકેશનનું કન્ટેન્ટ છુપાવ્યું"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"સુરક્ષા માટે સ્ક્રીન શેર કરતી વખતે ઍપનું કન્ટેન્ટ છુપાવેલું છે"</string> diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml index 14d2bf7086c9..f7ae13e66984 100644 --- a/core/res/res/values-hi/strings.xml +++ b/core/res/res/values-hi/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"प्राइवेट स्पेस"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"क्लोन"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"कम्यूनिटी"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"प्राइवेट स्पेस"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"संवेदनशील जानकारी वाली सूचना का कॉन्टेंट छिपा है"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"स्क्रीन शेयर करने के दौरान सुरक्षा के लिए, ऐप्लिकेशन का कॉन्टेंट छिपाया गया"</string> diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml index e69a59d969cf..f6f7e8b27ba3 100644 --- a/core/res/res/values-hr/strings.xml +++ b/core/res/res/values-hr/strings.xml @@ -2408,8 +2408,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privatni prostor"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Zajedničko"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Privatni prostor"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Skriven je osjetljiv sadržaj obavijesti"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Sadržaj aplikacije sakriven je od dijeljenja zaslona radi sigurnosti"</string> diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml index f1ff23613ab6..51626166db1b 100644 --- a/core/res/res/values-hu/strings.xml +++ b/core/res/res/values-hu/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privát terület"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klón"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Közös"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Privát terület"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Bizalmas értesítéstartalom elrejtve"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"A biztonság érdekében a képernyőmegosztástól elrejtett alkalmazástartalom"</string> diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml index 40812068752a..f2b6932b1300 100644 --- a/core/res/res/values-hy/strings.xml +++ b/core/res/res/values-hy/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Մասնավոր տարածք"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Կլոն"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Ընդհանուր"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Մասնավոր տարածք"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Ծանուցման զգայուն բովանդակությունը թաքցված է"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Անվտանգության նկատառումներից ելնելով՝ հավելվածի բովանդակությունը թաքցվել է էկրանի ցուցադրումից"</string> diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml index 866e9468ae3b..d1a20f34c6d7 100644 --- a/core/res/res/values-in/strings.xml +++ b/core/res/res/values-in/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Ruang privasi"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Umum"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Ruang privasi"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Konten notifikasi sensitif disembunyikan"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Konten aplikasi disembunyikan dari berbagi layar untuk alasan keamanan"</string> diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml index 76ba64bc9d4f..c8da94810664 100644 --- a/core/res/res/values-is/strings.xml +++ b/core/res/res/values-is/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Leynirými"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Afrit"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Sameiginlegt"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Leynirými"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Viðkvæmt tilkynningaefni falið"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Efni forrits falið í skjádeilingu af öryggisástæðum"</string> diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml index 5e87dcec0736..403c522e24ca 100644 --- a/core/res/res/values-it/strings.xml +++ b/core/res/res/values-it/strings.xml @@ -2408,8 +2408,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Spazio privato"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Condiviso"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Spazio privato"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Contenuti sensibili della notifica nascosti"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Contenuti dell\'app nascosti dalla condivisione schermo per sicurezza"</string> diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml index 439565d5951c..21e9293a7e4f 100644 --- a/core/res/res/values-iw/strings.xml +++ b/core/res/res/values-iw/strings.xml @@ -2039,7 +2039,7 @@ <string name="pin_specific_target" msgid="7824671240625957415">"הצמדה של <xliff:g id="LABEL">%1$s</xliff:g>"</string> <string name="unpin_target" msgid="3963318576590204447">"ביטול הצמדה"</string> <string name="unpin_specific_target" msgid="3859828252160908146">"ביטול ההצמדה של <xliff:g id="LABEL">%1$s</xliff:g>"</string> - <string name="app_info" msgid="6113278084877079851">"פרטי אפליקציה"</string> + <string name="app_info" msgid="6113278084877079851">"פרטי האפליקציה"</string> <string name="negative_duration" msgid="1938335096972945232">"−<xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="demo_starting_message" msgid="6577581216125805905">"תהליך ההדגמה מתחיל…"</string> <string name="demo_restarting_message" msgid="1160053183701746766">"מתבצע איפוס של המכשיר…"</string> @@ -2408,8 +2408,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"המרחב הפרטי"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"שכפול"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"שיתופי"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"המרחב הפרטי"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"יש תוכן רגיש בהתראה שהוסתר"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"תוכן האפליקציה מוסתר משיתוף המסך מטעמי אבטחה"</string> diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml index fcaef68c6ebf..149f3cd02f54 100644 --- a/core/res/res/values-ja/strings.xml +++ b/core/res/res/values-ja/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"プライベート スペース"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"複製"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"共用"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"プライベート スペース"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"プライベートな通知内容は表示されません"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"セキュリティ上、画面共有ではアプリの内容は非表示となります"</string> diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml index 2b98c58ff272..ee1e89d8a40f 100644 --- a/core/res/res/values-ka/strings.xml +++ b/core/res/res/values-ka/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"კერძო სივრცე"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"კლონის შექმნა"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"საერთო"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"კერძო სივრცე"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"სენსიტიური შეტყობინების კონტენტი დამალულია"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"ეკრანის გაზიარებიდან აპის კონტენტი დამალულია უსაფრთხოების მიზნით"</string> diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml index 6e9eb1220b50..63457e000bce 100644 --- a/core/res/res/values-kk/strings.xml +++ b/core/res/res/values-kk/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Құпия кеңістік"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Клон"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Жалпы"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Құпия кеңістік"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Хабарландырудың құпия контенті жасырылған."</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Қауіпсіздік мақсатында қолданба контенті экранды көрсету кезінде жасырылды."</string> diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml index 70f59c1c9235..7294d4b0f3fd 100644 --- a/core/res/res/values-km/strings.xml +++ b/core/res/res/values-km/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"លំហឯកជន"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"ក្លូន"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"ទូទៅ"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"លំហឯកជន"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"បានលាក់ខ្លឹមសារជូនដំណឹងដែលមានលក្ខណៈរសើប"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"បានលាក់ខ្លឹមសារកម្មវិធីពីការបង្ហាញអេក្រង់ដើម្បីសុវត្ថិភាព"</string> diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml index f22c43a4dd03..3d7c7823f4d6 100644 --- a/core/res/res/values-kn/strings.xml +++ b/core/res/res/values-kn/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"ಪ್ರೈವೆಟ್ ಸ್ಪೇಸ್"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"ಕ್ಲೋನ್"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"ಸಮುದಾಯ"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"ಪ್ರೈವೆಟ್ ಸ್ಪೇಸ್"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"ಸೂಕ್ಷ್ಮ ನೋಟಿಫಿಕೇಶನ್ ಕಂಟೆಂಟ್ ಅನ್ನು ಮರೆಮಾಡಲಾಗಿದೆ"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"ಭದ್ರತೆಗಾಗಿ ಸ್ಕ್ರೀನ್ ಹಂಚಿಕೊಳ್ಳುವಿಕೆಯಲ್ಲಿ ಆ್ಯಪ್ ಕಂಟೆಂಟ್ ಅನ್ನು ಮರೆಮಾಡಲಾಗಿದೆ"</string> diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml index 99b871579c1c..d815fe8f82bd 100644 --- a/core/res/res/values-ko/strings.xml +++ b/core/res/res/values-ko/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"비공개 스페이스"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"클론"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"공동"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"비공개 스페이스"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"민감한 알림 콘텐츠 숨김"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"보안을 위해 화면 공유에서 앱 콘텐츠가 숨겨집니다."</string> diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml index 427010b0e356..3cdf3cad862a 100644 --- a/core/res/res/values-ky/strings.xml +++ b/core/res/res/values-ky/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Жеке мейкиндик"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Клон"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Жалпы"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Жеке мейкиндик"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Купуя билдирменин мазмуну жашырылган"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Коопсуздук үчүн колдонмодогу контент бөлүшүлгөн экрандан жашырылды"</string> diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml index 4682bb0f1a93..80e09a6b4a1e 100644 --- a/core/res/res/values-lo/strings.xml +++ b/core/res/res/values-lo/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"ພື້ນທີ່ສ່ວນບຸກຄົນ"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"ໂຄລນ"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"ສ່ວນກາງ"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"ພື້ນທີ່ສ່ວນບຸກຄົນ"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"ເນື້ອຫາການແຈ້ງເຕືອນທີ່ລະອຽດອ່ອນເຊື່ອງຢູ່"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"ເນື້ອຫາແອັບຖືກເຊື່ອງໄວ້ຈາກການແບ່ງປັນໜ້າຈໍເພື່ອຄວາມປອດໄພ"</string> diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml index 6fbb9b829164..b2372414614a 100644 --- a/core/res/res/values-lt/strings.xml +++ b/core/res/res/values-lt/strings.xml @@ -2409,8 +2409,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privati erdvė"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klonuoti"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Bendruomenės"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Privati erdvė"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Neskelbtinos informacijos pranešimo turinys paslėptas"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Programos turinys paslėptas bendrinant ekraną saugumo sumetimais"</string> diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml index 0bea4ff64fbf..cc902cd9ab40 100644 --- a/core/res/res/values-lv/strings.xml +++ b/core/res/res/values-lv/strings.xml @@ -2408,8 +2408,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privātā telpa"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klons"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Kopīgs"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Privātā telpa"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Sensitīvs paziņojuma saturs ir paslēpts"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Drošības nolūkos lietotnes saturs kopīgotajā ekrānā ir paslēpts"</string> diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml index d7a6dd2902cc..b61b817dcf9c 100644 --- a/core/res/res/values-mk/strings.xml +++ b/core/res/res/values-mk/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Приватен простор"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Клониран профил"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Профил на заедницата"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Приватен простор"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Содржината на чувствителните известувања е скриена"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Од безбедносни причини, содржините на апликацијата се скриени од споделувањето екран"</string> diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml index 402333699754..9906470edbcf 100644 --- a/core/res/res/values-ml/strings.xml +++ b/core/res/res/values-ml/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"സ്വകാര്യ സ്പേസ്"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"ക്ലോൺ ചെയ്യുക"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"കമ്മ്യൂണൽ"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"സ്വകാര്യ സ്പേസ്"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"സൂക്ഷ്മമായി കൈകാര്യം ചെയ്യേണ്ട അറിയിപ്പ് ഉള്ളടക്കം മറച്ചിരിക്കുന്നു"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"ആപ്പ് ഉള്ളടക്കം, അതിന്റെ സുരക്ഷയ്ക്കായി സ്ക്രീൻ പങ്കിടലിൽ നിന്ന് മറച്ചിരിക്കുന്നു"</string> diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml index 9aa18e2d4b82..22f8f15ae8da 100644 --- a/core/res/res/values-mn/strings.xml +++ b/core/res/res/values-mn/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Хаалттай орон зай"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Клон"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Нийтийн"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Хаалттай орон зай"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Эмзэг мэдэгдлийн контентыг нуусан"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Аюулгүй байдлын улмаас аппын контентыг дэлгэц хуваалцахаас нуусан"</string> diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml index a115e2c61724..36d0bba7e17f 100644 --- a/core/res/res/values-mr/strings.xml +++ b/core/res/res/values-mr/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"खाजगी स्पेस"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"क्लोन"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"सामुदायिक"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"खाजगी स्पेस"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"संवेदनशील नोटिफिकेशनचा आशय लपवलेला आहे"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"स्क्रीन शेअर करताना सुरक्षेसाठी अॅपमधील आशय लपवला आहे"</string> diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml index 2fa571ceb8a0..c215f62e7aa1 100644 --- a/core/res/res/values-ms/strings.xml +++ b/core/res/res/values-ms/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Ruang persendirian"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Umum"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Ruang privasi"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Kandungan pemberitahuan yang sensitif disembunyikan"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Kandungan apl disembunyikan daripada perkongsian skrin untuk keselamatan"</string> diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml index 364a7b169c50..f030ef2ad024 100644 --- a/core/res/res/values-my/strings.xml +++ b/core/res/res/values-my/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"သီးသန့်နေရာ"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"ပုံတူပွားရန်"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"အများသုံး"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"သီးသန့်နေရာ"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"သတိထားရမည့် အကြောင်းကြားချက်ပါ အချက်အလက်ကို ဖျောက်ထားသည်"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"အက်ပ်အကြောင်းအရာသည် လုံခြုံရေးအတွက် မျက်နှာပြင် မျှဝေခြင်းမှ ဖျောက်ထားသည်"</string> diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml index e2de428b1e93..116c586cb628 100644 --- a/core/res/res/values-nb/strings.xml +++ b/core/res/res/values-nb/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privat område"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Felles"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Privat område"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Sensitivt varselinnhold er skjult"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Av sikkerhetsgrunner er appinnholdet skjult for skjermdelingen"</string> diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml index 8d5f560d521a..a202d4c4d81c 100644 --- a/core/res/res/values-ne/strings.xml +++ b/core/res/res/values-ne/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"निजी स्पेस"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"क्लोन"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"सामुदायिक"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"निजी स्पेस"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"संवेदनशील सूचनासम्बन्धी सामग्री लुकाइएको छ"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"स्क्रिन सेयर गर्दा सुरक्षाका लागि एपमा भएको सामग्री लुकाइएको छ"</string> diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml index 5bc6c4421339..51d895987fea 100644 --- a/core/res/res/values-nl/strings.xml +++ b/core/res/res/values-nl/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privégedeelte"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Kloon"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Gemeenschappelijk"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Privégedeelte"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Content van gevoelige meldingen verborgen"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"App-content verborgen voor scherm delen vanwege beveiligingsrisico\'s"</string> diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml index 4c7868d03914..90ab620fb710 100644 --- a/core/res/res/values-or/strings.xml +++ b/core/res/res/values-or/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"ପ୍ରାଇଭେଟ ସ୍ପେସ"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"କ୍ଲୋନ"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"କମ୍ୟୁନାଲ"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"ପ୍ରାଇଭେଟ ସ୍ପେସ"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"ସମ୍ୱେଦନଶୀଳ ବିଜ୍ଞପ୍ତି ବିଷୟବସ୍ତୁକୁ ଲୁଚାଯାଇଛି"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"ସୁରକ୍ଷା ପାଇଁ ସ୍କ୍ରିନ ସେୟାରରୁ ଆପ ବିଷୟବସ୍ତୁକୁ ଲୁଚାଯାଇଛି"</string> diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml index 38780b610f18..de91ea7ec24a 100644 --- a/core/res/res/values-pa/strings.xml +++ b/core/res/res/values-pa/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"ਪ੍ਰਾਈਵੇਟ ਸਪੇਸ"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"ਕਲੋਨ"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"ਭਾਈਚਾਰਕ"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"ਪ੍ਰਾਈਵੇਟ ਸਪੇਸ"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"ਲੁਕੀ ਹੋਈ ਸੰਵੇਦਨਸ਼ੀਲ ਸੂਚਨਾ ਸਮੱਗਰੀ"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"ਐਪ ਸਮੱਗਰੀ ਨੂੰ ਸੁਰੱਖਿਆ ਲਈ ਸਕ੍ਰੀਨ ਸਾਂਝਾਕਰਨ ਤੋਂ ਲੁਕਾਇਆ ਗਿਆ ਹੈ"</string> diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml index 95e1a30a6dd4..d9a95b898356 100644 --- a/core/res/res/values-pl/strings.xml +++ b/core/res/res/values-pl/strings.xml @@ -2409,8 +2409,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Przestrzeń prywatna"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Wspólny"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Przestrzeń prywatna"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Treść poufnego powiadomienia została ukryta"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Ze względów bezpieczeństwa zawartość aplikacji jest niewidoczna podczas udostępniania ekranu"</string> diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml index c9d36b149fdf..cd3a7c55bed8 100644 --- a/core/res/res/values-pt-rBR/strings.xml +++ b/core/res/res/values-pt-rBR/strings.xml @@ -2408,8 +2408,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Espaço privado"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Público"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Espaço privado"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Conteúdo de notificação sensível oculto"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Conteúdo do app oculto no compartilhamento de tela por motivos de segurança"</string> diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml index f1b2bd07acca..d8fe4fe8c2e1 100644 --- a/core/res/res/values-pt-rPT/strings.xml +++ b/core/res/res/values-pt-rPT/strings.xml @@ -733,7 +733,7 @@ <skip /> <string name="face_acquired_recalibrate_alt" msgid="5702674220280332115">"Não é possível criar o seu modelo de rosto. Tente novamente."</string> <string name="face_acquired_dark_glasses_detected_alt" msgid="4052123776406041972">"Óculos escuros detetados. O seu rosto tem de estar completamente visível."</string> - <string name="face_acquired_mouth_covering_detected_alt" msgid="1122294982850589766">"Máscara detetada. Todo o rosto tem de estar visível"</string> + <string name="face_acquired_mouth_covering_detected_alt" msgid="1122294982850589766">"Cobertura facial detetada. O seu rosto tem de estar completamente visível."</string> <string-array name="face_acquired_vendor"> </string-array> <string name="face_error_hw_not_available" msgid="5085202213036026288">"Não pode validar o rosto. Hardware não disponível."</string> @@ -1292,7 +1292,7 @@ <string name="android_upgrading_complete" msgid="409800058018374746">"A concluir o arranque."</string> <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Premiu o botão ligar/desligar. Geralmente, esta ação desliga o ecrã.\n\nExperimente tocar levemente ao configurar a sua impressão digital."</string> <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"Para terminar, desligue o ecrã"</string> - <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"Desligar"</string> + <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"Desativar"</string> <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Continuar a validar a impressão digital?"</string> <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Premiu o botão ligar/desligar. Geralmente, esta ação desliga o ecrã.\n\nExperimente tocar levemente para validar a sua impressão digital."</string> <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Desligar ecrã"</string> @@ -1438,7 +1438,7 @@ <string name="alert_windows_notification_channel_name" msgid="3437528564303192620">"A app <xliff:g id="NAME">%s</xliff:g> sobrepõe-se a outras aplicações"</string> <string name="alert_windows_notification_title" msgid="6331662751095228536">"O <xliff:g id="NAME">%s</xliff:g> sobrepõe-se a outras app"</string> <string name="alert_windows_notification_message" msgid="6538171456970725333">"Se não quer que a app <xliff:g id="NAME">%s</xliff:g> utilize esta funcionalidade, toque para abrir as definições e desative-a."</string> - <string name="alert_windows_notification_turn_off_action" msgid="7805857234839123780">"Desligar"</string> + <string name="alert_windows_notification_turn_off_action" msgid="7805857234839123780">"Desativar"</string> <string name="ext_media_checking_notification_title" msgid="8299199995416510094">"A verificar o <xliff:g id="NAME">%s</xliff:g>…"</string> <string name="ext_media_checking_notification_message" msgid="2231566971425375542">"A rever o conteúdo atual…"</string> <string name="ext_media_checking_notification_message" product="tv" msgid="7986154434946021415">"A analisar o armazenamento de multimédia"</string> @@ -2408,8 +2408,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Espaço privado"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Comum"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Espaço privado"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Conteúdo das notificações sensíveis ocultado"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Conteúdo da app ocultado da partilha de ecrã por motivos de segurança"</string> diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml index c9d36b149fdf..cd3a7c55bed8 100644 --- a/core/res/res/values-pt/strings.xml +++ b/core/res/res/values-pt/strings.xml @@ -2408,8 +2408,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Espaço privado"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Público"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Espaço privado"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Conteúdo de notificação sensível oculto"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Conteúdo do app oculto no compartilhamento de tela por motivos de segurança"</string> diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml index e14aec706cb7..d43556d47f86 100644 --- a/core/res/res/values-ro/strings.xml +++ b/core/res/res/values-ro/strings.xml @@ -2408,8 +2408,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Spațiu privat"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clonă"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Comun"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Spațiu privat"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Conținutul sensibil din notificări a fost ascuns"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Conținutul aplicației este ascuns de permiterea accesului la ecran din motive de securitate"</string> diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml index 79b0daa445fa..16ddae82f144 100644 --- a/core/res/res/values-ru/strings.xml +++ b/core/res/res/values-ru/strings.xml @@ -2409,8 +2409,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Частное пространство"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Клонированный"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Совместный"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Частное пространство"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Конфиденциальная информация в уведомлении скрыта"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Содержимое приложения исключено из демонстрации экрана в целях безопасности."</string> diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml index 1e69e0bc9911..4f6e756d569e 100644 --- a/core/res/res/values-si/strings.xml +++ b/core/res/res/values-si/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"රහසිගත අවකාශය"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"ක්ලෝන කරන්න"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"වාර්ගික"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"රහසිගත අවකාශය"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"සංවේදී දැනුම්දීම් අන්තර්ගතය සැඟවී ඇත"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"ආරක්ෂාව සඳහා යෙදුම් අන්තර්ගතය තිරය බෙදා ගැනීමෙන් සඟවා ඇත"</string> diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml index c7ca9f471942..aeea7b688dbb 100644 --- a/core/res/res/values-sk/strings.xml +++ b/core/res/res/values-sk/strings.xml @@ -2040,7 +2040,7 @@ <string name="pin_specific_target" msgid="7824671240625957415">"Pripnúť <xliff:g id="LABEL">%1$s</xliff:g>"</string> <string name="unpin_target" msgid="3963318576590204447">"Uvoľniť"</string> <string name="unpin_specific_target" msgid="3859828252160908146">"Odopnúť <xliff:g id="LABEL">%1$s</xliff:g>"</string> - <string name="app_info" msgid="6113278084877079851">"Info o aplikácii"</string> + <string name="app_info" msgid="6113278084877079851">"Informácie o aplikácii"</string> <string name="negative_duration" msgid="1938335096972945232">"-<xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="demo_starting_message" msgid="6577581216125805905">"Spúšťa sa ukážka…"</string> <string name="demo_restarting_message" msgid="1160053183701746766">"Resetuje sa zariadenie…"</string> @@ -2409,8 +2409,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Súkromný priestor"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Spoločný"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Súkromný priestor"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Obsah citlivého upozornenia je skrytý"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Obsah aplikácie bol na účely zabezpečenia skrytý v zdieľaní obrazovky"</string> diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml index 4f23a546376d..d3ce57cdb28b 100644 --- a/core/res/res/values-sl/strings.xml +++ b/core/res/res/values-sl/strings.xml @@ -2409,8 +2409,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Zasebni prostor"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Skupno"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Zasebni prostor"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Občutljiva vsebina obvestila je bila skrita"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Pri deljenju zaslona je vsebina aplikacije skrita zaradi varnosti"</string> diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml index c49402bc2cc5..de0210013732 100644 --- a/core/res/res/values-sq/strings.xml +++ b/core/res/res/values-sq/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Hapësira private"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"I përbashkët"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Hapësira private"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Përmbajtjet delikate të njoftimeve janë fshehur"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Përmbajtja e aplikacionit është fshehur nga ndarja e ekranit për arsye sigurie"</string> diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml index 2b3f75a60555..f200ac16c798 100644 --- a/core/res/res/values-sr/strings.xml +++ b/core/res/res/values-sr/strings.xml @@ -2408,8 +2408,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Приватан простор"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Клонирано"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Заједничко"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Приватан простор"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Осетљив садржај обавештења је скривен"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Садржај апликације је скривен за дељење садржаја екрана због безбедности"</string> diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml index 28333815e1d0..589414da6291 100644 --- a/core/res/res/values-sv/strings.xml +++ b/core/res/res/values-sv/strings.xml @@ -2038,7 +2038,7 @@ <string name="pin_specific_target" msgid="7824671240625957415">"Fäst <xliff:g id="LABEL">%1$s</xliff:g>"</string> <string name="unpin_target" msgid="3963318576590204447">"Lossa"</string> <string name="unpin_specific_target" msgid="3859828252160908146">"Lossa <xliff:g id="LABEL">%1$s</xliff:g>"</string> - <string name="app_info" msgid="6113278084877079851">"Info om appen"</string> + <string name="app_info" msgid="6113278084877079851">"Appinformation"</string> <string name="negative_duration" msgid="1938335096972945232">"-<xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="demo_starting_message" msgid="6577581216125805905">"Demo startas …"</string> <string name="demo_restarting_message" msgid="1160053183701746766">"Enheten återställs …"</string> @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privat område"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klona"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Allmän"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Privat område"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Känsligt aviseringsinnehåll dolt"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Av säkerhetsskäl döljs appinnehållet vid skärmdelning"</string> diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml index d3af99f7798f..4b50cb7366ef 100644 --- a/core/res/res/values-sw/strings.xml +++ b/core/res/res/values-sw/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Sehemu ya faragha"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Nakala"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Wasifu wa pamoja"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Sehemu ya faragha"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Maudhui nyeti kwenye arifa yamefichwa"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Maudhui ya programu yamefichwa ili yasionekane kwenye skrini ya pamoja kwa sababu za kiusalama"</string> diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml index 37abcef212cf..6ad8f59e0903 100644 --- a/core/res/res/values-ta/strings.xml +++ b/core/res/res/values-ta/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"ரகசிய இடம்"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"குளோன்"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"பொது"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"ரகசிய இடம்"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"உணர்வுபூர்வமான அறிவிப்பு உள்ளடக்கம் மறைக்கப்பட்டது"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"பாதுகாப்பிற்காக, திரைப் பகிர்வில் இருந்து ஆப்ஸ் உள்ளடக்கம் மறைக்கப்பட்டுள்ளது"</string> diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml index 2766248dabbd..91f9ef200a5c 100644 --- a/core/res/res/values-te/strings.xml +++ b/core/res/res/values-te/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"ప్రైవేట్ స్పేస్"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"క్లోన్"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"కమ్యూనల్"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"ప్రైవేట్ స్పేస్"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"గోప్యమైన నోటిఫికేషన్ కంటెంట్ దాచబడింది"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"సెక్యూరిటీ కోసం స్క్రీన్ షేర్ నుండి యాప్ కంటెంట్ దాచబడింది"</string> diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml index 1f2a0911ff5f..eb102fc79966 100644 --- a/core/res/res/values-th/strings.xml +++ b/core/res/res/values-th/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"พื้นที่ส่วนตัว"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"โคลน"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"ส่วนกลาง"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"พื้นที่ส่วนตัว"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"เนื้อหาการแจ้งเตือนที่ละเอียดอ่อนซ่อนอยู่"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"ซ่อนเนื้อหาแอปจากการแชร์หน้าจอเพื่อความปลอดภัย"</string> diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml index 7bce4b78c437..de499d346524 100644 --- a/core/res/res/values-tl/strings.xml +++ b/core/res/res/values-tl/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Pribadong space"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Communal"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Pribadong space"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Nakatago ang content ng sensitibong notification"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Nakatago ang content ng app mula sa pagbabahagi ng screen para sa seguridad"</string> diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml index cbfb77037e73..7dab2360da04 100644 --- a/core/res/res/values-tr/strings.xml +++ b/core/res/res/values-tr/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Özel alan"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Paylaşılan"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Özel alan"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Hassas bildirim içerikleri gizlendi"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Uygulama içerikleri, güvenlik nedeniyle ekran paylaşımında gizlendi"</string> diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml index 5266acde1ecb..22c72727537b 100644 --- a/core/res/res/values-uk/strings.xml +++ b/core/res/res/values-uk/strings.xml @@ -2409,8 +2409,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Приватний простір"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Копія профілю"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Спільний профіль"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Приватний простір"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Чутливий вміст сповіщення приховано"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"З міркувань безпеки вміст додатка приховано під час показу екрана"</string> diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml index 2fe52f83d09a..0d878c9d1346 100644 --- a/core/res/res/values-ur/strings.xml +++ b/core/res/res/values-ur/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"پرائیویٹ اسپیس"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"کلون"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"کمیونل"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"پرائیویٹ اسپیس"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"حساس اطلاعی مواد چھپا ہوا ہے"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"سیکیورٹی کے مد نظر ایپ کا مواد اسکرین کے اشتراک سے چھپا ہوا ہے"</string> diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml index d40f38338138..fc5d8ee32520 100644 --- a/core/res/res/values-uz/strings.xml +++ b/core/res/res/values-uz/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Maxfiy makon"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Nusxalash"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Umumiy"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Maxfiy makon"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Bildirishnomadagi maxfiy axborot berkitildi"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Ekran namoyishida xavfsizlik maqsadida ilova kontenti berkitildi"</string> diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml index 1051cdab6599..4619be37f2ad 100644 --- a/core/res/res/values-vi/strings.xml +++ b/core/res/res/values-vi/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Không gian riêng tư"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Nhân bản"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Dùng chung"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Không gian riêng tư"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Đã ẩn nội dung thông báo nhạy cảm"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Nội dung ứng dụng bị ẩn khỏi tính năng chia sẻ màn hình vì lý do bảo mật"</string> diff --git a/core/res/res/values-w204dp-round-watch/dimens_material.xml b/core/res/res/values-w204dp-round-watch/dimens_material.xml new file mode 100644 index 000000000000..c07d5c47e2e1 --- /dev/null +++ b/core/res/res/values-w204dp-round-watch/dimens_material.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> + <dimen name="screen_percentage_05">10.2dp</dimen> + <dimen name="screen_percentage_10">20.4dp</dimen> + <dimen name="screen_percentage_15">30.6dp</dimen> +</resources> diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml index d16a353e9bfc..a6c62dc826ce 100644 --- a/core/res/res/values-zh-rCN/strings.xml +++ b/core/res/res/values-zh-rCN/strings.xml @@ -581,7 +581,7 @@ <string name="permlab_changeTetherState" msgid="9079611809931863861">"更改网络共享连接"</string> <string name="permdesc_changeTetherState" msgid="3025129606422533085">"允许应用更改绑定网络连接的状态。"</string> <string name="permlab_accessWifiState" msgid="5552488500317911052">"查看WLAN连接"</string> - <string name="permdesc_accessWifiState" msgid="6913641669259483363">"允许该应用查看WLAN网络的相关信息,例如是否启用了WLAN以及连接的WLAN设备的名称。"</string> + <string name="permdesc_accessWifiState" msgid="6913641669259483363">"允许该应用查看 WLAN 网络的相关信息,例如是否启用了 WLAN 以及连接的 WLAN 设备的名称。"</string> <string name="permlab_changeWifiState" msgid="7947824109713181554">"连接WLAN网络和断开连接"</string> <string name="permdesc_changeWifiState" msgid="7170350070554505384">"允许该应用与WLAN接入点建立和断开连接,以及更改WLAN网络的设备配置。"</string> <string name="permlab_changeWifiMulticastState" msgid="285626875870754696">"允许接收WLAN多播"</string> @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"私密空间"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"克隆"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"共用"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"私密空间"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"已隐藏敏感通知内容"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"为安全起见而在屏幕共享画面中处于隐藏状态的应用内容"</string> diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml index 672a638e6932..f9b10a2dac52 100644 --- a/core/res/res/values-zh-rHK/strings.xml +++ b/core/res/res/values-zh-rHK/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"私人空間"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"複製"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"共用"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"私人空間"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"已隱藏敏感通知內容"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"為安全起見,應用程式內容已從分享螢幕畫面隱藏"</string> diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml index 8a224ed24675..4dce59ada110 100644 --- a/core/res/res/values-zh-rTW/strings.xml +++ b/core/res/res/values-zh-rTW/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"私人空間"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"複製"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"共通"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"私人空間"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"系統已隱藏含有私密資訊的通知內容"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"為安全起見,分享螢幕畫面未顯示應用程式內容"</string> diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml index 27bd3c949c14..60cb31b4db5c 100644 --- a/core/res/res/values-zu/strings.xml +++ b/core/res/res/values-zu/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Indawo engasese"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Yenza i-Clone"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Okomphakathi"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Indawo engasese"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Okuqukethwe kwesaziso esizwelayo kufihliwe"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Okuqukethwe kwe-app kufihliwe kusuka ekwabelaneni kwesikrini ngokuvikelwa"</string> diff --git a/core/tests/coretests/src/android/widget/RemoteViewsTest.java b/core/tests/coretests/src/android/widget/RemoteViewsTest.java index 15c90474c017..c8ea3742b3aa 100644 --- a/core/tests/coretests/src/android/widget/RemoteViewsTest.java +++ b/core/tests/coretests/src/android/widget/RemoteViewsTest.java @@ -34,6 +34,9 @@ import android.app.PendingIntent; import android.appwidget.AppWidgetHostView; import android.content.Context; import android.content.Intent; +import android.graphics.Bitmap; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; import android.graphics.drawable.Icon; import android.net.Uri; import android.os.AsyncTask; @@ -834,33 +837,6 @@ public class RemoteViewsTest { } @Test - public void visitUris_intents() { - RemoteViews views = new RemoteViews(mPackage, R.layout.remote_views_test); - - Uri fillIntentUri = Uri.parse("content://intent/fill"); - views.setOnCheckedChangeResponse( - R.id.layout, - RemoteViews.RemoteResponse.fromFillInIntent(new Intent("action", fillIntentUri))); - - Uri pendingIntentUri = Uri.parse("content://intent/pending"); - PendingIntent pendingIntent = getPendingIntentWithUri(pendingIntentUri); - views.setOnClickResponse( - R.id.layout, - RemoteViews.RemoteResponse.fromPendingIntent(pendingIntent)); - - Consumer<Uri> visitor = (Consumer<Uri>) spy(Consumer.class); - views.visitUris(visitor); - verify(visitor, times(1)).accept(eq(fillIntentUri)); - verify(visitor, times(1)).accept(eq(pendingIntentUri)); - } - - private PendingIntent getPendingIntentWithUri(Uri uri) { - return PendingIntent.getActivity(mContext, 0, - new Intent("action", uri), - PendingIntent.FLAG_IMMUTABLE); - } - - @Test public void layoutInflaterFactory_nothingSet_returnsNull() { final RemoteViews rv = new RemoteViews(mPackage, R.layout.remote_views_test); assertNull(rv.getLayoutInflaterFactory()); diff --git a/graphics/java/android/framework_graphics.aconfig b/graphics/java/android/framework_graphics.aconfig index 0b9e72d6ffdd..5ad97e6d8992 100644 --- a/graphics/java/android/framework_graphics.aconfig +++ b/graphics/java/android/framework_graphics.aconfig @@ -24,3 +24,12 @@ flag { description: "Return null when decode from URI fails in Icon.loadDrawable()" bug: "335878768" } + +flag { + name: "ok_lab_colorspace" + is_exported: true + is_fixed_read_only: true + namespace: "core_graphics" + description: "Add OkLab ColorSpace support" + bug: "344038816" +} diff --git a/graphics/java/android/graphics/ColorSpace.java b/graphics/java/android/graphics/ColorSpace.java index 4bc3ecebb067..3752257da2bc 100644 --- a/graphics/java/android/graphics/ColorSpace.java +++ b/graphics/java/android/graphics/ColorSpace.java @@ -17,6 +17,7 @@ package android.graphics; import android.annotation.AnyThread; +import android.annotation.FlaggedApi; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; @@ -27,9 +28,13 @@ import android.hardware.DataSpace; import android.hardware.DataSpace.ColorDataSpace; import android.util.SparseIntArray; +import com.android.graphics.flags.Flags; + import libcore.util.NativeAllocationRegistry; import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; import java.util.function.DoubleUnaryOperator; /** @@ -230,7 +235,9 @@ public abstract class ColorSpace { -2392 / 128.0, 8192 / 1305.0, Rgb.TransferParameters.TYPE_PQish); // See static initialization block next to #get(Named) - private static final ColorSpace[] sNamedColorSpaces = new ColorSpace[Named.values().length]; + private static final HashMap<Integer, ColorSpace> sNamedColorSpaceMap = + new HashMap<>(); + private static final SparseIntArray sDataToColorSpaces = new SparseIntArray(); @NonNull private final String mName; @@ -745,7 +752,23 @@ public abstract class ColorSpace { * <tr><td>Range</td><td colspan="4">\([0..1]\)</td></tr> * </table> */ - BT2020_PQ + BT2020_PQ, + + /** + * <p>{@link ColorSpace.Lab Lab} color space OkLab standardized as + * OkLab</p> + * <table summary="Color space definition"> + * <tr><th>Property</th><th colspan="4">Value</th></tr> + * <tr><td>Name</td><td colspan="4">Oklab</td></tr> + * <tr><td>CIE standard illuminant</td><td colspan="4">D65</td></tr> + * <tr> + * <td>Range</td> + * <td colspan="4">\(L: `[0.0, 1.0]`, a: `[-2, 2]`, b: `[-2, 2]`\)</td> + * </tr> + * </table> + */ + @FlaggedApi(Flags.FLAG_OK_LAB_COLORSPACE) + OK_LAB // Update the initialization block next to #get(Named) when adding new values } @@ -1428,11 +1451,11 @@ public abstract class ColorSpace { */ @NonNull static ColorSpace get(@IntRange(from = MIN_ID, to = MAX_ID) int index) { - if (index < 0 || index >= sNamedColorSpaces.length) { - throw new IllegalArgumentException("Invalid ID, must be in the range [0.." + - sNamedColorSpaces.length + ")"); + ColorSpace colorspace = sNamedColorSpaceMap.get(index); + if (colorspace == null) { + throw new IllegalArgumentException("Invalid ID: " + index); } - return sNamedColorSpaces[index]; + return colorspace; } /** @@ -1485,12 +1508,20 @@ public abstract class ColorSpace { * * <p>This method is thread-safe.</p> * + * Note that in the Android W release and later, this can return the SRGB ColorSpace if + * the {@link ColorSpace.Named} parameter is not enabled in the corresponding release. + * * @param name The name of the color space to get an instance of - * @return A non-null {@link ColorSpace} instance + * @return A non-null {@link ColorSpace} instance. If the ColorSpace is not supported + * then the SRGB ColorSpace is returned. */ @NonNull public static ColorSpace get(@NonNull Named name) { - return sNamedColorSpaces[name.ordinal()]; + ColorSpace colorSpace = sNamedColorSpaceMap.get(name.ordinal()); + if (colorSpace == null) { + return sNamedColorSpaceMap.get(Named.SRGB.ordinal()); + } + return colorSpace; } /** @@ -1511,7 +1542,8 @@ public abstract class ColorSpace { @NonNull @Size(9) float[] toXYZD50, @NonNull Rgb.TransferParameters function) { - for (ColorSpace colorSpace : sNamedColorSpaces) { + Collection<ColorSpace> colorspaces = sNamedColorSpaceMap.values(); + for (ColorSpace colorSpace : colorspaces) { if (colorSpace.getModel() == Model.RGB) { ColorSpace.Rgb rgb = (ColorSpace.Rgb) adapt(colorSpace, ILLUMINANT_D50_XYZ); if (compare(toXYZD50, rgb.mTransform) && @@ -1525,25 +1557,25 @@ public abstract class ColorSpace { } static { - sNamedColorSpaces[Named.SRGB.ordinal()] = new ColorSpace.Rgb( + sNamedColorSpaceMap.put(Named.SRGB.ordinal(), new ColorSpace.Rgb( "sRGB IEC61966-2.1", SRGB_PRIMARIES, ILLUMINANT_D65, null, SRGB_TRANSFER_PARAMETERS, Named.SRGB.ordinal() - ); + )); sDataToColorSpaces.put(DataSpace.DATASPACE_SRGB, Named.SRGB.ordinal()); - sNamedColorSpaces[Named.LINEAR_SRGB.ordinal()] = new ColorSpace.Rgb( + sNamedColorSpaceMap.put(Named.LINEAR_SRGB.ordinal(), new ColorSpace.Rgb( "sRGB IEC61966-2.1 (Linear)", SRGB_PRIMARIES, ILLUMINANT_D65, 1.0, 0.0f, 1.0f, Named.LINEAR_SRGB.ordinal() - ); + )); sDataToColorSpaces.put(DataSpace.DATASPACE_SRGB_LINEAR, Named.LINEAR_SRGB.ordinal()); - sNamedColorSpaces[Named.EXTENDED_SRGB.ordinal()] = new ColorSpace.Rgb( + sNamedColorSpaceMap.put(Named.EXTENDED_SRGB.ordinal(), new ColorSpace.Rgb( "scRGB-nl IEC 61966-2-2:2003", SRGB_PRIMARIES, ILLUMINANT_D65, @@ -1553,112 +1585,113 @@ public abstract class ColorSpace { -0.799f, 2.399f, SRGB_TRANSFER_PARAMETERS, Named.EXTENDED_SRGB.ordinal() - ); + )); sDataToColorSpaces.put(DataSpace.DATASPACE_SCRGB, Named.EXTENDED_SRGB.ordinal()); - sNamedColorSpaces[Named.LINEAR_EXTENDED_SRGB.ordinal()] = new ColorSpace.Rgb( + sNamedColorSpaceMap.put(Named.LINEAR_EXTENDED_SRGB.ordinal(), new ColorSpace.Rgb( "scRGB IEC 61966-2-2:2003", SRGB_PRIMARIES, ILLUMINANT_D65, 1.0, -0.5f, 7.499f, Named.LINEAR_EXTENDED_SRGB.ordinal() - ); + )); sDataToColorSpaces.put( DataSpace.DATASPACE_SCRGB_LINEAR, Named.LINEAR_EXTENDED_SRGB.ordinal()); - sNamedColorSpaces[Named.BT709.ordinal()] = new ColorSpace.Rgb( + sNamedColorSpaceMap.put(Named.BT709.ordinal(), new ColorSpace.Rgb( "Rec. ITU-R BT.709-5", SRGB_PRIMARIES, ILLUMINANT_D65, null, SMPTE_170M_TRANSFER_PARAMETERS, Named.BT709.ordinal() - ); + )); sDataToColorSpaces.put(DataSpace.DATASPACE_BT709, Named.BT709.ordinal()); - sNamedColorSpaces[Named.BT2020.ordinal()] = new ColorSpace.Rgb( + sNamedColorSpaceMap.put(Named.BT2020.ordinal(), new ColorSpace.Rgb( "Rec. ITU-R BT.2020-1", BT2020_PRIMARIES, ILLUMINANT_D65, null, new Rgb.TransferParameters(1 / 1.0993, 0.0993 / 1.0993, 1 / 4.5, 0.08145, 1 / 0.45), Named.BT2020.ordinal() - ); + )); + sDataToColorSpaces.put(DataSpace.DATASPACE_BT2020, Named.BT2020.ordinal()); - sNamedColorSpaces[Named.DCI_P3.ordinal()] = new ColorSpace.Rgb( + sNamedColorSpaceMap.put(Named.DCI_P3.ordinal(), new ColorSpace.Rgb( "SMPTE RP 431-2-2007 DCI (P3)", DCI_P3_PRIMARIES, new float[] { 0.314f, 0.351f }, 2.6, 0.0f, 1.0f, Named.DCI_P3.ordinal() - ); + )); sDataToColorSpaces.put(DataSpace.DATASPACE_DCI_P3, Named.DCI_P3.ordinal()); - sNamedColorSpaces[Named.DISPLAY_P3.ordinal()] = new ColorSpace.Rgb( + sNamedColorSpaceMap.put(Named.DISPLAY_P3.ordinal(), new ColorSpace.Rgb( "Display P3", DCI_P3_PRIMARIES, ILLUMINANT_D65, null, SRGB_TRANSFER_PARAMETERS, Named.DISPLAY_P3.ordinal() - ); + )); sDataToColorSpaces.put(DataSpace.DATASPACE_DISPLAY_P3, Named.DISPLAY_P3.ordinal()); - sNamedColorSpaces[Named.NTSC_1953.ordinal()] = new ColorSpace.Rgb( + sNamedColorSpaceMap.put(Named.NTSC_1953.ordinal(), new ColorSpace.Rgb( "NTSC (1953)", NTSC_1953_PRIMARIES, ILLUMINANT_C, null, SMPTE_170M_TRANSFER_PARAMETERS, Named.NTSC_1953.ordinal() - ); - sNamedColorSpaces[Named.SMPTE_C.ordinal()] = new ColorSpace.Rgb( + )); + sNamedColorSpaceMap.put(Named.SMPTE_C.ordinal(), new ColorSpace.Rgb( "SMPTE-C RGB", new float[] { 0.630f, 0.340f, 0.310f, 0.595f, 0.155f, 0.070f }, ILLUMINANT_D65, null, SMPTE_170M_TRANSFER_PARAMETERS, Named.SMPTE_C.ordinal() - ); - sNamedColorSpaces[Named.ADOBE_RGB.ordinal()] = new ColorSpace.Rgb( + )); + sNamedColorSpaceMap.put(Named.ADOBE_RGB.ordinal(), new ColorSpace.Rgb( "Adobe RGB (1998)", new float[] { 0.64f, 0.33f, 0.21f, 0.71f, 0.15f, 0.06f }, ILLUMINANT_D65, 2.2, 0.0f, 1.0f, Named.ADOBE_RGB.ordinal() - ); + )); sDataToColorSpaces.put(DataSpace.DATASPACE_ADOBE_RGB, Named.ADOBE_RGB.ordinal()); - sNamedColorSpaces[Named.PRO_PHOTO_RGB.ordinal()] = new ColorSpace.Rgb( + sNamedColorSpaceMap.put(Named.PRO_PHOTO_RGB.ordinal(), new ColorSpace.Rgb( "ROMM RGB ISO 22028-2:2013", new float[] { 0.7347f, 0.2653f, 0.1596f, 0.8404f, 0.0366f, 0.0001f }, ILLUMINANT_D50, null, new Rgb.TransferParameters(1.0, 0.0, 1 / 16.0, 0.031248, 1.8), Named.PRO_PHOTO_RGB.ordinal() - ); - sNamedColorSpaces[Named.ACES.ordinal()] = new ColorSpace.Rgb( + )); + sNamedColorSpaceMap.put(Named.ACES.ordinal(), new ColorSpace.Rgb( "SMPTE ST 2065-1:2012 ACES", new float[] { 0.73470f, 0.26530f, 0.0f, 1.0f, 0.00010f, -0.0770f }, ILLUMINANT_D60, 1.0, -65504.0f, 65504.0f, Named.ACES.ordinal() - ); - sNamedColorSpaces[Named.ACESCG.ordinal()] = new ColorSpace.Rgb( + )); + sNamedColorSpaceMap.put(Named.ACESCG.ordinal(), new ColorSpace.Rgb( "Academy S-2014-004 ACEScg", new float[] { 0.713f, 0.293f, 0.165f, 0.830f, 0.128f, 0.044f }, ILLUMINANT_D60, 1.0, -65504.0f, 65504.0f, Named.ACESCG.ordinal() - ); - sNamedColorSpaces[Named.CIE_XYZ.ordinal()] = new Xyz( + )); + sNamedColorSpaceMap.put(Named.CIE_XYZ.ordinal(), new Xyz( "Generic XYZ", Named.CIE_XYZ.ordinal() - ); - sNamedColorSpaces[Named.CIE_LAB.ordinal()] = new ColorSpace.Lab( + )); + sNamedColorSpaceMap.put(Named.CIE_LAB.ordinal(), new ColorSpace.Lab( "Generic L*a*b*", Named.CIE_LAB.ordinal() - ); - sNamedColorSpaces[Named.BT2020_HLG.ordinal()] = new ColorSpace.Rgb( + )); + sNamedColorSpaceMap.put(Named.BT2020_HLG.ordinal(), new ColorSpace.Rgb( "Hybrid Log Gamma encoding", BT2020_PRIMARIES, ILLUMINANT_D65, @@ -1668,9 +1701,9 @@ public abstract class ColorSpace { 0.0f, 1.0f, BT2020_HLG_TRANSFER_PARAMETERS, Named.BT2020_HLG.ordinal() - ); + )); sDataToColorSpaces.put(DataSpace.DATASPACE_BT2020_HLG, Named.BT2020_HLG.ordinal()); - sNamedColorSpaces[Named.BT2020_PQ.ordinal()] = new ColorSpace.Rgb( + sNamedColorSpaceMap.put(Named.BT2020_PQ.ordinal(), new ColorSpace.Rgb( "Perceptual Quantizer encoding", BT2020_PRIMARIES, ILLUMINANT_D65, @@ -1680,8 +1713,14 @@ public abstract class ColorSpace { 0.0f, 1.0f, BT2020_PQ_TRANSFER_PARAMETERS, Named.BT2020_PQ.ordinal() - ); + )); sDataToColorSpaces.put(DataSpace.DATASPACE_BT2020_PQ, Named.BT2020_PQ.ordinal()); + if (Flags.okLabColorspace()) { + sNamedColorSpaceMap.put(Named.OK_LAB.ordinal(), new ColorSpace.OkLab( + "Oklab", + Named.OK_LAB.ordinal() + )); + } } private static double transferHLGOETF(Rgb.TransferParameters params, double x) { @@ -2142,10 +2181,103 @@ public abstract class ColorSpace { return v; } + } + + private static float clamp(float x, float min, float max) { + return x < min ? min : x > max ? max : x; + } - private static float clamp(float x, float min, float max) { - return x < min ? min : x > max ? max : x; + /** + * Implementation of the Oklab color space. Oklab uses a D65 white point. + */ + @AnyThread + private static final class OkLab extends ColorSpace { + + private OkLab(@NonNull String name, @IntRange(from = MIN_ID, to = MAX_ID) int id) { + super(name, Model.LAB, id); } + + @Override + public boolean isWideGamut() { + return true; + } + + @Override + public float getMinValue(@IntRange(from = 0, to = 3) int component) { + return component == 0 ? 0.0f : -0.5f; + } + + @Override + public float getMaxValue(@IntRange(from = 0, to = 3) int component) { + return component == 0 ? 1.0f : 0.5f; + } + + @Override + public float[] toXyz(@NonNull @Size(min = 3) float[] v) { + v[0] = clamp(v[0], 0.0f, 1.0f); + v[1] = clamp(v[1], -0.5f, 0.5f); + v[2] = clamp(v[2], -0.5f, 0.5f); + + mul3x3Float3(INVERSE_M2, v); + v[0] = v[0] * v[0] * v[0]; + v[1] = v[1] * v[1] * v[1]; + v[2] = v[2] * v[2] * v[2]; + + mul3x3Float3(INVERSE_M1, v); + + return v; + } + + @Override + public float[] fromXyz(@NonNull @Size(min = 3) float[] v) { + mul3x3Float3(M1, v); + + v[0] = (float) Math.cbrt(v[0]); + v[1] = (float) Math.cbrt(v[1]); + v[2] = (float) Math.cbrt(v[2]); + + mul3x3Float3(M2, v); + return v; + } + + /** + * Temp array used as input to compute M1 below + */ + private static final float[] M1TMP = { + 0.8189330101f, 0.0329845436f, 0.0482003018f, + 0.3618667424f, 0.9293118715f, 0.2643662691f, + -0.1288597137f, 0.0361456387f, 0.6338517070f + }; + + /** + * This is the matrix applied before the nonlinear transform for (D50) XYZ-to-Oklab. + * This combines the D50-to-D65 white point transform with the normal transform matrix + * because this is always done together in [fromXyz]. + */ + private static final float[] M1 = mul3x3( + M1TMP, + chromaticAdaptation(Adaptation.BRADFORD, ILLUMINANT_D50, ILLUMINANT_D65) + ); + + /** + * Matrix applied after the nonlinear transform. + */ + private static final float[] M2 = { + 0.2104542553f, 1.9779984951f, 0.0259040371f, + 0.7936177850f, -2.4285922050f, 0.7827717662f, + -0.0040720468f, 0.4505937099f, -0.8086757660f + }; + + /** + * The inverse of the [M1] matrix, transforming back to XYZ (D50) + */ + private static final float[] INVERSE_M1 = inverse3x3(M1); + + /** + * The inverse of the [M2] matrix, doing the first linear transform in the + * Oklab-to-XYZ before doing the nonlinear transform. + */ + private static final float[] INVERSE_M2 = inverse3x3(M2); } /** diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java index 16c77d0c3c81..ecf47209a802 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java @@ -24,6 +24,7 @@ import android.app.Application; import android.app.compat.CompatChanges; import android.content.Context; import android.hardware.devicestate.DeviceStateManager; +import android.os.SystemProperties; import android.util.Log; import androidx.annotation.NonNull; @@ -50,6 +51,11 @@ class WindowExtensionsImpl implements WindowExtensions { private static final String TAG = "WindowExtensionsImpl"; /** + * The value of the system property that indicates no override is set. + */ + private static final int NO_LEVEL_OVERRIDE = -1; + + /** * The min version of the WM Extensions that must be supported in the current platform version. */ @VisibleForTesting @@ -66,14 +72,30 @@ class WindowExtensionsImpl implements WindowExtensions { WindowExtensionsImpl() { mIsActivityEmbeddingEnabled = isActivityEmbeddingEnabled(); - Log.i(TAG, "Initializing Window Extensions, vendor API level=" + mVersion - + ", activity embedding enabled=" + mIsActivityEmbeddingEnabled); + + Log.i(TAG, generateLogMessage()); + } + + private String generateLogMessage() { + final StringBuilder logBuilder = new StringBuilder("Initializing Window Extensions, " + + "vendor API level=" + mVersion); + final int levelOverride = getLevelOverride(); + if (levelOverride != NO_LEVEL_OVERRIDE) { + logBuilder.append(", override to ").append(levelOverride); + } + logBuilder.append(", activity embedding enabled=").append(mIsActivityEmbeddingEnabled); + return logBuilder.toString(); } // TODO(b/241126279) Introduce constants to better version functionality @Override public int getVendorApiLevel() { - return mVersion; + final int levelOverride = getLevelOverride(); + return (levelOverride != NO_LEVEL_OVERRIDE) ? levelOverride : mVersion; + } + + private int getLevelOverride() { + return SystemProperties.getInt("persist.wm.debug.ext_version_override", NO_LEVEL_OVERRIDE); } @NonNull diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java index 94c281fa9fac..290fefa5abfa 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/DividerPresenter.java @@ -1394,10 +1394,16 @@ class DividerPresenter implements View.OnTouchListener { primaryBounds = mProperties.mIsReversedLayout ? boundsBottom : boundsTop; secondaryBounds = mProperties.mIsReversedLayout ? boundsTop : boundsBottom; } - t.setWindowCrop(mPrimaryVeil, primaryBounds.width(), primaryBounds.height()); - t.setWindowCrop(mSecondaryVeil, secondaryBounds.width(), secondaryBounds.height()); - t.setPosition(mPrimaryVeil, primaryBounds.left, primaryBounds.top); - t.setPosition(mSecondaryVeil, secondaryBounds.left, secondaryBounds.top); + if (mPrimaryVeil != null) { + t.setWindowCrop(mPrimaryVeil, primaryBounds.width(), primaryBounds.height()); + t.setPosition(mPrimaryVeil, primaryBounds.left, primaryBounds.top); + t.setVisibility(mPrimaryVeil, !primaryBounds.isEmpty()); + } + if (mSecondaryVeil != null) { + t.setWindowCrop(mSecondaryVeil, secondaryBounds.width(), secondaryBounds.height()); + t.setPosition(mSecondaryVeil, secondaryBounds.left, secondaryBounds.top); + t.setVisibility(mSecondaryVeil, !secondaryBounds.isEmpty()); + } } private static float[] colorToFloatArray(@NonNull Color color) { diff --git a/libs/WindowManager/Shell/res/values-cs/strings.xml b/libs/WindowManager/Shell/res/values-cs/strings.xml index aafb2e16b703..150a6e642529 100644 --- a/libs/WindowManager/Shell/res/values-cs/strings.xml +++ b/libs/WindowManager/Shell/res/values-cs/strings.xml @@ -84,7 +84,7 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"Bublina"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Spravovat"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bublina byla zavřena."</string> - <string name="restart_button_description" msgid="4564728020654658478">"Klepnutím tuto aplikaci kvůli lepšímu zobrazení restartujete"</string> + <string name="restart_button_description" msgid="4564728020654658478">"Klepnutím tuto aplikaci restartujete kvůli lepšímu zobrazení"</string> <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Změnit v Nastavení poměr stran této aplikace"</string> <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Změnit poměr stran"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problémy s fotoaparátem?\nKlepnutím vyřešíte"</string> diff --git a/libs/WindowManager/Shell/res/values-nb/strings.xml b/libs/WindowManager/Shell/res/values-nb/strings.xml index 9f03d8b5b178..6005be4fb5c9 100644 --- a/libs/WindowManager/Shell/res/values-nb/strings.xml +++ b/libs/WindowManager/Shell/res/values-nb/strings.xml @@ -85,7 +85,7 @@ <string name="manage_bubbles_text" msgid="7730624269650594419">"Administrer"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Boblen er avvist."</string> <string name="restart_button_description" msgid="4564728020654658478">"Trykk for å starte denne appen på nytt og få en bedre visning"</string> - <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Endre høyde/bredde-forholdet for denne appen i innstillingene"</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Endre høyde/bredde-forholdet for denne appen i Innstillinger"</string> <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Endre høyde/bredde-forholdet"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Har du kameraproblemer?\nTrykk for å tilpasse"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Ble ikke problemet løst?\nTrykk for å gå tilbake"</string> diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java index 32873d96e76c..81e7d1fc4d87 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java @@ -602,7 +602,7 @@ public class BubbleData { List<Bubble> removedBubbles = filterAllBubbles(bubble -> userId == bubble.getUser().getIdentifier()); for (Bubble b : removedBubbles) { - doRemove(b.getKey(), Bubbles.DISMISS_USER_REMOVED); + doRemove(b.getKey(), Bubbles.DISMISS_USER_ACCOUNT_REMOVED); } if (!removedBubbles.isEmpty()) { dispatchPendingChanges(); @@ -678,7 +678,7 @@ public class BubbleData { || reason == Bubbles.DISMISS_SHORTCUT_REMOVED || reason == Bubbles.DISMISS_PACKAGE_REMOVED || reason == Bubbles.DISMISS_USER_CHANGED - || reason == Bubbles.DISMISS_USER_REMOVED; + || reason == Bubbles.DISMISS_USER_ACCOUNT_REMOVED; int indexToRemove = indexForKey(key); if (indexToRemove == -1) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java index 1d053f9aab35..82af88d03b19 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubbles.java @@ -61,7 +61,7 @@ public interface Bubbles { DISMISS_NOTIF_CANCEL, DISMISS_ACCESSIBILITY_ACTION, DISMISS_NO_LONGER_BUBBLE, DISMISS_USER_CHANGED, DISMISS_GROUP_CANCELLED, DISMISS_INVALID_INTENT, DISMISS_OVERFLOW_MAX_REACHED, DISMISS_SHORTCUT_REMOVED, DISMISS_PACKAGE_REMOVED, - DISMISS_NO_BUBBLE_UP, DISMISS_RELOAD_FROM_DISK, DISMISS_USER_REMOVED, + DISMISS_NO_BUBBLE_UP, DISMISS_RELOAD_FROM_DISK, DISMISS_USER_ACCOUNT_REMOVED, DISMISS_SWITCH_TO_STACK}) @Target({FIELD, LOCAL_VARIABLE, PARAMETER}) @interface DismissReason { @@ -82,7 +82,7 @@ public interface Bubbles { int DISMISS_PACKAGE_REMOVED = 13; int DISMISS_NO_BUBBLE_UP = 14; int DISMISS_RELOAD_FROM_DISK = 15; - int DISMISS_USER_REMOVED = 16; + int DISMISS_USER_ACCOUNT_REMOVED = 16; int DISMISS_SWITCH_TO_STACK = 17; /** Returns a binder that can be passed to an external process to manipulate Bubbles. */ 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 a7da07d013c1..972dce51e02b 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 @@ -209,7 +209,7 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView @Override public void onDismissBubble(Bubble bubble) { - mManager.dismissBubble(bubble, Bubbles.DISMISS_USER_REMOVED); + mManager.dismissBubble(bubble, Bubbles.DISMISS_USER_GESTURE); } }); mHandleView.setOnClickListener(view -> { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java index 57e95d6fcfd6..f4ac5f260fcd 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java @@ -538,8 +538,10 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged @Override public void onAnimationStart(Animator animation) { + ValueAnimator valueAnimator = (ValueAnimator) animation; + float value = (float) valueAnimator.getAnimatedValue(); SurfaceControl.Transaction t = mTransactionPool.acquire(); - t.setPosition(mImeSourceControl.getLeash(), x, startY); + t.setPosition(mImeSourceControl.getLeash(), x, value); if (DEBUG) { Slog.d(TAG, "onAnimationStart d:" + mDisplayId + " top:" + imeTop(hiddenY) + "->" + imeTop(shownY) @@ -549,7 +551,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged imeTop(shownY), mAnimationDirection == DIRECTION_SHOW, isFloating, t); mAnimateAlpha = (flags & ImePositionProcessor.IME_ANIMATION_NO_ALPHA) == 0; final float alpha = (mAnimateAlpha || isFloating) - ? (startY - hiddenY) / (shownY - hiddenY) + ? (value - hiddenY) / (shownY - hiddenY) : 1.f; t.setAlpha(mImeSourceControl.getLeash(), alpha); if (mAnimationDirection == DIRECTION_SHOW) { @@ -560,7 +562,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged if (DEBUG_IME_VISIBILITY) { EventLog.writeEvent(IMF_IME_REMOTE_ANIM_START, mStatsToken != null ? mStatsToken.getTag() : ImeTracker.TOKEN_NONE, - mDisplayId, mAnimationDirection, alpha, startY , endY, + mDisplayId, mAnimationDirection, alpha, value, endY, Objects.toString(mImeSourceControl.getLeash()), Objects.toString(mImeSourceControl.getInsetsHint()), Objects.toString(mImeSourceControl.getSurfacePosition()), diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java index 2234041b8c9d..c2242a8b87fa 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/DividerView.java @@ -336,6 +336,11 @@ public class DividerView extends FrameLayout implements View.OnTouchListener { setTouching(); mStartPos = touchPos; mMoving = false; + // This triggers initialization of things like the resize veil in preparation for + // showing it when the user moves the divider past the slop, and has to be done + // before onStartDragging() which starts the jank interaction tracing + mSplitLayout.updateDividerBounds(mSplitLayout.getDividerPosition(), + false /* shouldUseParallaxEffect */); mSplitLayout.onStartDragging(); break; case MotionEvent.ACTION_MOVE: diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java index 991fbafed296..609e5af5c5b0 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java @@ -87,6 +87,7 @@ import com.android.wm.shell.performance.PerfHintController; import com.android.wm.shell.recents.RecentTasks; import com.android.wm.shell.recents.RecentTasksController; import com.android.wm.shell.recents.RecentsTransitionHandler; +import com.android.wm.shell.recents.TaskStackTransitionObserver; import com.android.wm.shell.shared.DesktopModeStatus; import com.android.wm.shell.shared.ShellTransitions; import com.android.wm.shell.shared.annotations.ShellAnimationThread; @@ -619,12 +620,13 @@ public abstract class WMShellBaseModule { TaskStackListenerImpl taskStackListener, ActivityTaskManager activityTaskManager, Optional<DesktopModeTaskRepository> desktopModeTaskRepository, + TaskStackTransitionObserver taskStackTransitionObserver, @ShellMainThread ShellExecutor mainExecutor ) { return Optional.ofNullable( RecentTasksController.create(context, shellInit, shellController, shellCommandHandler, taskStackListener, activityTaskManager, - desktopModeTaskRepository, mainExecutor)); + desktopModeTaskRepository, taskStackTransitionObserver, mainExecutor)); } @BindsOptionalOf @@ -924,6 +926,19 @@ public abstract class WMShellBaseModule { } // + // Task Stack + // + + @WMSingleton + @Provides + static TaskStackTransitionObserver provideTaskStackTransitionObserver( + Lazy<Transitions> transitions, + ShellInit shellInit + ) { + return new TaskStackTransitionObserver(transitions, shellInit); + } + + // // Misc // 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 12bbd51b968d..87bd84017dee 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 @@ -121,9 +121,9 @@ import java.util.Optional; */ @Module( includes = { - WMShellBaseModule.class, - PipModule.class, - ShellBackAnimationModule.class, + WMShellBaseModule.class, + PipModule.class, + ShellBackAnimationModule.class, }) public abstract class WMShellModule { @@ -664,7 +664,8 @@ public abstract class WMShellModule { @Provides static Object provideIndependentShellComponentsToCreate( DragAndDropController dragAndDropController, - Optional<DesktopTasksTransitionObserver> desktopTasksTransitionObserverOptional) { + Optional<DesktopTasksTransitionObserver> desktopTasksTransitionObserverOptional + ) { return new Object(); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasksListener.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasksListener.aidl index 62d195efb381..245829ecafb3 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasksListener.aidl +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasksListener.aidl @@ -42,4 +42,7 @@ oneway interface IRecentTasksListener { * Called when a running task changes. */ void onRunningTaskChanged(in RunningTaskInfo taskInfo); -} + + /** A task has moved to front. */ + oneway void onTaskMovedToFront(in RunningTaskInfo taskInfo); +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/OWNERS new file mode 100644 index 000000000000..452644b05a2a --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/OWNERS @@ -0,0 +1,6 @@ +# WM shell sub-module task stack owners +uysalorhan@google.com +samcackett@google.com +alexchau@google.com +silvajordan@google.com +uwaisashraf@google.com
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java index 9d162462830f..03c8cf8cc795 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java @@ -20,6 +20,7 @@ import static android.app.ActivityTaskManager.INVALID_TASK_ID; import static android.content.pm.PackageManager.FEATURE_PC; import static com.android.window.flags.Flags.enableDesktopWindowingTaskbarRunningApps; +import static com.android.window.flags.Flags.enableTaskStackObserverInShell; import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_RECENT_TASKS; import android.app.ActivityManager; @@ -57,6 +58,7 @@ import com.android.wm.shell.shared.annotations.ShellMainThread; import com.android.wm.shell.sysui.ShellCommandHandler; import com.android.wm.shell.sysui.ShellController; import com.android.wm.shell.sysui.ShellInit; +import com.android.wm.shell.transition.Transitions; import com.android.wm.shell.util.GroupedRecentTaskInfo; import com.android.wm.shell.util.SplitBounds; @@ -73,7 +75,8 @@ import java.util.function.Consumer; * Manages the recent task list from the system, caching it as necessary. */ public class RecentTasksController implements TaskStackListenerCallback, - RemoteCallable<RecentTasksController>, DesktopModeTaskRepository.ActiveTasksListener { + RemoteCallable<RecentTasksController>, DesktopModeTaskRepository.ActiveTasksListener, + TaskStackTransitionObserver.TaskStackTransitionObserverListener { private static final String TAG = RecentTasksController.class.getSimpleName(); private final Context mContext; @@ -84,6 +87,7 @@ public class RecentTasksController implements TaskStackListenerCallback, private final TaskStackListenerImpl mTaskStackListener; private final RecentTasksImpl mImpl = new RecentTasksImpl(); private final ActivityTaskManager mActivityTaskManager; + private final TaskStackTransitionObserver mTaskStackTransitionObserver; private RecentsTransitionHandler mTransitionHandler = null; private IRecentTasksListener mListener; private final boolean mPcFeatureEnabled; @@ -112,13 +116,15 @@ public class RecentTasksController implements TaskStackListenerCallback, TaskStackListenerImpl taskStackListener, ActivityTaskManager activityTaskManager, Optional<DesktopModeTaskRepository> desktopModeTaskRepository, + TaskStackTransitionObserver taskStackTransitionObserver, @ShellMainThread ShellExecutor mainExecutor ) { if (!context.getResources().getBoolean(com.android.internal.R.bool.config_hasRecents)) { return null; } return new RecentTasksController(context, shellInit, shellController, shellCommandHandler, - taskStackListener, activityTaskManager, desktopModeTaskRepository, mainExecutor); + taskStackListener, activityTaskManager, desktopModeTaskRepository, + taskStackTransitionObserver, mainExecutor); } RecentTasksController(Context context, @@ -128,6 +134,7 @@ public class RecentTasksController implements TaskStackListenerCallback, TaskStackListenerImpl taskStackListener, ActivityTaskManager activityTaskManager, Optional<DesktopModeTaskRepository> desktopModeTaskRepository, + TaskStackTransitionObserver taskStackTransitionObserver, ShellExecutor mainExecutor) { mContext = context; mShellController = shellController; @@ -136,6 +143,7 @@ public class RecentTasksController implements TaskStackListenerCallback, mPcFeatureEnabled = mContext.getPackageManager().hasSystemFeature(FEATURE_PC); mTaskStackListener = taskStackListener; mDesktopModeTaskRepository = desktopModeTaskRepository; + mTaskStackTransitionObserver = taskStackTransitionObserver; mMainExecutor = mainExecutor; shellInit.addInitCallback(this::onInit, this); } @@ -154,6 +162,10 @@ public class RecentTasksController implements TaskStackListenerCallback, mShellCommandHandler.addDumpCallback(this::dump, this); mTaskStackListener.addListener(this); mDesktopModeTaskRepository.ifPresent(it -> it.addActiveTaskListener(this)); + if (Transitions.ENABLE_SHELL_TRANSITIONS) { + mTaskStackTransitionObserver.addTaskStackTransitionObserverListener(this, + mMainExecutor); + } } void setTransitionHandler(RecentsTransitionHandler handler) { @@ -267,6 +279,12 @@ public class RecentTasksController implements TaskStackListenerCallback, notifyRecentTasksChanged(); } + @Override + public void onTaskMovedToFrontThroughTransition( + ActivityManager.RunningTaskInfo runningTaskInfo) { + notifyTaskMovedToFront(runningTaskInfo); + } + @VisibleForTesting void notifyRecentTasksChanged() { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENT_TASKS, "Notify recent tasks changed"); @@ -328,6 +346,19 @@ public class RecentTasksController implements TaskStackListenerCallback, } } + private void notifyTaskMovedToFront(ActivityManager.RunningTaskInfo taskInfo) { + if (mListener == null + || !enableTaskStackObserverInShell() + || taskInfo.realActivity == null) { + return; + } + try { + mListener.onTaskMovedToFront(taskInfo); + } catch (RemoteException e) { + Slog.w(TAG, "Failed call onTaskMovedToFront", e); + } + } + private boolean shouldEnableRunningTasksForDesktopMode() { return mPcFeatureEnabled || (DesktopModeStatus.canEnterDesktopMode(mContext) @@ -464,6 +495,7 @@ public class RecentTasksController implements TaskStackListenerCallback, } return null; } + public void dump(@NonNull PrintWriter pw, String prefix) { final String innerPrefix = prefix + " "; pw.println(prefix + TAG); @@ -547,6 +579,11 @@ public class RecentTasksController implements TaskStackListenerCallback, public void onRunningTaskChanged(ActivityManager.RunningTaskInfo taskInfo) { mListener.call(l -> l.onRunningTaskChanged(taskInfo)); } + + @Override + public void onTaskMovedToFront(ActivityManager.RunningTaskInfo taskInfo) { + mListener.call(l -> l.onTaskMovedToFront(taskInfo)); + } }; public IRecentTasksImpl(RecentTasksController controller) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/TaskStackTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/TaskStackTransitionObserver.kt new file mode 100644 index 000000000000..7c5f10a5bcca --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/TaskStackTransitionObserver.kt @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.recents + +import android.app.ActivityManager.RunningTaskInfo +import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM +import android.os.IBinder +import android.util.ArrayMap +import android.view.SurfaceControl +import android.view.WindowManager +import android.window.TransitionInfo +import com.android.window.flags.Flags.enableTaskStackObserverInShell +import com.android.wm.shell.shared.TransitionUtil +import com.android.wm.shell.sysui.ShellInit +import com.android.wm.shell.transition.Transitions +import dagger.Lazy +import java.util.concurrent.Executor + +/** + * A [Transitions.TransitionObserver] that observes shell transitions and sends updates to listeners + * about task stack changes. + * + * TODO(346588978) Move split/pip signals here as well so that launcher don't need to handle it + */ +class TaskStackTransitionObserver( + private val transitions: Lazy<Transitions>, + shellInit: ShellInit +) : Transitions.TransitionObserver { + + private val transitionToTransitionChanges: MutableMap<IBinder, TransitionChanges> = + mutableMapOf() + private val taskStackTransitionObserverListeners = + ArrayMap<TaskStackTransitionObserverListener, Executor>() + + init { + if (Transitions.ENABLE_SHELL_TRANSITIONS) { + shellInit.addInitCallback(::onInit, this) + } + } + + fun onInit() { + transitions.get().registerObserver(this) + } + + override fun onTransitionReady( + transition: IBinder, + info: TransitionInfo, + startTransaction: SurfaceControl.Transaction, + finishTransaction: SurfaceControl.Transaction + ) { + if (enableTaskStackObserverInShell()) { + val taskInfoList = mutableListOf<RunningTaskInfo>() + val transitionTypeList = mutableListOf<Int>() + + for (change in info.changes) { + if (change.flags and TransitionInfo.FLAG_IS_WALLPAPER != 0) { + continue + } + + val taskInfo = change.taskInfo + if (taskInfo == null || taskInfo.taskId == -1) { + continue + } + + if (change.mode == WindowManager.TRANSIT_OPEN) { + change.taskInfo?.let { taskInfoList.add(it) } + transitionTypeList.add(change.mode) + } + } + transitionToTransitionChanges.put( + transition, + TransitionChanges(taskInfoList, transitionTypeList) + ) + } + } + + override fun onTransitionStarting(transition: IBinder) {} + + override fun onTransitionMerged(merged: IBinder, playing: IBinder) {} + + override fun onTransitionFinished(transition: IBinder, aborted: Boolean) { + val taskInfoList = + transitionToTransitionChanges.getOrDefault(transition, TransitionChanges()).taskInfoList + val typeList = + transitionToTransitionChanges + .getOrDefault(transition, TransitionChanges()) + .transitionTypeList + transitionToTransitionChanges.remove(transition) + + for ((index, taskInfo) in taskInfoList.withIndex()) { + if ( + TransitionUtil.isOpeningType(typeList[index]) && + taskInfo.windowingMode == WINDOWING_MODE_FREEFORM + ) { + notifyTaskStackTransitionObserverListeners(taskInfo) + } + } + } + + fun addTaskStackTransitionObserverListener( + taskStackTransitionObserverListener: TaskStackTransitionObserverListener, + executor: Executor + ) { + taskStackTransitionObserverListeners[taskStackTransitionObserverListener] = executor + } + + fun removeTaskStackTransitionObserverListener( + taskStackTransitionObserverListener: TaskStackTransitionObserverListener + ) { + taskStackTransitionObserverListeners.remove(taskStackTransitionObserverListener) + } + + private fun notifyTaskStackTransitionObserverListeners(taskInfo: RunningTaskInfo) { + taskStackTransitionObserverListeners.forEach { (listener, executor) -> + executor.execute { listener.onTaskMovedToFrontThroughTransition(taskInfo) } + } + } + + /** Listener to use to get updates regarding task stack from this observer */ + interface TaskStackTransitionObserverListener { + /** Called when a task is moved to front. */ + fun onTaskMovedToFrontThroughTransition(taskInfo: RunningTaskInfo) {} + } + + private data class TransitionChanges( + val taskInfoList: MutableList<RunningTaskInfo> = ArrayList(), + val transitionTypeList: MutableList<Int> = ArrayList() + ) +} 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 3dcdc0be9e6c..4d597cac889e 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 @@ -99,10 +99,12 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin private DragPositioningCallback mDragPositioningCallback; private DragResizeInputListener mDragResizeListener; private DragDetector mDragDetector; - + private Runnable mCurrentViewHostRunnable = null; private RelayoutParams mRelayoutParams = new RelayoutParams(); private final WindowDecoration.RelayoutResult<WindowDecorLinearLayout> mResult = new WindowDecoration.RelayoutResult<>(); + private final Runnable mViewHostRunnable = + () -> updateViewHost(mRelayoutParams, null /* onDrawTransaction */, mResult); private final Point mPositionInParent = new Point(); private HandleMenu mHandleMenu; @@ -194,17 +196,88 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin // position and crop are set. final boolean shouldSetTaskPositionAndCrop = !DesktopModeStatus.isVeiledResizeEnabled() && mTaskDragResizer.isResizingOrAnimating(); - // Use |applyStartTransactionOnDraw| so that the transaction (that applies task crop) is - // 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 */, - shouldSetTaskPositionAndCrop); + // For headers only (i.e. in freeform): use |applyStartTransactionOnDraw| so that the + // transaction (that applies task crop) is 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. + final boolean applyTransactionOnDraw = + taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM; + relayout(taskInfo, t, t, applyTransactionOnDraw, shouldSetTaskPositionAndCrop); + if (!applyTransactionOnDraw) { + t.apply(); + } } void relayout(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT, boolean applyStartTransactionOnDraw, boolean shouldSetTaskPositionAndCrop) { Trace.beginSection("DesktopModeWindowDecoration#relayout"); + if (taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) { + // 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, + shouldSetTaskPositionAndCrop); + } 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, + shouldSetTaskPositionAndCrop); + } + Trace.endSection(); + } + + /** Run the whole relayout phase immediately without delay. */ + private void relayoutInSync(ActivityManager.RunningTaskInfo taskInfo, + SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT, + boolean applyStartTransactionOnDraw, boolean shouldSetTaskPositionAndCrop) { + // Clear the current ViewHost runnable as we will update the ViewHost here + clearCurrentViewHostRunnable(); + updateRelayoutParamsAndSurfaces(taskInfo, startT, finishT, applyStartTransactionOnDraw, + shouldSetTaskPositionAndCrop); + if (mResult.mRootView != null) { + updateViewHost(mRelayoutParams, startT, mResult); + } + } + + /** + * Clear the current ViewHost runnable - to ensure it doesn't run once relayout params have been + * updated. + */ + private void clearCurrentViewHostRunnable() { + if (mCurrentViewHostRunnable != null) { + mHandler.removeCallbacks(mCurrentViewHostRunnable); + mCurrentViewHostRunnable = null; + } + } + + /** + * Relayout the window decoration but repost some of the work, to unblock the current callstack. + */ + private void relayoutWithDelayedViewHost(ActivityManager.RunningTaskInfo taskInfo, + SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT, + boolean applyStartTransactionOnDraw, boolean shouldSetTaskPositionAndCrop) { + if (applyStartTransactionOnDraw) { + throw new IllegalArgumentException( + "We cannot both sync viewhost ondraw and delay viewhost creation."); + } + // Clear the current ViewHost runnable as we will update the ViewHost here + clearCurrentViewHostRunnable(); + updateRelayoutParamsAndSurfaces(taskInfo, startT, finishT, + false /* applyStartTransactionOnDraw */, shouldSetTaskPositionAndCrop); + if (mResult.mRootView == null) { + // This means something blocks the window decor from showing, e.g. the task is hidden. + // Nothing is set up in this case including the decoration surface. + return; + } + // Store the current runnable so it can be removed if we start a new relayout. + mCurrentViewHostRunnable = mViewHostRunnable; + mHandler.post(mCurrentViewHostRunnable); + } + + private void updateRelayoutParamsAndSurfaces(ActivityManager.RunningTaskInfo taskInfo, + SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT, + boolean applyStartTransactionOnDraw, boolean shouldSetTaskPositionAndCrop) { + Trace.beginSection("DesktopModeWindowDecoration#updateRelayoutParamsAndSurfaces"); if (isHandleMenuActive()) { mHandleMenu.relayout(startT); } @@ -216,8 +289,8 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin final SurfaceControl oldDecorationSurface = mDecorationContainerSurface; final WindowContainerTransaction wct = new WindowContainerTransaction(); - Trace.beginSection("DesktopModeWindowDecoration#relayout-inner"); - relayout(mRelayoutParams, startT, finishT, wct, oldRootView, mResult); + Trace.beginSection("DesktopModeWindowDecoration#relayout-updateViewsAndSurfaces"); + updateViewsAndSurfaces(mRelayoutParams, startT, finishT, wct, oldRootView, mResult); Trace.endSection(); // After this line, mTaskInfo is up-to-date and should be used instead of taskInfo @@ -228,7 +301,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin 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. - Trace.endSection(); // DesktopModeWindowDecoration#relayout + Trace.endSection(); // DesktopModeWindowDecoration#updateRelayoutParamsAndSurfaces return; } @@ -246,7 +319,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin updateDragResizeListener(oldDecorationSurface); updateMaximizeMenu(startT); - Trace.endSection(); // DesktopModeWindowDecoration#relayout + Trace.endSection(); // DesktopModeWindowDecoration#updateRelayoutParamsAndSurfaces } private void updateDragResizeListener(SurfaceControl oldDecorationSurface) { @@ -851,6 +924,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin closeHandleMenu(); mExclusionRegionListener.onExclusionRegionDismissed(mTaskInfo.taskId); disposeResizeVeil(); + clearCurrentViewHostRunnable(); super.close(); } 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 b9532dd9e681..216990c35247 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 @@ -199,8 +199,16 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> void relayout(RelayoutParams params, SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT, WindowContainerTransaction wct, T rootView, RelayoutResult<T> outResult) { - outResult.reset(); + updateViewsAndSurfaces(params, startT, finishT, wct, rootView, outResult); + if (outResult.mRootView != null) { + updateViewHost(params, startT, outResult); + } + } + protected void updateViewsAndSurfaces(RelayoutParams params, + SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT, + WindowContainerTransaction wct, T rootView, RelayoutResult<T> outResult) { + outResult.reset(); if (params.mRunningTaskInfo != null) { mTaskInfo = params.mRunningTaskInfo; } @@ -236,7 +244,6 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> updateCaptionContainerSurface(startT, outResult); updateCaptionInsets(params, wct, outResult, taskBounds); updateTaskSurface(params, startT, finishT, outResult); - updateViewHost(params, startT, outResult); } private void inflateIfNeeded(RelayoutParams params, WindowContainerTransaction wct, @@ -410,8 +417,17 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> } } - private void updateViewHost(RelayoutParams params, SurfaceControl.Transaction onDrawTransaction, - RelayoutResult<T> outResult) { + /** + * Updates a {@link SurfaceControlViewHost} to connect the window decoration surfaces with our + * View hierarchy. + * + * @param params parameters to use from the last relayout + * @param onDrawTransaction a transaction to apply in sync with #onDraw + * @param outResult results to use from the last relayout + * + */ + protected void updateViewHost(RelayoutParams params, + SurfaceControl.Transaction onDrawTransaction, RelayoutResult<T> outResult) { Trace.beginSection("CaptionViewHostLayout"); if (mCaptionWindowManager == null) { // Put caption under a container surface because ViewRootImpl sets the destination frame @@ -433,6 +449,9 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> mViewHost = mSurfaceControlViewHostFactory.create(mDecorWindowContext, mDisplay, mCaptionWindowManager); if (params.mApplyStartTransactionOnDraw) { + if (onDrawTransaction == null) { + throw new IllegalArgumentException("Trying to sync a null Transaction"); + } mViewHost.getRootSurfaceControl().applyTransactionOnDraw(onDrawTransaction); } mViewHost.setView(outResult.mRootView, lp); @@ -440,6 +459,9 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> } else { Trace.beginSection("CaptionViewHostLayout-relayout"); if (params.mApplyStartTransactionOnDraw) { + if (onDrawTransaction == null) { + throw new IllegalArgumentException("Trying to sync a null Transaction"); + } mViewHost.getRootSurfaceControl().applyTransactionOnDraw(onDrawTransaction); } mViewHost.relayout(lp); diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/Android.bp b/libs/WindowManager/Shell/tests/flicker/splitscreen/Android.bp index f813b0d3b0b7..0fe7a16be851 100644 --- a/libs/WindowManager/Shell/tests/flicker/splitscreen/Android.bp +++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/Android.bp @@ -65,9 +65,11 @@ android_test { android_test { name: "WMShellFlickerTestsSplitScreenGroup2", + defaults: ["WMShellFlickerTestsDefault"], manifest: "AndroidManifest.xml", package_name: "com.android.wm.shell.flicker.splitscreen", instrumentation_target_package: "com.android.wm.shell.flicker.splitscreen", + test_config_template: "AndroidTestTemplate.xml", srcs: [ ":WMShellFlickerTestsSplitScreenBase-src", ":WMShellFlickerTestsSplitScreenGroup2-src", diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/MultipleShowImeRequestsInSplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/MultipleShowImeRequestsInSplitScreen.kt new file mode 100644 index 000000000000..dad5db94d062 --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/MultipleShowImeRequestsInSplitScreen.kt @@ -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.wm.shell.flicker.splitscreen + +import android.platform.test.annotations.Presubmit +import android.tools.Rotation +import android.tools.flicker.junit.FlickerParametersRunnerFactory +import android.tools.flicker.legacy.FlickerBuilder +import android.tools.flicker.legacy.LegacyFlickerTest +import android.tools.flicker.legacy.LegacyFlickerTestFactory +import android.tools.traces.component.ComponentNameMatcher +import androidx.test.filters.RequiresDevice +import com.android.wm.shell.flicker.splitscreen.benchmark.MultipleShowImeRequestsInSplitScreenBenchmark +import com.android.wm.shell.flicker.utils.ICommonAssertions +import org.junit.FixMethodOrder +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +/** + * Test quick switch between two split pairs. + * + * To run this test: `atest WMShellFlickerTestsSplitScreenGroup2:MultipleShowImeRequestsInSplitScreen` + */ +@RequiresDevice +@RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +class MultipleShowImeRequestsInSplitScreen(override val flicker: LegacyFlickerTest) : + MultipleShowImeRequestsInSplitScreenBenchmark(flicker), ICommonAssertions { + override val transition: FlickerBuilder.() -> Unit + get() = { + defaultSetup(this) + defaultTeardown(this) + thisTransition(this) + } + + @Presubmit + @Test + fun imeLayerAlwaysVisible() = + flicker.assertLayers { + this.isVisible(ComponentNameMatcher.IME) + } + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams() = LegacyFlickerTestFactory.nonRotationTests( + supportedRotations = listOf(Rotation.ROTATION_0) + ) + } +} diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt index 90453640c91a..d34998815fca 100644 --- a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt +++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt @@ -47,7 +47,7 @@ import org.junit.runners.Parameterized * To run this test: `atest WMShellFlickerTestsSplitScreen:UnlockKeyguardToSplitScreen` */ @RequiresDevice -@Postsubmit +@FlakyTest(bugId = 293578017) @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) @@ -61,7 +61,6 @@ class UnlockKeyguardToSplitScreen(override val flicker: LegacyFlickerTest) : } @Test - @FlakyTest(bugId = 293578017) override fun visibleLayersShownMoreThanOneConsecutiveEntry() = super.visibleLayersShownMoreThanOneConsecutiveEntry() diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/MultipleShowImeRequestsInSplitScreenBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/MultipleShowImeRequestsInSplitScreenBenchmark.kt new file mode 100644 index 000000000000..249253185607 --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/MultipleShowImeRequestsInSplitScreenBenchmark.kt @@ -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 com.android.wm.shell.flicker.splitscreen.benchmark + +import android.tools.flicker.junit.FlickerParametersRunnerFactory +import android.tools.flicker.legacy.FlickerBuilder +import android.tools.flicker.legacy.LegacyFlickerTest +import android.tools.flicker.legacy.LegacyFlickerTestFactory +import androidx.test.filters.RequiresDevice +import com.android.server.wm.flicker.helpers.ImeAppHelper +import com.android.wm.shell.flicker.utils.SplitScreenUtils +import org.junit.FixMethodOrder +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +@RequiresDevice +@RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +abstract class MultipleShowImeRequestsInSplitScreenBenchmark( + override val flicker: LegacyFlickerTest +) : SplitScreenBase(flicker) { + override val primaryApp = ImeAppHelper(instrumentation) + override val defaultTeardown: FlickerBuilder.() -> Unit + get() = { + teardown { + primaryApp.closeIME(wmHelper) + super.defaultTeardown + } + } + + protected val thisTransition: FlickerBuilder.() -> Unit + get() = { + setup { + SplitScreenUtils.enterSplit( + wmHelper, + tapl, + device, + primaryApp, + secondaryApp, + flicker.scenario.startRotation + ) + // initially open the IME + primaryApp.openIME(wmHelper) + } + transitions { + for (i in 1..OPEN_IME_COUNT) { + primaryApp.openIME(wmHelper) + } + } + } + + companion object { + const val OPEN_IME_COUNT = 30 + + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams() = LegacyFlickerTestFactory.nonRotationTests() + } +} diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SplitScreenBase.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SplitScreenBase.kt index 4b106034b2b5..51074f634e30 100644 --- a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SplitScreenBase.kt +++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SplitScreenBase.kt @@ -25,7 +25,7 @@ import com.android.wm.shell.flicker.utils.SplitScreenUtils abstract class SplitScreenBase(flicker: LegacyFlickerTest) : BaseBenchmarkTest(flicker) { protected val context: Context = instrumentation.context - protected val primaryApp = SplitScreenUtils.getPrimary(instrumentation) + protected open val primaryApp = SplitScreenUtils.getPrimary(instrumentation) protected val secondaryApp = SplitScreenUtils.getSecondary(instrumentation) protected open val defaultSetup: FlickerBuilder.() -> Unit = { diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java index f55c96cb1769..93e405131a58 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleDataTest.java @@ -1170,9 +1170,9 @@ public class BubbleDataTest extends ShellTestCase { // Verify the update has the removals. BubbleData.Update update = mUpdateCaptor.getValue(); assertThat(update.removedBubbles.get(0)).isEqualTo( - Pair.create(mBubbleA2, Bubbles.DISMISS_USER_REMOVED)); + Pair.create(mBubbleA2, Bubbles.DISMISS_USER_ACCOUNT_REMOVED)); assertThat(update.removedBubbles.get(1)).isEqualTo( - Pair.create(mBubbleA1, Bubbles.DISMISS_USER_REMOVED)); + Pair.create(mBubbleA1, Bubbles.DISMISS_USER_ACCOUNT_REMOVED)); // Verify no A bubbles in active or overflow. assertBubbleListContains(mBubbleC1, mBubbleB3); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt index 5f6132ab9e58..0d3cd10e90df 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt @@ -40,14 +40,14 @@ import com.android.modules.utils.testing.ExtendedMockitoRule import com.android.wm.shell.common.ShellExecutor import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.EnterReason import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ExitReason -import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_HANDLE_MENU_BUTTON -import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_KEYBOARD_SHORTCUT -import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_TASK_DRAG -import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_UNKNOWN import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_ENTER_DESKTOP_FROM_APP_FROM_OVERVIEW import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_ENTER_DESKTOP_FROM_APP_HANDLE_MENU_BUTTON import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_ENTER_DESKTOP_FROM_KEYBOARD_SHORTCUT import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_ENTER_DESKTOP_FROM_UNKNOWN +import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_HANDLE_MENU_BUTTON +import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_KEYBOARD_SHORTCUT +import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_TASK_DRAG +import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_UNKNOWN import com.android.wm.shell.shared.DesktopModeStatus import com.android.wm.shell.sysui.ShellInit import com.android.wm.shell.transition.TransitionInfoBuilder @@ -78,409 +78,397 @@ import org.mockito.kotlin.verifyZeroInteractions @RunWith(AndroidTestingRunner::class) class DesktopModeLoggerTransitionObserverTest { - @JvmField - @Rule - val extendedMockitoRule = - ExtendedMockitoRule.Builder(this) - .mockStatic(DesktopModeEventLogger::class.java) - .mockStatic(DesktopModeStatus::class.java) - .build()!! - - @Mock lateinit var testExecutor: ShellExecutor - @Mock private lateinit var mockShellInit: ShellInit - @Mock private lateinit var transitions: Transitions - @Mock private lateinit var context: Context - - private lateinit var transitionObserver: DesktopModeLoggerTransitionObserver - private lateinit var shellInit: ShellInit - private lateinit var desktopModeEventLogger: DesktopModeEventLogger - - @Before - fun setup() { - doReturn(true).`when` { DesktopModeStatus.canEnterDesktopMode(any()) } - shellInit = Mockito.spy(ShellInit(testExecutor)) - desktopModeEventLogger = mock(DesktopModeEventLogger::class.java) - - transitionObserver = - DesktopModeLoggerTransitionObserver( - context, - mockShellInit, - transitions, - desktopModeEventLogger - ) - if (Transitions.ENABLE_SHELL_TRANSITIONS) { - val initRunnableCaptor = ArgumentCaptor.forClass(Runnable::class.java) - verify(mockShellInit) - .addInitCallback(initRunnableCaptor.capture(), same(transitionObserver)) - initRunnableCaptor.value.run() - } else { - transitionObserver.onInit() - } - } - - @Test - fun testRegistersObserverAtInit() { - verify(transitions).registerObserver(same(transitionObserver)) - } - - @Test - fun transitOpen_notFreeformWindow_doesNotLogTaskAddedOrSessionEnter() { - val change = createChange(TRANSIT_OPEN, createTaskInfo(1, WINDOWING_MODE_FULLSCREEN)) - val transitionInfo = TransitionInfoBuilder(TRANSIT_OPEN, 0).addChange(change).build() - - callOnTransitionReady(transitionInfo) - - verify(desktopModeEventLogger, never()).logSessionEnter(any(), any()) - verify(desktopModeEventLogger, never()).logTaskAdded(any(), any()) - } - - @Test - fun transitOpen_logTaskAddedAndEnterReasonAppFreeformIntent() { - val change = createChange(TRANSIT_OPEN, createTaskInfo(1, WINDOWING_MODE_FREEFORM)) - val transitionInfo = TransitionInfoBuilder(TRANSIT_OPEN, 0).addChange(change).build() - - callOnTransitionReady(transitionInfo) - val sessionId = transitionObserver.getLoggerSessionId() - - assertThat(sessionId).isNotNull() - verify(desktopModeEventLogger, times(1)) - .logSessionEnter(eq(sessionId!!), eq(EnterReason.APP_FREEFORM_INTENT)) - verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any()) - verifyZeroInteractions(desktopModeEventLogger) - } - - @Test - fun transitEndDragToDesktop_logTaskAddedAndEnterReasonAppHandleDrag() { - val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM)) - // task change is finalised when drag ends - val transitionInfo = - TransitionInfoBuilder(Transitions.TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP, 0) - .addChange(change) - .build() - - callOnTransitionReady(transitionInfo) - val sessionId = transitionObserver.getLoggerSessionId() - - assertThat(sessionId).isNotNull() - verify(desktopModeEventLogger, times(1)) - .logSessionEnter(eq(sessionId!!), eq(EnterReason.APP_HANDLE_DRAG)) - verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any()) - verifyZeroInteractions(desktopModeEventLogger) - } - - @Test - fun transitEnterDesktopByButtonTap_logTaskAddedAndEnterReasonButtonTap() { - val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM)) - val transitionInfo = - TransitionInfoBuilder(TRANSIT_ENTER_DESKTOP_FROM_APP_HANDLE_MENU_BUTTON, 0) - .addChange(change) - .build() - - callOnTransitionReady(transitionInfo) - val sessionId = transitionObserver.getLoggerSessionId() - - assertThat(sessionId).isNotNull() - verify(desktopModeEventLogger, times(1)) - .logSessionEnter(eq(sessionId!!), eq(EnterReason.APP_HANDLE_MENU_BUTTON)) - verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any()) - verifyZeroInteractions(desktopModeEventLogger) - } - - @Test - fun transitEnterDesktopFromAppFromOverview_logTaskAddedAndEnterReasonUnknown() { - val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM)) - val transitionInfo = - TransitionInfoBuilder(TRANSIT_ENTER_DESKTOP_FROM_APP_FROM_OVERVIEW, 0) - .addChange(change) - .build() - - callOnTransitionReady(transitionInfo) - val sessionId = transitionObserver.getLoggerSessionId() - - assertThat(sessionId).isNotNull() - verify(desktopModeEventLogger, times(1)) - .logSessionEnter(eq(sessionId!!), eq(EnterReason.APP_FROM_OVERVIEW)) - verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any()) - verifyZeroInteractions(desktopModeEventLogger) - } - - @Test - fun transitEnterDesktopFromKeyboardShortcut_logTaskAddedAndEnterReasonKeyboardShortcut() { - val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM)) - val transitionInfo = - TransitionInfoBuilder(TRANSIT_ENTER_DESKTOP_FROM_KEYBOARD_SHORTCUT, 0) - .addChange(change) - .build() - - callOnTransitionReady(transitionInfo) - val sessionId = transitionObserver.getLoggerSessionId() - - assertThat(sessionId).isNotNull() - verify(desktopModeEventLogger, times(1)) - .logSessionEnter(eq(sessionId!!), eq(EnterReason.KEYBOARD_SHORTCUT_ENTER)) - verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any()) - verifyZeroInteractions(desktopModeEventLogger) - } - - @Test - fun transitEnterDesktopFromUnknown_logTaskAddedAndEnterReasonUnknown() { - val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM)) - val transitionInfo = - TransitionInfoBuilder(TRANSIT_ENTER_DESKTOP_FROM_UNKNOWN, 0).addChange(change).build() - - callOnTransitionReady(transitionInfo) - val sessionId = transitionObserver.getLoggerSessionId() - - assertThat(sessionId).isNotNull() - verify(desktopModeEventLogger, times(1)) - .logSessionEnter(eq(sessionId!!), eq(EnterReason.UNKNOWN_ENTER)) - verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any()) - verifyZeroInteractions(desktopModeEventLogger) - } - - @Test - fun transitWake_logTaskAddedAndEnterReasonScreenOn() { - val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM)) - val transitionInfo = TransitionInfoBuilder(TRANSIT_WAKE, 0).addChange(change).build() - - callOnTransitionReady(transitionInfo) - val sessionId = transitionObserver.getLoggerSessionId() - - assertThat(sessionId).isNotNull() - verify(desktopModeEventLogger, times(1)) - .logSessionEnter(eq(sessionId!!), eq(EnterReason.SCREEN_ON)) - verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any()) - verifyZeroInteractions(desktopModeEventLogger) - } - - @Test - fun transitSleep_logTaskAddedAndExitReasonScreenOff_sessionIdNull() { - val sessionId = 1 - // add a freeform task - transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM)) - transitionObserver.setLoggerSessionId(sessionId) - - val transitionInfo = TransitionInfoBuilder(TRANSIT_SLEEP).build() - callOnTransitionReady(transitionInfo) - - verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any()) - verify(desktopModeEventLogger, times(1)) - .logSessionExit(eq(sessionId), eq(ExitReason.SCREEN_OFF)) - verifyZeroInteractions(desktopModeEventLogger) - assertThat(transitionObserver.getLoggerSessionId()).isNull() - } - - @Test - fun transitExitDesktopTaskDrag_logTaskRemovedAndExitReasonDragToExit_sessionIdNull() { - val sessionId = 1 - // add a freeform task - transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM)) - transitionObserver.setLoggerSessionId(sessionId) - - // window mode changing from FREEFORM to FULLSCREEN - val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FULLSCREEN)) - val transitionInfo = - TransitionInfoBuilder(TRANSIT_EXIT_DESKTOP_MODE_TASK_DRAG).addChange(change).build() - callOnTransitionReady(transitionInfo) - - verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any()) - verify(desktopModeEventLogger, times(1)) - .logSessionExit(eq(sessionId), eq(ExitReason.DRAG_TO_EXIT)) - verifyZeroInteractions(desktopModeEventLogger) - assertThat(transitionObserver.getLoggerSessionId()).isNull() - } - - @Test - fun transitExitDesktopAppHandleButton_logTaskRemovedAndExitReasonButton_sessionIdNull() { - val sessionId = 1 - // add a freeform task - transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM)) - transitionObserver.setLoggerSessionId(sessionId) - - // window mode changing from FREEFORM to FULLSCREEN - val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FULLSCREEN)) - val transitionInfo = - TransitionInfoBuilder(TRANSIT_EXIT_DESKTOP_MODE_HANDLE_MENU_BUTTON) - .addChange(change) - .build() - callOnTransitionReady(transitionInfo) - - verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any()) - verify(desktopModeEventLogger, times(1)) - .logSessionExit(eq(sessionId), eq(ExitReason.APP_HANDLE_MENU_BUTTON_EXIT)) - verifyZeroInteractions(desktopModeEventLogger) - assertThat(transitionObserver.getLoggerSessionId()).isNull() - } - - @Test - fun transitExitDesktopUsingKeyboard_logTaskRemovedAndExitReasonKeyboard_sessionIdNull() { - val sessionId = 1 - // add a freeform task - transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM)) - transitionObserver.setLoggerSessionId(sessionId) - - // window mode changing from FREEFORM to FULLSCREEN - val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FULLSCREEN)) - val transitionInfo = - TransitionInfoBuilder(TRANSIT_EXIT_DESKTOP_MODE_KEYBOARD_SHORTCUT) - .addChange(change) - .build() - callOnTransitionReady(transitionInfo) - - verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any()) - verify(desktopModeEventLogger, times(1)) - .logSessionExit(eq(sessionId), eq(ExitReason.KEYBOARD_SHORTCUT_EXIT)) - verifyZeroInteractions(desktopModeEventLogger) - assertThat(transitionObserver.getLoggerSessionId()).isNull() - } - - @Test - fun transitExitDesktopUnknown_logTaskRemovedAndExitReasonUnknown_sessionIdNull() { - val sessionId = 1 - // add a freeform task - transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM)) - transitionObserver.setLoggerSessionId(sessionId) - - // window mode changing from FREEFORM to FULLSCREEN - val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FULLSCREEN)) - val transitionInfo = - TransitionInfoBuilder(TRANSIT_EXIT_DESKTOP_MODE_UNKNOWN).addChange(change).build() - callOnTransitionReady(transitionInfo) - - verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any()) - verify(desktopModeEventLogger, times(1)) - .logSessionExit(eq(sessionId), eq(ExitReason.UNKNOWN_EXIT)) - verifyZeroInteractions(desktopModeEventLogger) - assertThat(transitionObserver.getLoggerSessionId()).isNull() + @JvmField + @Rule + val extendedMockitoRule = + ExtendedMockitoRule.Builder(this) + .mockStatic(DesktopModeEventLogger::class.java) + .mockStatic(DesktopModeStatus::class.java) + .build()!! + + @Mock lateinit var testExecutor: ShellExecutor + @Mock private lateinit var mockShellInit: ShellInit + @Mock private lateinit var transitions: Transitions + @Mock private lateinit var context: Context + + private lateinit var transitionObserver: DesktopModeLoggerTransitionObserver + private lateinit var shellInit: ShellInit + private lateinit var desktopModeEventLogger: DesktopModeEventLogger + + @Before + fun setup() { + doReturn(true).`when` { DesktopModeStatus.canEnterDesktopMode(any()) } + shellInit = Mockito.spy(ShellInit(testExecutor)) + desktopModeEventLogger = mock(DesktopModeEventLogger::class.java) + + transitionObserver = + DesktopModeLoggerTransitionObserver( + context, mockShellInit, transitions, desktopModeEventLogger) + if (Transitions.ENABLE_SHELL_TRANSITIONS) { + val initRunnableCaptor = ArgumentCaptor.forClass(Runnable::class.java) + verify(mockShellInit).addInitCallback(initRunnableCaptor.capture(), same(transitionObserver)) + initRunnableCaptor.value.run() + } else { + transitionObserver.onInit() } - - @Test - fun transitToFrontWithFlagRecents_logTaskRemovedAndExitReasonOverview_sessionIdNull() { - val sessionId = 1 - // add a freeform task - transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM)) - transitionObserver.setLoggerSessionId(sessionId) - - // recents transition - val change = createChange(TRANSIT_TO_BACK, createTaskInfo(1, WINDOWING_MODE_FREEFORM)) - val transitionInfo = - TransitionInfoBuilder(TRANSIT_TO_FRONT, TRANSIT_FLAG_IS_RECENTS) - .addChange(change) - .build() - callOnTransitionReady(transitionInfo) - - verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any()) - verify(desktopModeEventLogger, times(1)) - .logSessionExit(eq(sessionId), eq(ExitReason.RETURN_HOME_OR_OVERVIEW)) - verifyZeroInteractions(desktopModeEventLogger) - assertThat(transitionObserver.getLoggerSessionId()).isNull() - } - - @Test - fun transitClose_logTaskRemovedAndExitReasonTaskFinished_sessionIdNull() { - val sessionId = 1 - // add a freeform task - transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM)) - transitionObserver.setLoggerSessionId(sessionId) - - // task closing - val change = createChange(TRANSIT_CLOSE, createTaskInfo(1, WINDOWING_MODE_FULLSCREEN)) - val transitionInfo = TransitionInfoBuilder(TRANSIT_CLOSE).addChange(change).build() - callOnTransitionReady(transitionInfo) - - verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any()) - verify(desktopModeEventLogger, times(1)) - .logSessionExit(eq(sessionId), eq(ExitReason.TASK_FINISHED)) - verifyZeroInteractions(desktopModeEventLogger) - assertThat(transitionObserver.getLoggerSessionId()).isNull() - } - - @Test - fun sessionExitByRecents_cancelledAnimation_sessionRestored() { - val sessionId = 1 - // add a freeform task to an existing session - transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM)) - transitionObserver.setLoggerSessionId(sessionId) - - // recents transition sent freeform window to back - val change = createChange(TRANSIT_TO_BACK, createTaskInfo(1, WINDOWING_MODE_FREEFORM)) - val transitionInfo1 = - TransitionInfoBuilder(TRANSIT_TO_FRONT, TRANSIT_FLAG_IS_RECENTS) - .addChange(change) - .build() - callOnTransitionReady(transitionInfo1) - verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any()) - verify(desktopModeEventLogger, times(1)) - .logSessionExit(eq(sessionId), eq(ExitReason.RETURN_HOME_OR_OVERVIEW)) - assertThat(transitionObserver.getLoggerSessionId()).isNull() - - val transitionInfo2 = TransitionInfoBuilder(TRANSIT_NONE).build() - callOnTransitionReady(transitionInfo2) - - verify(desktopModeEventLogger, times(1)).logSessionEnter(any(), any()) - verify(desktopModeEventLogger, times(1)).logTaskAdded(any(), any()) - } - - @Test - fun sessionAlreadyStarted_newFreeformTaskAdded_logsTaskAdded() { - val sessionId = 1 - // add an existing freeform task - transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM)) - transitionObserver.setLoggerSessionId(sessionId) - - // new freeform task added - val change = createChange(TRANSIT_OPEN, createTaskInfo(2, WINDOWING_MODE_FREEFORM)) - val transitionInfo = TransitionInfoBuilder(TRANSIT_OPEN, 0).addChange(change).build() - callOnTransitionReady(transitionInfo) - - verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any()) - verify(desktopModeEventLogger, never()).logSessionEnter(any(), any()) - } - - @Test - fun sessionAlreadyStarted_freeformTaskRemoved_logsTaskRemoved() { - val sessionId = 1 - // add two existing freeform tasks - transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM)) - transitionObserver.addTaskInfosToCachedMap(createTaskInfo(2, WINDOWING_MODE_FREEFORM)) - transitionObserver.setLoggerSessionId(sessionId) - - // new freeform task added - val change = createChange(TRANSIT_CLOSE, createTaskInfo(2, WINDOWING_MODE_FREEFORM)) - val transitionInfo = TransitionInfoBuilder(TRANSIT_CLOSE, 0).addChange(change).build() - callOnTransitionReady(transitionInfo) - - verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any()) - verify(desktopModeEventLogger, never()).logSessionExit(any(), any()) - } - - /** Simulate calling the onTransitionReady() method */ - private fun callOnTransitionReady(transitionInfo: TransitionInfo) { - val transition = mock(IBinder::class.java) - val startT = mock(SurfaceControl.Transaction::class.java) - val finishT = mock(SurfaceControl.Transaction::class.java) - - transitionObserver.onTransitionReady(transition, transitionInfo, startT, finishT) + } + + @Test + fun testRegistersObserverAtInit() { + verify(transitions).registerObserver(same(transitionObserver)) + } + + @Test + fun transitOpen_notFreeformWindow_doesNotLogTaskAddedOrSessionEnter() { + val change = createChange(TRANSIT_OPEN, createTaskInfo(1, WINDOWING_MODE_FULLSCREEN)) + val transitionInfo = TransitionInfoBuilder(TRANSIT_OPEN, 0).addChange(change).build() + + callOnTransitionReady(transitionInfo) + + verify(desktopModeEventLogger, never()).logSessionEnter(any(), any()) + verify(desktopModeEventLogger, never()).logTaskAdded(any(), any()) + } + + @Test + fun transitOpen_logTaskAddedAndEnterReasonAppFreeformIntent() { + val change = createChange(TRANSIT_OPEN, createTaskInfo(1, WINDOWING_MODE_FREEFORM)) + val transitionInfo = TransitionInfoBuilder(TRANSIT_OPEN, 0).addChange(change).build() + + callOnTransitionReady(transitionInfo) + val sessionId = transitionObserver.getLoggerSessionId() + + assertThat(sessionId).isNotNull() + verify(desktopModeEventLogger, times(1)) + .logSessionEnter(eq(sessionId!!), eq(EnterReason.APP_FREEFORM_INTENT)) + verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any()) + verifyZeroInteractions(desktopModeEventLogger) + } + + @Test + fun transitEndDragToDesktop_logTaskAddedAndEnterReasonAppHandleDrag() { + val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM)) + // task change is finalised when drag ends + val transitionInfo = + TransitionInfoBuilder(Transitions.TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP, 0) + .addChange(change) + .build() + + callOnTransitionReady(transitionInfo) + val sessionId = transitionObserver.getLoggerSessionId() + + assertThat(sessionId).isNotNull() + verify(desktopModeEventLogger, times(1)) + .logSessionEnter(eq(sessionId!!), eq(EnterReason.APP_HANDLE_DRAG)) + verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any()) + verifyZeroInteractions(desktopModeEventLogger) + } + + @Test + fun transitEnterDesktopByButtonTap_logTaskAddedAndEnterReasonButtonTap() { + val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM)) + val transitionInfo = + TransitionInfoBuilder(TRANSIT_ENTER_DESKTOP_FROM_APP_HANDLE_MENU_BUTTON, 0) + .addChange(change) + .build() + + callOnTransitionReady(transitionInfo) + val sessionId = transitionObserver.getLoggerSessionId() + + assertThat(sessionId).isNotNull() + verify(desktopModeEventLogger, times(1)) + .logSessionEnter(eq(sessionId!!), eq(EnterReason.APP_HANDLE_MENU_BUTTON)) + verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any()) + verifyZeroInteractions(desktopModeEventLogger) + } + + @Test + fun transitEnterDesktopFromAppFromOverview_logTaskAddedAndEnterReasonUnknown() { + val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM)) + val transitionInfo = + TransitionInfoBuilder(TRANSIT_ENTER_DESKTOP_FROM_APP_FROM_OVERVIEW, 0) + .addChange(change) + .build() + + callOnTransitionReady(transitionInfo) + val sessionId = transitionObserver.getLoggerSessionId() + + assertThat(sessionId).isNotNull() + verify(desktopModeEventLogger, times(1)) + .logSessionEnter(eq(sessionId!!), eq(EnterReason.APP_FROM_OVERVIEW)) + verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any()) + verifyZeroInteractions(desktopModeEventLogger) + } + + @Test + fun transitEnterDesktopFromKeyboardShortcut_logTaskAddedAndEnterReasonKeyboardShortcut() { + val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM)) + val transitionInfo = + TransitionInfoBuilder(TRANSIT_ENTER_DESKTOP_FROM_KEYBOARD_SHORTCUT, 0) + .addChange(change) + .build() + + callOnTransitionReady(transitionInfo) + val sessionId = transitionObserver.getLoggerSessionId() + + assertThat(sessionId).isNotNull() + verify(desktopModeEventLogger, times(1)) + .logSessionEnter(eq(sessionId!!), eq(EnterReason.KEYBOARD_SHORTCUT_ENTER)) + verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any()) + verifyZeroInteractions(desktopModeEventLogger) + } + + @Test + fun transitEnterDesktopFromUnknown_logTaskAddedAndEnterReasonUnknown() { + val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM)) + val transitionInfo = + TransitionInfoBuilder(TRANSIT_ENTER_DESKTOP_FROM_UNKNOWN, 0).addChange(change).build() + + callOnTransitionReady(transitionInfo) + val sessionId = transitionObserver.getLoggerSessionId() + + assertThat(sessionId).isNotNull() + verify(desktopModeEventLogger, times(1)) + .logSessionEnter(eq(sessionId!!), eq(EnterReason.UNKNOWN_ENTER)) + verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any()) + verifyZeroInteractions(desktopModeEventLogger) + } + + @Test + fun transitWake_logTaskAddedAndEnterReasonScreenOn() { + val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM)) + val transitionInfo = TransitionInfoBuilder(TRANSIT_WAKE, 0).addChange(change).build() + + callOnTransitionReady(transitionInfo) + val sessionId = transitionObserver.getLoggerSessionId() + + assertThat(sessionId).isNotNull() + verify(desktopModeEventLogger, times(1)) + .logSessionEnter(eq(sessionId!!), eq(EnterReason.SCREEN_ON)) + verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any()) + verifyZeroInteractions(desktopModeEventLogger) + } + + @Test + fun transitSleep_logTaskAddedAndExitReasonScreenOff_sessionIdNull() { + val sessionId = 1 + // add a freeform task + transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM)) + transitionObserver.setLoggerSessionId(sessionId) + + val transitionInfo = TransitionInfoBuilder(TRANSIT_SLEEP).build() + callOnTransitionReady(transitionInfo) + + verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any()) + verify(desktopModeEventLogger, times(1)) + .logSessionExit(eq(sessionId), eq(ExitReason.SCREEN_OFF)) + verifyZeroInteractions(desktopModeEventLogger) + assertThat(transitionObserver.getLoggerSessionId()).isNull() + } + + @Test + fun transitExitDesktopTaskDrag_logTaskRemovedAndExitReasonDragToExit_sessionIdNull() { + val sessionId = 1 + // add a freeform task + transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM)) + transitionObserver.setLoggerSessionId(sessionId) + + // window mode changing from FREEFORM to FULLSCREEN + val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FULLSCREEN)) + val transitionInfo = + TransitionInfoBuilder(TRANSIT_EXIT_DESKTOP_MODE_TASK_DRAG).addChange(change).build() + callOnTransitionReady(transitionInfo) + + verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any()) + verify(desktopModeEventLogger, times(1)) + .logSessionExit(eq(sessionId), eq(ExitReason.DRAG_TO_EXIT)) + verifyZeroInteractions(desktopModeEventLogger) + assertThat(transitionObserver.getLoggerSessionId()).isNull() + } + + @Test + fun transitExitDesktopAppHandleButton_logTaskRemovedAndExitReasonButton_sessionIdNull() { + val sessionId = 1 + // add a freeform task + transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM)) + transitionObserver.setLoggerSessionId(sessionId) + + // window mode changing from FREEFORM to FULLSCREEN + val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FULLSCREEN)) + val transitionInfo = + TransitionInfoBuilder(TRANSIT_EXIT_DESKTOP_MODE_HANDLE_MENU_BUTTON) + .addChange(change) + .build() + callOnTransitionReady(transitionInfo) + + verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any()) + verify(desktopModeEventLogger, times(1)) + .logSessionExit(eq(sessionId), eq(ExitReason.APP_HANDLE_MENU_BUTTON_EXIT)) + verifyZeroInteractions(desktopModeEventLogger) + assertThat(transitionObserver.getLoggerSessionId()).isNull() + } + + @Test + fun transitExitDesktopUsingKeyboard_logTaskRemovedAndExitReasonKeyboard_sessionIdNull() { + val sessionId = 1 + // add a freeform task + transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM)) + transitionObserver.setLoggerSessionId(sessionId) + + // window mode changing from FREEFORM to FULLSCREEN + val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FULLSCREEN)) + val transitionInfo = + TransitionInfoBuilder(TRANSIT_EXIT_DESKTOP_MODE_KEYBOARD_SHORTCUT).addChange(change).build() + callOnTransitionReady(transitionInfo) + + verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any()) + verify(desktopModeEventLogger, times(1)) + .logSessionExit(eq(sessionId), eq(ExitReason.KEYBOARD_SHORTCUT_EXIT)) + verifyZeroInteractions(desktopModeEventLogger) + assertThat(transitionObserver.getLoggerSessionId()).isNull() + } + + @Test + fun transitExitDesktopUnknown_logTaskRemovedAndExitReasonUnknown_sessionIdNull() { + val sessionId = 1 + // add a freeform task + transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM)) + transitionObserver.setLoggerSessionId(sessionId) + + // window mode changing from FREEFORM to FULLSCREEN + val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FULLSCREEN)) + val transitionInfo = + TransitionInfoBuilder(TRANSIT_EXIT_DESKTOP_MODE_UNKNOWN).addChange(change).build() + callOnTransitionReady(transitionInfo) + + verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any()) + verify(desktopModeEventLogger, times(1)) + .logSessionExit(eq(sessionId), eq(ExitReason.UNKNOWN_EXIT)) + verifyZeroInteractions(desktopModeEventLogger) + assertThat(transitionObserver.getLoggerSessionId()).isNull() + } + + @Test + fun transitToFrontWithFlagRecents_logTaskRemovedAndExitReasonOverview_sessionIdNull() { + val sessionId = 1 + // add a freeform task + transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM)) + transitionObserver.setLoggerSessionId(sessionId) + + // recents transition + val change = createChange(TRANSIT_TO_BACK, createTaskInfo(1, WINDOWING_MODE_FREEFORM)) + val transitionInfo = + TransitionInfoBuilder(TRANSIT_TO_FRONT, TRANSIT_FLAG_IS_RECENTS).addChange(change).build() + callOnTransitionReady(transitionInfo) + + verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any()) + verify(desktopModeEventLogger, times(1)) + .logSessionExit(eq(sessionId), eq(ExitReason.RETURN_HOME_OR_OVERVIEW)) + verifyZeroInteractions(desktopModeEventLogger) + assertThat(transitionObserver.getLoggerSessionId()).isNull() + } + + @Test + fun transitClose_logTaskRemovedAndExitReasonTaskFinished_sessionIdNull() { + val sessionId = 1 + // add a freeform task + transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM)) + transitionObserver.setLoggerSessionId(sessionId) + + // task closing + val change = createChange(TRANSIT_CLOSE, createTaskInfo(1, WINDOWING_MODE_FULLSCREEN)) + val transitionInfo = TransitionInfoBuilder(TRANSIT_CLOSE).addChange(change).build() + callOnTransitionReady(transitionInfo) + + verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any()) + verify(desktopModeEventLogger, times(1)) + .logSessionExit(eq(sessionId), eq(ExitReason.TASK_FINISHED)) + verifyZeroInteractions(desktopModeEventLogger) + assertThat(transitionObserver.getLoggerSessionId()).isNull() + } + + @Test + fun sessionExitByRecents_cancelledAnimation_sessionRestored() { + val sessionId = 1 + // add a freeform task to an existing session + transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM)) + transitionObserver.setLoggerSessionId(sessionId) + + // recents transition sent freeform window to back + val change = createChange(TRANSIT_TO_BACK, createTaskInfo(1, WINDOWING_MODE_FREEFORM)) + val transitionInfo1 = + TransitionInfoBuilder(TRANSIT_TO_FRONT, TRANSIT_FLAG_IS_RECENTS).addChange(change).build() + callOnTransitionReady(transitionInfo1) + verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any()) + verify(desktopModeEventLogger, times(1)) + .logSessionExit(eq(sessionId), eq(ExitReason.RETURN_HOME_OR_OVERVIEW)) + assertThat(transitionObserver.getLoggerSessionId()).isNull() + + val transitionInfo2 = TransitionInfoBuilder(TRANSIT_NONE).build() + callOnTransitionReady(transitionInfo2) + + verify(desktopModeEventLogger, times(1)).logSessionEnter(any(), any()) + verify(desktopModeEventLogger, times(1)).logTaskAdded(any(), any()) + } + + @Test + fun sessionAlreadyStarted_newFreeformTaskAdded_logsTaskAdded() { + val sessionId = 1 + // add an existing freeform task + transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM)) + transitionObserver.setLoggerSessionId(sessionId) + + // new freeform task added + val change = createChange(TRANSIT_OPEN, createTaskInfo(2, WINDOWING_MODE_FREEFORM)) + val transitionInfo = TransitionInfoBuilder(TRANSIT_OPEN, 0).addChange(change).build() + callOnTransitionReady(transitionInfo) + + verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any()) + verify(desktopModeEventLogger, never()).logSessionEnter(any(), any()) + } + + @Test + fun sessionAlreadyStarted_freeformTaskRemoved_logsTaskRemoved() { + val sessionId = 1 + // add two existing freeform tasks + transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM)) + transitionObserver.addTaskInfosToCachedMap(createTaskInfo(2, WINDOWING_MODE_FREEFORM)) + transitionObserver.setLoggerSessionId(sessionId) + + // new freeform task added + val change = createChange(TRANSIT_CLOSE, createTaskInfo(2, WINDOWING_MODE_FREEFORM)) + val transitionInfo = TransitionInfoBuilder(TRANSIT_CLOSE, 0).addChange(change).build() + callOnTransitionReady(transitionInfo) + + verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any()) + verify(desktopModeEventLogger, never()).logSessionExit(any(), any()) + } + + /** Simulate calling the onTransitionReady() method */ + private fun callOnTransitionReady(transitionInfo: TransitionInfo) { + val transition = mock(IBinder::class.java) + val startT = mock(SurfaceControl.Transaction::class.java) + val finishT = mock(SurfaceControl.Transaction::class.java) + + transitionObserver.onTransitionReady(transition, transitionInfo, startT, finishT) + } + + companion object { + fun createTaskInfo(taskId: Int, windowMode: Int): ActivityManager.RunningTaskInfo { + val taskInfo = ActivityManager.RunningTaskInfo() + taskInfo.taskId = taskId + taskInfo.configuration.windowConfiguration.windowingMode = windowMode + + return taskInfo } - companion object { - fun createTaskInfo(taskId: Int, windowMode: Int): ActivityManager.RunningTaskInfo { - val taskInfo = ActivityManager.RunningTaskInfo() - taskInfo.taskId = taskId - taskInfo.configuration.windowConfiguration.windowingMode = windowMode - - return taskInfo - } - - fun createChange(mode: Int, taskInfo: ActivityManager.RunningTaskInfo): Change { - val change = - Change( - WindowContainerToken(mock(IWindowContainerToken::class.java)), - mock(SurfaceControl::class.java) - ) - change.mode = mode - change.taskInfo = taskInfo - return change - } + fun createChange(mode: Int, taskInfo: ActivityManager.RunningTaskInfo): Change { + val change = + Change( + WindowContainerToken(mock(IWindowContainerToken::class.java)), + mock(SurfaceControl::class.java)) + change.mode = mode + change.taskInfo = taskInfo + return change } + } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java index 56c4ceacc8ab..e291c0e1a151 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java @@ -113,6 +113,8 @@ public class RecentTasksControllerTest extends ShellTestCase { private DisplayInsetsController mDisplayInsetsController; @Mock private IRecentTasksListener mRecentTasksListener; + @Mock + private TaskStackTransitionObserver mTaskStackTransitionObserver; @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); @@ -139,7 +141,8 @@ public class RecentTasksControllerTest extends ShellTestCase { mDisplayInsetsController, mMainExecutor)); mRecentTasksControllerReal = new RecentTasksController(mContext, mShellInit, mShellController, mShellCommandHandler, mTaskStackListener, mActivityTaskManager, - Optional.of(mDesktopModeTaskRepository), mMainExecutor); + Optional.of(mDesktopModeTaskRepository), mTaskStackTransitionObserver, + mMainExecutor); mRecentTasksController = spy(mRecentTasksControllerReal); mShellTaskOrganizer = new ShellTaskOrganizer(mShellInit, mShellCommandHandler, null /* sizeCompatUI */, Optional.empty(), Optional.of(mRecentTasksController), @@ -557,6 +560,30 @@ public class RecentTasksControllerTest extends ShellTestCase { } @Test + @EnableFlags(Flags.FLAG_ENABLE_TASK_STACK_OBSERVER_IN_SHELL) + public void onTaskMovedToFront_TaskStackObserverEnabled_triggersOnTaskMovedToFront() + throws Exception { + mRecentTasksControllerReal.registerRecentTasksListener(mRecentTasksListener); + ActivityManager.RunningTaskInfo taskInfo = makeRunningTaskInfo(/* taskId= */10); + + mRecentTasksControllerReal.onTaskMovedToFrontThroughTransition(taskInfo); + + verify(mRecentTasksListener).onTaskMovedToFront(taskInfo); + } + + @Test + @DisableFlags(Flags.FLAG_ENABLE_TASK_STACK_OBSERVER_IN_SHELL) + public void onTaskMovedToFront_TaskStackObserverEnabled_doesNotTriggersOnTaskMovedToFront() + throws Exception { + mRecentTasksControllerReal.registerRecentTasksListener(mRecentTasksListener); + ActivityManager.RunningTaskInfo taskInfo = makeRunningTaskInfo(/* taskId= */10); + + mRecentTasksControllerReal.onTaskMovedToFront(taskInfo); + + verify(mRecentTasksListener, never()).onTaskMovedToFront(any()); + } + + @Test public void getNullSplitBoundsNonSplitTask() { SplitBounds sb = mRecentTasksController.getSplitBoundsForTaskId(3); assertNull("splitBounds should be null for non-split task", sb); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/TaskStackTransitionObserverTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/TaskStackTransitionObserverTest.kt new file mode 100644 index 000000000000..f9599702e763 --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/TaskStackTransitionObserverTest.kt @@ -0,0 +1,217 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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.recents + +import android.app.ActivityManager +import android.app.WindowConfiguration +import android.os.IBinder +import android.platform.test.annotations.EnableFlags +import android.platform.test.flag.junit.SetFlagsRule +import android.testing.AndroidTestingRunner +import android.view.SurfaceControl +import android.view.WindowManager +import android.window.IWindowContainerToken +import android.window.TransitionInfo +import android.window.WindowContainerToken +import androidx.test.filters.SmallTest +import com.android.window.flags.Flags +import com.android.wm.shell.TestShellExecutor +import com.android.wm.shell.common.ShellExecutor +import com.android.wm.shell.sysui.ShellInit +import com.android.wm.shell.transition.TransitionInfoBuilder +import com.android.wm.shell.transition.Transitions +import com.google.common.truth.Truth.assertThat +import dagger.Lazy +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.Mockito +import org.mockito.MockitoAnnotations +import org.mockito.kotlin.same +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever + + +/** + * Test class for {@link TaskStackTransitionObserver} + * + * Usage: atest WMShellUnitTests:TaskStackTransitionObserverTest + */ +@SmallTest +@RunWith(AndroidTestingRunner::class) +class TaskStackTransitionObserverTest { + + @JvmField @Rule val setFlagsRule = SetFlagsRule() + + @Mock private lateinit var shellInit: ShellInit + @Mock lateinit var testExecutor: ShellExecutor + @Mock private lateinit var transitionsLazy: Lazy<Transitions> + @Mock private lateinit var transitions: Transitions + @Mock private lateinit var mockTransitionBinder: IBinder + + private lateinit var transitionObserver: TaskStackTransitionObserver + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + shellInit = Mockito.spy(ShellInit(testExecutor)) + whenever(transitionsLazy.get()).thenReturn(transitions) + transitionObserver = TaskStackTransitionObserver(transitionsLazy, shellInit) + if (Transitions.ENABLE_SHELL_TRANSITIONS) { + val initRunnableCaptor = ArgumentCaptor.forClass(Runnable::class.java) + verify(shellInit) + .addInitCallback(initRunnableCaptor.capture(), same(transitionObserver)) + initRunnableCaptor.value.run() + } else { + transitionObserver.onInit() + } + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_TASK_STACK_OBSERVER_IN_SHELL) + fun testRegistersObserverAtInit() { + verify(transitions).registerObserver(same(transitionObserver)) + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_TASK_STACK_OBSERVER_IN_SHELL) + fun taskCreated_freeformWindow_listenerNotified() { + val listener = TestListener() + val executor = TestShellExecutor() + transitionObserver.addTaskStackTransitionObserverListener(listener, executor) + val change = + createChange( + WindowManager.TRANSIT_OPEN, + createTaskInfo(1, WindowConfiguration.WINDOWING_MODE_FREEFORM) + ) + val transitionInfo = + TransitionInfoBuilder(WindowManager.TRANSIT_OPEN, 0).addChange(change).build() + + callOnTransitionReady(transitionInfo) + callOnTransitionFinished() + executor.flushAll() + + assertThat(listener.taskInfoToBeNotified.taskId).isEqualTo(change.taskInfo?.taskId) + assertThat(listener.taskInfoToBeNotified.windowingMode) + .isEqualTo(change.taskInfo?.windowingMode) + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_TASK_STACK_OBSERVER_IN_SHELL) + fun taskCreated_fullscreenWindow_listenerNotNotified() { + val listener = TestListener() + val executor = TestShellExecutor() + transitionObserver.addTaskStackTransitionObserverListener(listener, executor) + val change = + createChange( + WindowManager.TRANSIT_OPEN, + createTaskInfo(1, WindowConfiguration.WINDOWING_MODE_FULLSCREEN) + ) + val transitionInfo = + TransitionInfoBuilder(WindowManager.TRANSIT_OPEN, 0).addChange(change).build() + + callOnTransitionReady(transitionInfo) + callOnTransitionFinished() + executor.flushAll() + + assertThat(listener.taskInfoToBeNotified.taskId).isEqualTo(0) + assertThat(listener.taskInfoToBeNotified.windowingMode) + .isEqualTo(WindowConfiguration.WINDOWING_MODE_UNDEFINED) + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_TASK_STACK_OBSERVER_IN_SHELL) + fun taskCreated_freeformWindowOnTopOfFreeform_listenerNotified() { + val listener = TestListener() + val executor = TestShellExecutor() + transitionObserver.addTaskStackTransitionObserverListener(listener, executor) + val freeformOpenChange = + createChange( + WindowManager.TRANSIT_OPEN, + createTaskInfo(1, WindowConfiguration.WINDOWING_MODE_FREEFORM) + ) + val freeformReorderChange = + createChange( + WindowManager.TRANSIT_TO_BACK, + createTaskInfo(2, WindowConfiguration.WINDOWING_MODE_FREEFORM) + ) + val transitionInfo = + TransitionInfoBuilder(WindowManager.TRANSIT_OPEN, 0) + .addChange(freeformOpenChange) + .addChange(freeformReorderChange) + .build() + + callOnTransitionReady(transitionInfo) + callOnTransitionFinished() + executor.flushAll() + + assertThat(listener.taskInfoToBeNotified.taskId) + .isEqualTo(freeformOpenChange.taskInfo?.taskId) + assertThat(listener.taskInfoToBeNotified.windowingMode) + .isEqualTo(freeformOpenChange.taskInfo?.windowingMode) + } + + class TestListener : TaskStackTransitionObserver.TaskStackTransitionObserverListener { + var taskInfoToBeNotified = ActivityManager.RunningTaskInfo() + + override fun onTaskMovedToFrontThroughTransition( + taskInfo: ActivityManager.RunningTaskInfo + ) { + taskInfoToBeNotified = taskInfo + } + } + + /** Simulate calling the onTransitionReady() method */ + private fun callOnTransitionReady(transitionInfo: TransitionInfo) { + val startT = Mockito.mock(SurfaceControl.Transaction::class.java) + val finishT = Mockito.mock(SurfaceControl.Transaction::class.java) + + transitionObserver.onTransitionReady(mockTransitionBinder, transitionInfo, startT, finishT) + } + + /** Simulate calling the onTransitionFinished() method */ + private fun callOnTransitionFinished() { + transitionObserver.onTransitionFinished(mockTransitionBinder, false) + } + + companion object { + fun createTaskInfo(taskId: Int, windowingMode: Int): ActivityManager.RunningTaskInfo { + val taskInfo = ActivityManager.RunningTaskInfo() + taskInfo.taskId = taskId + taskInfo.configuration.windowConfiguration.windowingMode = windowingMode + + return taskInfo + } + + fun createChange( + mode: Int, + taskInfo: ActivityManager.RunningTaskInfo + ): TransitionInfo.Change { + val change = + TransitionInfo.Change( + WindowContainerToken(Mockito.mock(IWindowContainerToken::class.java)), + Mockito.mock(SurfaceControl::class.java) + ) + change.mode = mode + change.taskInfo = taskInfo + return change + } + } +} 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 1b223cf2bd59..46c158908226 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 @@ -23,12 +23,15 @@ import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType import static android.view.WindowInsetsController.APPEARANCE_TRANSPARENT_CAPTION_BAR_BACKGROUND; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; +import static com.android.wm.shell.MockSurfaceControlHelper.createMockSurfaceControlTransaction; import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyInt; import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -37,7 +40,7 @@ import android.app.ActivityManager; import android.content.ComponentName; import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; -import android.content.res.Configuration; +import android.content.pm.PackageManager; import android.content.res.Resources; import android.content.res.TypedArray; import android.os.Handler; @@ -47,13 +50,19 @@ import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.SetFlagsRule; import android.testing.AndroidTestingRunner; import android.testing.TestableContext; +import android.view.AttachedSurfaceControl; import android.view.Choreographer; import android.view.Display; +import android.view.GestureDetector; +import android.view.InsetsState; +import android.view.MotionEvent; import android.view.SurfaceControl; import android.view.SurfaceControlViewHost; +import android.view.View; import android.view.WindowManager; import android.window.WindowContainerTransaction; +import androidx.annotation.Nullable; import androidx.test.filters.SmallTest; import com.android.dx.mockito.inline.extended.StaticMockitoSession; @@ -74,6 +83,7 @@ import org.junit.BeforeClass; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.quality.Strictness; @@ -112,18 +122,25 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { @Mock private Supplier<SurfaceControl.Transaction> mMockTransactionSupplier; @Mock - private SurfaceControl.Transaction mMockTransaction; - @Mock private SurfaceControl mMockSurfaceControl; @Mock private SurfaceControlViewHost mMockSurfaceControlViewHost; @Mock + private AttachedSurfaceControl mMockRootSurfaceControl; + @Mock private WindowDecoration.SurfaceControlViewHostFactory mMockSurfaceControlViewHostFactory; @Mock private TypedArray mMockRoundedCornersRadiusArray; - private final Configuration mConfiguration = new Configuration(); + @Mock + private TestTouchEventListener mMockTouchEventListener; + @Mock + private DesktopModeWindowDecoration.ExclusionRegionListener mMockExclusionRegionListener; + @Mock + private PackageManager mMockPackageManager; + private final InsetsState mInsetsState = new InsetsState(); + private SurfaceControl.Transaction mMockTransaction; private StaticMockitoSession mMockitoSession; private TestableContext mTestableContext; @@ -145,9 +162,17 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { when(DesktopModeStatus.useDesktopOverrideDensity()).thenReturn(false); doReturn(mMockSurfaceControlViewHost).when(mMockSurfaceControlViewHostFactory).create( any(), any(), any()); + when(mMockSurfaceControlViewHost.getRootSurfaceControl()) + .thenReturn(mMockRootSurfaceControl); + mMockTransaction = createMockSurfaceControlTransaction(); doReturn(mMockTransaction).when(mMockTransactionSupplier).get(); mTestableContext = new TestableContext(mContext); mTestableContext.ensureTestableResources(); + mContext.setMockPackageManager(mMockPackageManager); + when(mMockPackageManager.getApplicationLabel(any())).thenReturn("applicationLabel"); + final Display defaultDisplay = mock(Display.class); + doReturn(defaultDisplay).when(mMockDisplayController).getDisplay(Display.DEFAULT_DISPLAY); + doReturn(mInsetsState).when(mMockDisplayController).getInsetsState(anyInt()); } @After @@ -341,6 +366,99 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { assertThat(hasNoInputChannelFeature(relayoutParams)).isTrue(); } + @Test + public void relayout_fullscreenTask_appliesTransactionImmediately() { + final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true); + final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo)); + taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN); + + spyWindowDecor.relayout(taskInfo); + + verify(mMockTransaction).apply(); + verify(mMockRootSurfaceControl, never()).applyTransactionOnDraw(any()); + } + + @Test + public void relayout_freeformTask_appliesTransactionOnDraw() { + final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true); + final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo)); + taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM); + // Make non-resizable to avoid dealing with input-permissions (MONITOR_INPUT) + taskInfo.isResizeable = false; + + spyWindowDecor.relayout(taskInfo); + + verify(mMockTransaction, never()).apply(); + verify(mMockRootSurfaceControl).applyTransactionOnDraw(mMockTransaction); + } + + @Test + public void relayout_fullscreenTask_doesNotCreateViewHostImmediately() { + final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true); + final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo)); + taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN); + + spyWindowDecor.relayout(taskInfo); + + verify(mMockSurfaceControlViewHostFactory, never()).create(any(), any(), any()); + } + + @Test + public void relayout_fullscreenTask_postsViewHostCreation() { + final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true); + final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo)); + taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN); + + ArgumentCaptor<Runnable> runnableArgument = ArgumentCaptor.forClass(Runnable.class); + spyWindowDecor.relayout(taskInfo); + + verify(mMockHandler).post(runnableArgument.capture()); + runnableArgument.getValue().run(); + verify(mMockSurfaceControlViewHostFactory).create(any(), any(), any()); + } + + @Test + public void relayout_freeformTask_createsViewHostImmediately() { + final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true); + final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo)); + taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM); + // Make non-resizable to avoid dealing with input-permissions (MONITOR_INPUT) + taskInfo.isResizeable = false; + + spyWindowDecor.relayout(taskInfo); + + verify(mMockSurfaceControlViewHostFactory).create(any(), any(), any()); + verify(mMockHandler, never()).post(any()); + } + + @Test + public void relayout_removesExistingHandlerCallback() { + final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true); + final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo)); + taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN); + ArgumentCaptor<Runnable> runnableArgument = ArgumentCaptor.forClass(Runnable.class); + spyWindowDecor.relayout(taskInfo); + verify(mMockHandler).post(runnableArgument.capture()); + + spyWindowDecor.relayout(taskInfo); + + verify(mMockHandler).removeCallbacks(runnableArgument.getValue()); + } + + @Test + public void close_removesExistingHandlerCallback() { + final ActivityManager.RunningTaskInfo taskInfo = createTaskInfo(/* visible= */ true); + final DesktopModeWindowDecoration spyWindowDecor = spy(createWindowDecoration(taskInfo)); + taskInfo.configuration.windowConfiguration.setWindowingMode(WINDOWING_MODE_FULLSCREEN); + ArgumentCaptor<Runnable> runnableArgument = ArgumentCaptor.forClass(Runnable.class); + spyWindowDecor.relayout(taskInfo); + verify(mMockHandler).post(runnableArgument.capture()); + + spyWindowDecor.close(); + + verify(mMockHandler).removeCallbacks(runnableArgument.getValue()); + } + private void fillRoundedCornersResources(int fillValue) { when(mMockRoundedCornersRadiusArray.getDimensionPixelSize(anyInt(), anyInt())) .thenReturn(fillValue); @@ -361,12 +479,16 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { private DesktopModeWindowDecoration createWindowDecoration( ActivityManager.RunningTaskInfo taskInfo) { - return new DesktopModeWindowDecoration(mContext, mMockDisplayController, - mMockShellTaskOrganizer, taskInfo, mMockSurfaceControl, + DesktopModeWindowDecoration windowDecor = new DesktopModeWindowDecoration(mContext, + mMockDisplayController, mMockShellTaskOrganizer, taskInfo, mMockSurfaceControl, mMockHandler, mMockChoreographer, mMockSyncQueue, mMockRootTaskDisplayAreaOrganizer, SurfaceControl.Builder::new, mMockTransactionSupplier, WindowContainerTransaction::new, SurfaceControl::new, mMockSurfaceControlViewHostFactory); + windowDecor.setCaptionListeners(mMockTouchEventListener, mMockTouchEventListener, + mMockTouchEventListener, mMockTouchEventListener); + windowDecor.setExclusionRegionListener(mMockExclusionRegionListener); + return windowDecor; } private ActivityManager.RunningTaskInfo createTaskInfo(boolean visible) { @@ -391,4 +513,32 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { return (params.mInputFeatures & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) != 0; } + + private static class TestTouchEventListener extends GestureDetector.SimpleOnGestureListener + implements View.OnClickListener, View.OnTouchListener, View.OnLongClickListener, + View.OnGenericMotionListener, DragDetector.MotionEventHandler { + + @Override + public void onClick(View v) {} + + @Override + public boolean onGenericMotion(View v, MotionEvent event) { + return false; + } + + @Override + public boolean onLongClick(View v) { + return false; + } + + @Override + public boolean onTouch(View v, MotionEvent event) { + return false; + } + + @Override + public boolean handleMotionEvent(@Nullable View v, MotionEvent ev) { + return false; + } + } } 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 e73069ab52a7..f3603e1d9b46 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 @@ -32,6 +32,7 @@ import static junit.framework.Assert.assertTrue; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThrows; import static org.mockito.ArgumentMatchers.anyFloat; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.any; @@ -828,6 +829,36 @@ public class WindowDecorationTests extends ShellTestCase { eq(mMockTaskSurface), anyInt(), anyInt()); } + @Test + public void updateViewHost_applyTransactionOnDrawIsTrue_surfaceControlIsUpdated() { + final TestWindowDecoration windowDecor = createWindowDecoration( + new TestRunningTaskInfoBuilder().build()); + mRelayoutParams.mApplyStartTransactionOnDraw = true; + + windowDecor.updateViewHost(mRelayoutParams, mMockSurfaceControlStartT, mRelayoutResult); + + verify(mMockRootSurfaceControl).applyTransactionOnDraw(mMockSurfaceControlStartT); + } + + @Test + public void updateViewHost_nullDrawTransaction_applyTransactionOnDrawIsTrue_throwsException() { + final TestWindowDecoration windowDecor = createWindowDecoration( + new TestRunningTaskInfoBuilder().build()); + mRelayoutParams.mApplyStartTransactionOnDraw = true; + + assertThrows(IllegalArgumentException.class, + () -> windowDecor.updateViewHost( + mRelayoutParams, null /* onDrawTransaction */, mRelayoutResult)); + } + + @Test + public void updateViewHost_nullDrawTransaction_applyTransactionOnDrawIsFalse_doesNotThrow() { + final TestWindowDecoration windowDecor = createWindowDecoration( + new TestRunningTaskInfoBuilder().build()); + mRelayoutParams.mApplyStartTransactionOnDraw = false; + + windowDecor.updateViewHost(mRelayoutParams, null /* onDrawTransaction */, mRelayoutResult); + } private TestWindowDecoration createWindowDecoration(ActivityManager.RunningTaskInfo taskInfo) { return new TestWindowDecoration(mContext, mMockDisplayController, mMockShellTaskOrganizer, diff --git a/nfc/api/system-current.txt b/nfc/api/system-current.txt index 055ccbced58a..3375e18c001d 100644 --- a/nfc/api/system-current.txt +++ b/nfc/api/system-current.txt @@ -57,7 +57,9 @@ package android.nfc { @FlaggedApi("android.nfc.nfc_oem_extension") public final class NfcOemExtension { method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void clearPreference(); + method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void maybeTriggerFirmwareUpdate(); method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void registerCallback(@NonNull java.util.concurrent.Executor, @NonNull android.nfc.NfcOemExtension.Callback); + method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void synchronizeScreenState(); method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void unregisterCallback(@NonNull android.nfc.NfcOemExtension.Callback); } diff --git a/nfc/java/android/nfc/INfcAdapter.aidl b/nfc/java/android/nfc/INfcAdapter.aidl index 7150b54cf7f1..fd77820afc81 100644 --- a/nfc/java/android/nfc/INfcAdapter.aidl +++ b/nfc/java/android/nfc/INfcAdapter.aidl @@ -110,4 +110,6 @@ interface INfcAdapter void registerOemExtensionCallback(INfcOemExtensionCallback callbacks); void unregisterOemExtensionCallback(INfcOemExtensionCallback callbacks); void clearPreference(); + void setScreenState(); + void checkFirmware(); } diff --git a/nfc/java/android/nfc/NfcOemExtension.java b/nfc/java/android/nfc/NfcOemExtension.java index 1eff58cb80fd..f6138a63fae4 100644 --- a/nfc/java/android/nfc/NfcOemExtension.java +++ b/nfc/java/android/nfc/NfcOemExtension.java @@ -141,6 +141,34 @@ public final class NfcOemExtension { } } + /** + * Get the screen state from system and set it to current screen state. + */ + @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) + @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) + public void synchronizeScreenState() { + try { + NfcAdapter.sService.setScreenState(); + } catch (RemoteException e) { + mAdapter.attemptDeadServiceRecovery(e); + } + } + + /** + * Check if the firmware needs updating. + * + * <p>If an update is needed, a firmware will be triggered when NFC is disabled. + */ + @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) + @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) + public void maybeTriggerFirmwareUpdate() { + try { + NfcAdapter.sService.checkFirmware(); + } catch (RemoteException e) { + mAdapter.attemptDeadServiceRecovery(e); + } + } + private final class NfcOemExtensionCallback extends INfcOemExtensionCallback.Stub { @Override public void onTagConnected(boolean connected, Tag tag) throws RemoteException { diff --git a/packages/CredentialManager/res/values-pt-rBR/strings.xml b/packages/CredentialManager/res/values-pt-rBR/strings.xml index bc8a969ff44e..91385c6b8f62 100644 --- a/packages/CredentialManager/res/values-pt-rBR/strings.xml +++ b/packages/CredentialManager/res/values-pt-rBR/strings.xml @@ -48,7 +48,7 @@ <string name="passwords" msgid="5419394230391253816">"senhas"</string> <string name="sign_ins" msgid="4710739369149469208">"logins"</string> <string name="sign_in_info" msgid="2627704710674232328">"informações de login"</string> - <string name="save_credential_to_title" msgid="3172811692275634301">"Salvar <xliff:g id="CREDENTIALTYPES">%1$s</xliff:g> em"</string> + <string name="save_credential_to_title" msgid="3172811692275634301">"Salvar <xliff:g id="CREDENTIALTYPES">%1$s</xliff:g> no"</string> <string name="create_passkey_in_other_device_title" msgid="2360053098931886245">"Criar chave de acesso em outro dispositivo?"</string> <string name="save_password_on_other_device_title" msgid="5829084591948321207">"Salvar senha em outro dispositivo?"</string> <string name="save_sign_in_on_other_device_title" msgid="2827990118560134692">"Salvar credenciais de login em outro dispositivo?"</string> @@ -57,9 +57,9 @@ <string name="set_as_default" msgid="4415328591568654603">"Definir como padrão"</string> <string name="settings" msgid="6536394145760913145">"Configurações"</string> <string name="use_once" msgid="9027366575315399714">"Usar uma vez"</string> - <string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> senhas • <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> chaves de acesso"</string> - <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> senhas"</string> - <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> chaves de acesso"</string> + <string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"Senhas (<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>) • Chaves de acesso (<xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g>)"</string> + <string name="more_options_usage_passwords" msgid="1632047277723187813">"Senhas (<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>)"</string> + <string name="more_options_usage_passkeys" msgid="5390320437243042237">"Chaves de acesso (<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g>)"</string> <string name="more_options_usage_credentials" msgid="1785697001787193984">"<xliff:g id="TOTALCREDENTIALSNUMBER">%1$s</xliff:g> credenciais"</string> <string name="passkey_before_subtitle" msgid="2448119456208647444">"Chave de acesso"</string> <string name="another_device" msgid="5147276802037801217">"Outro dispositivo"</string> diff --git a/packages/CredentialManager/res/values-pt/strings.xml b/packages/CredentialManager/res/values-pt/strings.xml index bc8a969ff44e..91385c6b8f62 100644 --- a/packages/CredentialManager/res/values-pt/strings.xml +++ b/packages/CredentialManager/res/values-pt/strings.xml @@ -48,7 +48,7 @@ <string name="passwords" msgid="5419394230391253816">"senhas"</string> <string name="sign_ins" msgid="4710739369149469208">"logins"</string> <string name="sign_in_info" msgid="2627704710674232328">"informações de login"</string> - <string name="save_credential_to_title" msgid="3172811692275634301">"Salvar <xliff:g id="CREDENTIALTYPES">%1$s</xliff:g> em"</string> + <string name="save_credential_to_title" msgid="3172811692275634301">"Salvar <xliff:g id="CREDENTIALTYPES">%1$s</xliff:g> no"</string> <string name="create_passkey_in_other_device_title" msgid="2360053098931886245">"Criar chave de acesso em outro dispositivo?"</string> <string name="save_password_on_other_device_title" msgid="5829084591948321207">"Salvar senha em outro dispositivo?"</string> <string name="save_sign_in_on_other_device_title" msgid="2827990118560134692">"Salvar credenciais de login em outro dispositivo?"</string> @@ -57,9 +57,9 @@ <string name="set_as_default" msgid="4415328591568654603">"Definir como padrão"</string> <string name="settings" msgid="6536394145760913145">"Configurações"</string> <string name="use_once" msgid="9027366575315399714">"Usar uma vez"</string> - <string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> senhas • <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> chaves de acesso"</string> - <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> senhas"</string> - <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> chaves de acesso"</string> + <string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"Senhas (<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>) • Chaves de acesso (<xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g>)"</string> + <string name="more_options_usage_passwords" msgid="1632047277723187813">"Senhas (<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>)"</string> + <string name="more_options_usage_passkeys" msgid="5390320437243042237">"Chaves de acesso (<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g>)"</string> <string name="more_options_usage_credentials" msgid="1785697001787193984">"<xliff:g id="TOTALCREDENTIALSNUMBER">%1$s</xliff:g> credenciais"</string> <string name="passkey_before_subtitle" msgid="2448119456208647444">"Chave de acesso"</string> <string name="another_device" msgid="5147276802037801217">"Outro dispositivo"</string> diff --git a/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorActivity.kt b/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorActivity.kt index 652e62cb26b4..5728c8cb9263 100644 --- a/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorActivity.kt +++ b/packages/CredentialManager/wear/src/com/android/credentialmanager/CredentialSelectorActivity.kt @@ -18,6 +18,7 @@ package com.android.credentialmanager import android.content.Intent import android.os.Bundle +import android.util.Log import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.activity.viewModels @@ -33,6 +34,7 @@ class CredentialSelectorActivity : Hilt_CredentialSelectorActivity() { @OptIn(ExperimentalHorologistApi::class) override fun onCreate(savedInstanceState: Bundle?) { + Log.d(TAG, "onCreate, intent: $intent") super.onCreate(savedInstanceState) setTheme(android.R.style.Theme_DeviceDefault) setContent { @@ -47,6 +49,7 @@ class CredentialSelectorActivity : Hilt_CredentialSelectorActivity() { } override fun onNewIntent(intent: Intent) { + Log.d(TAG, "onNewIntent, intent: $intent") super.onNewIntent(intent) setIntent(intent) viewModel.updateRequest(intent) diff --git a/packages/CtsShim/apk/riscv64/CtsShim.apk b/packages/CtsShim/apk/riscv64/CtsShim.apk Binary files differindex af306a504d30..5ab190d19514 100644 --- a/packages/CtsShim/apk/riscv64/CtsShim.apk +++ b/packages/CtsShim/apk/riscv64/CtsShim.apk diff --git a/packages/CtsShim/apk/riscv64/CtsShimPriv.apk b/packages/CtsShim/apk/riscv64/CtsShimPriv.apk Binary files differindex 9a9997dc4155..441f86f04625 100644 --- a/packages/CtsShim/apk/riscv64/CtsShimPriv.apk +++ b/packages/CtsShim/apk/riscv64/CtsShimPriv.apk diff --git a/packages/EasterEgg/AndroidManifest.xml b/packages/EasterEgg/AndroidManifest.xml index 1500583a7677..754abb2f76be 100644 --- a/packages/EasterEgg/AndroidManifest.xml +++ b/packages/EasterEgg/AndroidManifest.xml @@ -33,7 +33,7 @@ <uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> <application - android:icon="@drawable/android14_patch_adaptive" + android:icon="@drawable/android15_patch_adaptive" android:label="@string/app_name"> <!-- Android V easter egg: Daydream version of Landroid @@ -41,7 +41,7 @@ <service android:name=".landroid.DreamUniverse" android:exported="true" - android:icon="@drawable/android14_patch_adaptive" + android:icon="@drawable/android15_patch_adaptive" android:label="@string/v_egg_name" android:description="@string/dream_description" android:enabled="false" @@ -62,7 +62,7 @@ android:name=".landroid.MainActivity" android:exported="true" android:label="@string/u_egg_name" - android:icon="@drawable/android14_patch_adaptive" + android:icon="@drawable/android15_patch_adaptive" android:configChanges="orientation|screenLayout|screenSize|density" android:theme="@android:style/Theme.DeviceDefault.NoActionBar.Fullscreen"> <intent-filter> diff --git a/packages/EasterEgg/res/drawable/android15_patch_adaptive.xml b/packages/EasterEgg/res/drawable/android15_patch_adaptive.xml new file mode 100644 index 000000000000..d9492000beeb --- /dev/null +++ b/packages/EasterEgg/res/drawable/android15_patch_adaptive.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> + <background android:drawable="@drawable/android15_patch_adaptive_background"/> + <foreground android:drawable="@drawable/android15_patch_adaptive_foreground"/> + <monochrome android:drawable="@drawable/android15_patch_monochrome"/> +</adaptive-icon> diff --git a/packages/EasterEgg/res/drawable/android15_patch_adaptive_background.xml b/packages/EasterEgg/res/drawable/android15_patch_adaptive_background.xml new file mode 100644 index 000000000000..642b30aedf4c --- /dev/null +++ b/packages/EasterEgg/res/drawable/android15_patch_adaptive_background.xml @@ -0,0 +1,75 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="108dp" + android:height="108dp" + android:viewportWidth="108" + android:viewportHeight="108"> + <!-- space --> + <path + android:pathData="M0,0h108v108h-108z" + android:fillColor="#202124"/> + <!-- stars --> + <group> + <path + android:pathData="M32,34 h1v1h-1z" + android:fillColor="#ffffff"/> + <path + android:pathData="M33,61 h1v1h-1z" + android:fillColor="#ffffff"/> + <path + android:pathData="M71,34 h1v1h-1z" + android:fillColor="#ffffff"/> + <path + android:pathData="M62,56 h1v1h-1z" + android:fillColor="#ffffff"/> + <path + android:pathData="M68,47 h1v1h-1z" + android:fillColor="#ffffff"/> + <path + android:pathData="M72,55 h1v1h-1z" + android:fillColor="#ffffff"/> + <path + android:pathData="M39,36 h1v1h-1z" + android:fillColor="#ffffff"/> + + <path + android:pathData="M72,49 h2v2h-2z" + android:fillColor="#ffffff"/> + <path + android:pathData="M46,53 h2v2h-2z" + android:fillColor="#ffffff"/> + <path + android:pathData="M32,45 h2v2h-2z" + android:fillColor="#ffffff"/> + <path + android:pathData="M43,37 h2v2h-2z" + android:fillColor="#ffffff"/> + <path + android:pathData="M78,51 h2v2h-2z" + android:fillColor="#ffffff"/> + <path + android:pathData="M34,51 h2v2h-2z" + android:fillColor="#ffffff"/> + <path + android:pathData="M76,41 h2v2h-2z" + android:fillColor="#ffffff"/> + <path + android:pathData="M39,46 h2v2h-2z" + android:fillColor="#ffffff"/> + </group> +</vector> diff --git a/packages/EasterEgg/res/drawable/android15_patch_adaptive_foreground.xml b/packages/EasterEgg/res/drawable/android15_patch_adaptive_foreground.xml new file mode 100644 index 000000000000..1100eb725780 --- /dev/null +++ b/packages/EasterEgg/res/drawable/android15_patch_adaptive_foreground.xml @@ -0,0 +1,60 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="108dp" + android:height="108dp" + android:viewportWidth="108" + android:viewportHeight="108"> + + <!-- zoomies --> + <group> + <path + android:pathData="M53,42C52.21,50.58 46.46,68.95 32.11,74.63C19.22,79.75 5.77,82.32 1.19,83.19C0.68,83.29 0.28,83.36 0,83.42V108H54H108V83.42C107.72,83.36 107.32,83.29 106.81,83.19C102.23,82.32 88.78,79.75 75.89,74.63C61.54,68.95 55.79,50.58 55,42H54H53Z" + android:fillColor="#C6FF00" + android:fillType="evenOdd"/> + <path + android:pathData="M53.25,42C52.88,50.53 50.44,69.01 43.68,74.67C36.91,80.33 32.65,82.86 31.37,83.41L54,102.87L76.63,83.41C75.35,82.86 71.09,80.33 64.32,74.67C57.56,69.01 55.12,50.53 54.75,42H54H53.25Z" + android:fillColor="#ffffff" + android:fillType="evenOdd"/> + <path + android:pathData="M54,42m-1,0a1,1 0,1 1,2 0a1,1 0,1 1,-2 0" + android:fillColor="#ffffff"/> + </group> + <group> + <!-- head! it's like sputnik --> + <path + android:pathData="M54,94.25m-26.25,0a26.25,26.25 0,1 1,52.5 0a26.25,26.25 0,1 1,-52.5 0" + android:fillColor="#34A853"/> + <!-- ant --> + <path + android:pathData="M38,63.5L44.5,74.5" + android:strokeWidth="5" + android:fillColor="#00000000" + android:strokeColor="#34A853" + android:strokeLineCap="round"/> + <path + android:pathData="M70,63.5L63.5,74.5" + android:strokeWidth="5" + android:fillColor="#00000000" + android:strokeColor="#34A853" + android:strokeLineCap="round"/> + </group> + <!-- spaceship --> + <path + android:pathData="M54,34C52.34,34 51,35.29 51,36.88V40.44C51,40.75 51.25,41 51.56,41C51.87,41 52.13,40.75 52.13,40.44V39.48C52.13,38.87 52.63,38.37 53.25,38.37H54.75C55.37,38.37 55.87,38.87 55.87,39.48V40.44C55.87,40.75 56.13,41 56.44,41C56.75,41 57,40.75 57,40.44V36.88C57,35.29 55.66,34 54,34H54Z" + android:fillColor="#E9F3EB"/> +</vector> diff --git a/packages/EasterEgg/res/drawable/android15_patch_monochrome.xml b/packages/EasterEgg/res/drawable/android15_patch_monochrome.xml new file mode 100644 index 000000000000..a91cc864e7cd --- /dev/null +++ b/packages/EasterEgg/res/drawable/android15_patch_monochrome.xml @@ -0,0 +1,119 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="108dp" + android:height="108dp" + android:viewportWidth="108" + android:viewportHeight="108"> + <group> + <path + android:pathData=" + M54,94.25 + m-26.25,0 + a26.25,26.25 0,1 1,52.5 0 + a26.25,26.25 0,1 1,-52.5 0 + " + android:fillColor="#000000"/> + <path + android:pathData="M38,63.5L44.5,74.5" + android:strokeWidth="5" + android:fillColor="#00000000" + android:strokeColor="#000000" + android:strokeLineCap="round"/> + <path + android:pathData="M70,63.5L63.5,74.5" + android:strokeWidth="5" + android:fillColor="#00000000" + android:strokeColor="#000000" + android:strokeLineCap="round"/> + + <path + android:pathData=" + M54,34 + C52.34,34 51,35.29 51,36.88 + V40.44 + C51,40.75 51.25,41 51.56,41 + C51.87,41 52.13,40.75 52.13,40.44 + V39.48 + C52.13,38.87 52.63,38.37 53.25,38.37 + H54.75 + C55.37,38.37 55.87,38.87 55.87,39.48 + V40.44 + C55.87,40.75 56.13,41 56.44,41 + C56.75,41 57,40.75 57,40.44 + V36.88 + C57,35.29 55.66,34 54,34 + H54 + Z + " + android:fillColor="#34A853"/> + <path + android:strokeWidth="1" + android:pathData="M54,40V67" + android:fillColor="#00000000" + android:strokeColor="#40FFFFFF" + /> + + <path + android:pathData="M32,34 h1v1h-1z" + android:fillColor="#ffffff"/> + <path + android:pathData="M33,61 h1v1h-1z" + android:fillColor="#ffffff"/> + <path + android:pathData="M71,34 h1v1h-1z" + android:fillColor="#ffffff"/> + <path + android:pathData="M62,56 h1v1h-1z" + android:fillColor="#ffffff"/> + <path + android:pathData="M68,47 h1v1h-1z" + android:fillColor="#ffffff"/> + <path + android:pathData="M72,55 h1v1h-1z" + android:fillColor="#ffffff"/> + <path + android:pathData="M39,36 h1v1h-1z" + android:fillColor="#ffffff"/> + + <path + android:pathData="M72,49 h2v2h-2z" + android:fillColor="#ffffff"/> + <path + android:pathData="M46,53 h2v2h-2z" + android:fillColor="#ffffff"/> + <path + android:pathData="M32,45 h2v2h-2z" + android:fillColor="#ffffff"/> + <path + android:pathData="M43,37 h2v2h-2z" + android:fillColor="#ffffff"/> + <path + android:pathData="M78,51 h2v2h-2z" + android:fillColor="#ffffff"/> + <path + android:pathData="M34,51 h2v2h-2z" + android:fillColor="#ffffff"/> + <path + android:pathData="M76,41 h2v2h-2z" + android:fillColor="#ffffff"/> + <path + android:pathData="M39,46 h2v2h-2z" + android:fillColor="#ffffff"/> + + </group> +</vector> diff --git a/packages/PackageInstaller/res/values-pa/strings.xml b/packages/PackageInstaller/res/values-pa/strings.xml index 1db92a00683e..80dc8897e3c3 100644 --- a/packages/PackageInstaller/res/values-pa/strings.xml +++ b/packages/PackageInstaller/res/values-pa/strings.xml @@ -63,7 +63,7 @@ <string name="archive_application_text_all_users" msgid="3151229641681672580">"ਕੀ ਇਸ ਐਪ ਨੂੰ ਸਾਰੇ ਵਰਤੋਂਕਾਰਾਂ ਲਈ ਪੁਰਾਲੇਖਬੱਧ ਕਰਨਾ ਹੈ? ਤੁਹਾਡਾ ਨਿੱਜੀ ਡਾਟਾ ਰੱਖਿਅਤ ਕੀਤਾ ਜਾਵੇਗਾ"</string> <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"ਕੀ ਇਸ ਐਪ ਨੂੰ ਤੁਹਾਡੇ ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ \'ਤੇ ਪੁਰਾਲੇਖਬੱਧ ਕਰਨਾ ਹੈ? ਤੁਹਾਡਾ ਨਿੱਜੀ ਡਾਟਾ ਰੱਖਿਅਤ ਕੀਤਾ ਜਾਵੇਗਾ"</string> <string name="archive_application_text_user" msgid="2586558895535581451">"ਕੀ ਇਸ ਐਪ ਨੂੰ <xliff:g id="USERNAME">%1$s</xliff:g> ਲਈ ਪੁਰਾਲੇਖਬੱਧ ਕਰਨਾ ਹੈ? ਤੁਹਾਡਾ ਨਿੱਜੀ ਡਾਟਾ ਰੱਖਿਅਤ ਕੀਤਾ ਜਾਵੇਗਾ"</string> - <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"ਕੀ ਤੁਸੀਂ ਇਸ ਐਪ ਨੂੰ ਆਪਣੀ ਨਿੱਜੀ ਸਪੇਸ ਤੋਂ ਪੁਰਾਲੇਖਬੱਧ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ? ਤੁਹਾਡਾ ਨਿੱਜੀ ਡਾਟਾ ਰੱਖਿਅਤ ਕੀਤਾ ਜਾਵੇਗਾ"</string> + <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"ਕੀ ਤੁਸੀਂ ਇਸ ਐਪ ਨੂੰ ਆਪਣੀ ਪ੍ਰਾਈਵੇਟ ਸਪੇਸ ਤੋਂ ਪੁਰਾਲੇਖਬੱਧ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ? ਤੁਹਾਡਾ ਨਿੱਜੀ ਡਾਟਾ ਰੱਖਿਅਤ ਕੀਤਾ ਜਾਵੇਗਾ"</string> <string name="uninstall_application_text_all_users" msgid="575491774380227119">"ਕੀ ਤੁਸੀਂ ਇਸ ਐਪ ਨੂੰ "<b>"ਸਾਰੇ"</b>" ਵਰਤੋਂਕਾਰਾਂ ਲਈ ਅਣਸਥਾਪਤ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ? ਐਪਲੀਕੇਸ਼ਨ ਅਤੇ ਇਸਦਾ ਡਾਟਾ ਡੀਵਾਈਸ \'ਤੇ "<b>"ਸਾਰੇ"</b>" ਵਰਤੋਂਕਾਰਾਂ ਵੱਲੋਂ ਹਟਾ ਦਿੱਤਾ ਜਾਵੇਗਾ।"</string> <string name="uninstall_application_text_user" msgid="498072714173920526">"ਕੀ ਤੁਸੀਂ ਵਰਤੋਂਕਾਰ <xliff:g id="USERNAME">%1$s</xliff:g> ਲਈ ਇਸ ਐਪ ਨੂੰ ਅਣਸਥਾਪਤ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ?"</string> <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"ਕੀ ਤੁਸੀਂ ਇਸ ਐਪ ਨੂੰ ਆਪਣੇ ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ ਤੋਂ ਅਣਸਥਾਪਤ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ?"</string> diff --git a/packages/PackageInstaller/res/values-tr/strings.xml b/packages/PackageInstaller/res/values-tr/strings.xml index 006ad52297de..e10eb0e52fcd 100644 --- a/packages/PackageInstaller/res/values-tr/strings.xml +++ b/packages/PackageInstaller/res/values-tr/strings.xml @@ -55,7 +55,7 @@ <string name="user_is_not_allowed_dlg_text" msgid="3468447791330611681">"Geçerli kullanıcının bu yüklemeyi kaldırma izni yok."</string> <string name="generic_error_dlg_title" msgid="5863195085927067752">"Hata"</string> <string name="generic_error_dlg_text" msgid="5287861443265795232">"Uygulamanın yüklemesi kaldırılamadı."</string> - <string name="uninstall_application_title" msgid="4045420072401428123">"Uygulamanın yüklemesini kaldır"</string> + <string name="uninstall_application_title" msgid="4045420072401428123">"Uygulamayı kaldır"</string> <string name="uninstall_update_title" msgid="824411791011583031">"Güncelleme kaldırılsın mı?"</string> <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g>, şu uygulamanın bir parçasıdır:"</string> <string name="uninstall_application_text" msgid="3816830743706143980">"Bu uygulamanın yüklemesini kaldırmak istiyor musunuz?"</string> diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallFailed.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallFailed.java index c96644ca8920..03d52d0868d1 100644 --- a/packages/PackageInstaller/src/com/android/packageinstaller/InstallFailed.java +++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallFailed.java @@ -28,6 +28,7 @@ import android.content.pm.PackageManager; import android.os.Bundle; import android.util.Log; import android.view.View; + import androidx.annotation.Nullable; /** @@ -84,22 +85,28 @@ public class InstallFailed extends Activity { int statusCode = intent.getIntExtra(PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_FAILURE); boolean returnResult = intent.getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false); - - if (returnResult) { - int legacyStatus = intent.getIntExtra(PackageInstaller.EXTRA_LEGACY_STATUS, + int legacyStatus = intent.getIntExtra(PackageInstaller.EXTRA_LEGACY_STATUS, PackageManager.INSTALL_FAILED_INTERNAL_ERROR); + // TODO (b/346655018): Use INSTALL_FAILED_ABORTED legacyCode in the condition + // statusCode can be STATUS_FAILURE_ABORTED if: + // 1. GPP blocks an install. + // 2. User denies ownership update explicitly. + // InstallFailed dialog must not be shown only when the user denies ownership update. We + // must show this dialog for all other install failures. + boolean userDenied = statusCode == PackageInstaller.STATUS_FAILURE_ABORTED + && legacyStatus != PackageManager.INSTALL_FAILED_VERIFICATION_TIMEOUT + && legacyStatus != PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE; + + if (returnResult) { // Return result if requested Intent result = new Intent(); result.putExtra(Intent.EXTRA_INSTALL_RESULT, legacyStatus); setResult(Activity.RESULT_FIRST_USER, result); finish(); - } else if (statusCode != PackageInstaller.STATUS_FAILURE_ABORTED) { - // statusCode will be STATUS_FAILURE_ABORTED if the update-owner confirmation dialog was - // dismissed by the user. We don't want to show a InstallFailed dialog in this case. - // If the user denies install permission for normal installs, this dialog will never be - // triggered as the status code is returned from PackageInstallerActivity.java - + } else if (userDenied) { + finish(); + } else { // Set header icon and title PackageUtil.AppSnippet as = intent.getParcelableExtra( PackageInstallerActivity.EXTRA_APP_SNIPPET, PackageUtil.AppSnippet.class); @@ -127,8 +134,6 @@ public class InstallFailed extends Activity { // Get status messages setExplanationFromErrorCode(statusCode); - } else { - finish(); } } diff --git a/packages/PrintSpooler/res/values-as/strings.xml b/packages/PrintSpooler/res/values-as/strings.xml index 020eac770134..950c5c8f585c 100644 --- a/packages/PrintSpooler/res/values-as/strings.xml +++ b/packages/PrintSpooler/res/values-as/strings.xml @@ -100,7 +100,7 @@ </string-array> <string-array name="orientation_labels"> <item msgid="4061931020926489228">"প\'ৰ্ট্ৰেইট"</item> - <item msgid="3199660090246166812">"লেণ্ডস্কেইপ"</item> + <item msgid="3199660090246166812">"লেণ্ডস্কে’প"</item> </string-array> <string name="print_write_error_message" msgid="5787642615179572543">"ফাইলত লিখিব পৰা নহ\'ল"</string> <string name="print_error_default_message" msgid="8602678405502922346">"দুঃখিত, প্ৰিণ্টিঙৰ কাম নহ\'ল। পুনৰ চেষ্টা কৰক।"</string> diff --git a/packages/SettingsLib/ProfileSelector/res/values-pt-rBR/strings.xml b/packages/SettingsLib/ProfileSelector/res/values-pt-rBR/strings.xml index eb33d573c495..75e1a186d028 100644 --- a/packages/SettingsLib/ProfileSelector/res/values-pt-rBR/strings.xml +++ b/packages/SettingsLib/ProfileSelector/res/values-pt-rBR/strings.xml @@ -19,5 +19,5 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="settingslib_category_personal" msgid="1142302328104700620">"Pessoal"</string> <string name="settingslib_category_work" msgid="4867750733682444676">"Trabalho"</string> - <string name="settingslib_category_private" msgid="5039276873477591386">"Particular"</string> + <string name="settingslib_category_private" msgid="5039276873477591386">"Privado"</string> </resources> diff --git a/packages/SettingsLib/ProfileSelector/res/values-pt/strings.xml b/packages/SettingsLib/ProfileSelector/res/values-pt/strings.xml index eb33d573c495..75e1a186d028 100644 --- a/packages/SettingsLib/ProfileSelector/res/values-pt/strings.xml +++ b/packages/SettingsLib/ProfileSelector/res/values-pt/strings.xml @@ -19,5 +19,5 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="settingslib_category_personal" msgid="1142302328104700620">"Pessoal"</string> <string name="settingslib_category_work" msgid="4867750733682444676">"Trabalho"</string> - <string name="settingslib_category_private" msgid="5039276873477591386">"Particular"</string> + <string name="settingslib_category_private" msgid="5039276873477591386">"Privado"</string> </resources> diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml index f0fb4df441b6..caeee064bf99 100644 --- a/packages/SettingsLib/res/values-de/strings.xml +++ b/packages/SettingsLib/res/values-de/strings.xml @@ -710,7 +710,7 @@ <string name="keyboard_layout_default_label" msgid="1997292217218546957">"Standard"</string> <string name="turn_screen_on_title" msgid="2662312432042116026">"Steuerelement zum Aktivieren des Displays"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"Aktivieren des Displays erlauben"</string> - <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Einer App erlauben, das Display zu aktivieren. Wenn du diese Erlaubnis erteilst, kann die App jederzeit das Display aktivieren – auch ohne deine explizite Absicht."</string> + <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Einer App erlauben, das Display zu aktivieren. Wenn du diese Erlaubnis erteilst, kann die App jederzeit das Display aktivieren – auch ohne dass du dies beabsichtigst."</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="5392738488989777074">"<xliff:g id="APP_NAME">%1$s</xliff:g> nicht mehr streamen?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="268234802198852753">"Wenn du <xliff:g id="SWITCHAPP">%1$s</xliff:g> streamst oder die Ausgabe änderst, wird dein aktueller Stream beendet"</string> <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="5749813313369517812">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> streamen"</string> diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml index 13819033d166..a25e179ee452 100644 --- a/packages/SettingsLib/res/values-kn/strings.xml +++ b/packages/SettingsLib/res/values-kn/strings.xml @@ -632,7 +632,7 @@ <string name="profile_info_settings_title" msgid="105699672534365099">"ಪ್ರೊಫೈಲ್ ಮಾಹಿತಿ"</string> <string name="user_need_lock_message" msgid="4311424336209509301">"ನೀವು ನಿರ್ಬಂಧಿತ ಪ್ರೊಫೈಲ್ ಅನ್ನು ರಚಿಸಬಹುದಾದರ ಮೊದಲು, ನಿಮ್ಮ ಅಪ್ಲಿಕೇಶನ್ಗಳು ಮತ್ತು ವೈಯಕ್ತಿಕ ಡೇಟಾವನ್ನು ರಕ್ಷಿಸಲು ನೀವು ಪರದೆಯ ಲಾಕ್ ಹೊಂದಿಸುವ ಅಗತ್ಯವಿದೆ."</string> <string name="user_set_lock_button" msgid="1427128184982594856">"ಲಾಕ್ ಹೊಂದಿಸಿ"</string> - <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g> ಗೆ ಬದಲಿಸಿ"</string> + <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g>ಗೆ ಬದಲಿಸಿ"</string> <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"ಹೊಸ ಬಳಕೆದಾರರನ್ನು ರಚಿಸಲಾಗುತ್ತಿದೆ…"</string> <string name="creating_new_guest_dialog_message" msgid="1114905602181350690">"ಹೊಸ ಅತಿಥಿಯನ್ನು ರಚಿಸಲಾಗುತ್ತಿದೆ…"</string> <string name="add_user_failed" msgid="4809887794313944872">"ಹೊಸ ಬಳಕೆದಾರರನ್ನು ರಚಿಸಲು ವಿಫಲವಾಗಿದೆ"</string> diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml index 5032e358f213..1e33f88879d1 100644 --- a/packages/SettingsLib/res/values-ne/strings.xml +++ b/packages/SettingsLib/res/values-ne/strings.xml @@ -641,7 +641,7 @@ <string name="edit_user_info_message" msgid="6677556031419002895">"यो डिभाइस प्रयोग गर्ने सबै जना तपाईंले छनौट गर्ने नाम र फोटो देख्न सक्ने छन्।"</string> <string name="user_add_user" msgid="7876449291500212468">"प्रयोगकर्ता कनेक्ट गर्नुहोस्"</string> <string name="guest_new_guest" msgid="3482026122932643557">"अतिथि कनेक्ट गर्नुहोस्"</string> - <string name="guest_exit_guest" msgid="5908239569510734136">"गेस्ट मोडबाट बाहिर निस्कियोस्"</string> + <string name="guest_exit_guest" msgid="5908239569510734136">"अथिति हटाउनुहोस्"</string> <string name="guest_reset_guest" msgid="6110013010356013758">"अतिथि सत्र रिसेट गर्नुहोस्"</string> <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"अतिथिका रूपमा ब्राउज गर्ने सेसन रिसेट गर्ने हो?"</string> <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"यी अतिथि हटाउने हो?"</string> diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml index d47a1f4d3b5a..337b011cc359 100644 --- a/packages/SettingsLib/res/values-pt-rBR/strings.xml +++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml @@ -237,7 +237,7 @@ <string name="choose_profile" msgid="343803890897657450">"Escolher perfil"</string> <string name="category_personal" msgid="6236798763159385225">"Pessoal"</string> <string name="category_work" msgid="4014193632325996115">"Trabalho"</string> - <string name="category_private" msgid="4244892185452788977">"Particular"</string> + <string name="category_private" msgid="4244892185452788977">"Privado"</string> <string name="category_clone" msgid="1554511758987195974">"Clone"</string> <string name="development_settings_title" msgid="140296922921597393">"Opções do desenvolvedor"</string> <string name="development_settings_enable" msgid="4285094651288242183">"Ativar opções do desenvolvedor"</string> diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml index d47a1f4d3b5a..337b011cc359 100644 --- a/packages/SettingsLib/res/values-pt/strings.xml +++ b/packages/SettingsLib/res/values-pt/strings.xml @@ -237,7 +237,7 @@ <string name="choose_profile" msgid="343803890897657450">"Escolher perfil"</string> <string name="category_personal" msgid="6236798763159385225">"Pessoal"</string> <string name="category_work" msgid="4014193632325996115">"Trabalho"</string> - <string name="category_private" msgid="4244892185452788977">"Particular"</string> + <string name="category_private" msgid="4244892185452788977">"Privado"</string> <string name="category_clone" msgid="1554511758987195974">"Clone"</string> <string name="development_settings_title" msgid="140296922921597393">"Opções do desenvolvedor"</string> <string name="development_settings_enable" msgid="4285094651288242183">"Ativar opções do desenvolvedor"</string> diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml index ff62ba0d29a4..e8acd6ec391d 100644 --- a/packages/SettingsLib/res/values-ta/strings.xml +++ b/packages/SettingsLib/res/values-ta/strings.xml @@ -161,7 +161,7 @@ <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"ரத்துசெய்"</string> <string name="bluetooth_pairing_will_share_phonebook" msgid="3064334458659165176">"இணைத்தலானது உங்கள் தொடர்புகள், அழைப்பு வரலாறுக்கான அணுகலை வழங்குகிறது."</string> <string name="bluetooth_pairing_error_message" msgid="6626399020672335565">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> உடன் இணைய முடியவில்லை."</string> - <string name="bluetooth_pairing_pin_error_message" msgid="264422127613704940">"தவறான பின் அல்லது கடவுச்சொல் காரணமாக <xliff:g id="DEVICE_NAME">%1$s</xliff:g> உடன் இணைக்க முடியவில்லை."</string> + <string name="bluetooth_pairing_pin_error_message" msgid="264422127613704940">"தவறான பின் அல்லது கடவுச்சாவி காரணமாக <xliff:g id="DEVICE_NAME">%1$s</xliff:g> உடன் இணைக்க முடியவில்லை."</string> <string name="bluetooth_pairing_device_down_error_message" msgid="2554424863101358857">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> உடன் இணைக்க முடியவில்லை."</string> <string name="bluetooth_pairing_rejected_error_message" msgid="5943444352777314442">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> இணைப்பதை நிராகரித்தது."</string> <string name="bluetooth_talkback_computer" msgid="3736623135703893773">"கணினி"</string> diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml index 6aea659292c3..e8bac312b4dc 100644 --- a/packages/SettingsLib/res/values-tr/strings.xml +++ b/packages/SettingsLib/res/values-tr/strings.xml @@ -492,9 +492,9 @@ <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - Tamamen şarj olmasına <xliff:g id="TIME">%2$s</xliff:g> kaldı"</string> <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Şarj işlemi optimize edildi"</string> <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> ‑ Şarj ediliyor"</string> - <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - <xliff:g id="TIME">%3$s</xliff:g> içinde tamamen dolacak"</string> - <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> içinde tamamen şarj olacak"</string> - <string name="power_remaining_charging_duration_only_v2" msgid="5358176435722950193">"<xliff:g id="TIME">%1$s</xliff:g> içinde tamamen şarj olacak"</string> + <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - Tamamen dolacağı zaman: <xliff:g id="TIME">%3$s</xliff:g>"</string> + <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - Tamamen şarj olacağı zaman: <xliff:g id="TIME">%2$s</xliff:g>"</string> + <string name="power_remaining_charging_duration_only_v2" msgid="5358176435722950193">"Tamamen şarj olacağı zaman: <xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="power_remaining_fast_charging_duration_only_v2" msgid="6270950195810579563">"<xliff:g id="TIME">%1$s</xliff:g> itibarıyla tamamen dolacak"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Bilinmiyor"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Şarj oluyor"</string> diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java index 5245456d98c2..00fb7a1feab1 100644 --- a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java +++ b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java @@ -80,6 +80,7 @@ public class SystemSettings { Settings.System.SIP_RECEIVE_CALLS, Settings.System.POINTER_SPEED, Settings.System.POINTER_FILL_STYLE, + Settings.System.POINTER_SCALE, Settings.System.VIBRATE_ON, Settings.System.VIBRATE_WHEN_RINGING, Settings.System.RINGTONE, diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java index 2c3be4c10f5a..4235bc4157bf 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java @@ -26,6 +26,8 @@ import static android.provider.settings.validators.SettingsValidators.NON_NEGATI import static android.provider.settings.validators.SettingsValidators.NON_NEGATIVE_INTEGER_VALIDATOR; import static android.provider.settings.validators.SettingsValidators.URI_VALIDATOR; import static android.provider.settings.validators.SettingsValidators.VIBRATION_INTENSITY_VALIDATOR; +import static android.view.PointerIcon.DEFAULT_POINTER_SCALE; +import static android.view.PointerIcon.LARGE_POINTER_SCALE; import static android.view.PointerIcon.POINTER_ICON_VECTOR_STYLE_FILL_BEGIN; import static android.view.PointerIcon.POINTER_ICON_VECTOR_STYLE_FILL_END; @@ -211,6 +213,8 @@ public class SystemSettingsValidators { VALIDATORS.put(System.POINTER_FILL_STYLE, new InclusiveIntegerRangeValidator(POINTER_ICON_VECTOR_STYLE_FILL_BEGIN, POINTER_ICON_VECTOR_STYLE_FILL_END)); + VALIDATORS.put(System.POINTER_SCALE, + new InclusiveFloatRangeValidator(DEFAULT_POINTER_SCALE, LARGE_POINTER_SCALE)); VALIDATORS.put(System.TOUCHPAD_POINTER_SPEED, new InclusiveIntegerRangeValidator(-7, 7)); VALIDATORS.put(System.TOUCHPAD_NATURAL_SCROLLING, BOOLEAN_VALIDATOR); VALIDATORS.put(System.TOUCHPAD_TAP_TO_CLICK, BOOLEAN_VALIDATOR); diff --git a/packages/SettingsProvider/src/com/android/providers/settings/NonWritableNamespacesForBackgroundUserPrefixes.java b/packages/SettingsProvider/src/com/android/providers/settings/NonWritableNamespacesForBackgroundUserPrefixes.java new file mode 100644 index 000000000000..5c5ca46111ab --- /dev/null +++ b/packages/SettingsProvider/src/com/android/providers/settings/NonWritableNamespacesForBackgroundUserPrefixes.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.providers.settings; + +import android.util.ArraySet; + +import java.util.Arrays; +import java.util.Set; + +/** + * Contains the list of prefixes for namespaces in which nothing can be written by background + * user. + * + * <p> + * The list in enforced is Auto devices only. To add to + * the list, create a change and tag the OWNER. In the change description, include a + * description of the flag's functionality, and a justification for why it needs to be + * denylisted. + */ +final class NonWritableNamespacesForBackgroundUserPrefixes { + public static final Set<String> DENYLIST = + new ArraySet<String>(Arrays.asList( + "game_overlay" + )); +} diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java index 625b8e4e2911..384cb7ee9c49 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java @@ -2915,6 +2915,9 @@ class SettingsProtoDumpUtil { dumpSetting(s, p, Settings.System.POINTER_FILL_STYLE, SystemSettingsProto.Pointer.POINTER_FILL_STYLE); + dumpSetting(s, p, + Settings.System.POINTER_SCALE, + SystemSettingsProto.Pointer.POINTER_SCALE); p.end(pointerToken); dumpSetting(s, p, Settings.System.POINTER_SPEED, diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java index 15f8a7b92878..d54236e60dd7 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java @@ -2392,8 +2392,12 @@ public class SettingsProvider extends ContentProvider { == PackageManager.PERMISSION_GRANTED; boolean isRoot = Binder.getCallingUid() == Process.ROOT_UID; - if (isRoot || hasWritePermission) { + if (isRoot) { return; + } + + if (hasWritePermission) { + assertCallingUserDenyList(flags); } else if (hasAllowlistPermission) { for (String flag : flags) { boolean namespaceAllowed = false; @@ -2410,12 +2414,49 @@ public class SettingsProvider extends ContentProvider { + "'; allowlist permission granted, but must add flag to the allowlist."); } } + assertCallingUserDenyList(flags); } else { throw new SecurityException("Permission denial to mutate flag, must have root, " + "WRITE_DEVICE_CONFIG, or WRITE_ALLOWLISTED_DEVICE_CONFIG"); } } + // The check is added mainly for auto devices. On auto devices, it is possible that + // multiple users are visible simultaneously using visible background users. + // In such cases, it is desired that Non-current user (ex. visible background users) can + // only change settings for certain namespaces. + private void assertCallingUserDenyList(@NonNull Set<String> flags) { + if (!UserManager.isVisibleBackgroundUsersEnabled()) { + // enforce the deny list only on devices supporting visible background user. + return; + } + + int callingUser = UserHandle.getCallingUserId(); + final long identity = Binder.clearCallingIdentity(); + try { + int currentUser = ActivityManager.getCurrentUser(); + if (callingUser == currentUser) { + // enforce the deny list only if the caller is not current user. Currently only auto + // uses background visible user, and auto doesn't support profiles so profiles of + // current users is not checked here. + return; + } + } finally { + Binder.restoreCallingIdentity(identity); + } + + for (String flag : flags) { + for (String denylistedPrefix : + NonWritableNamespacesForBackgroundUserPrefixes.DENYLIST) { + if (flag.startsWith(denylistedPrefix)) { + throw new SecurityException("Permission denial for flag '" + flag + + "' for background user " + callingUser + ". Namespace is added to " + + "denylist."); + } + } + } + } + private static void warnOrThrowForUndesiredSecureSettingsMutationForTargetSdk( int targetSdkVersion, String name) { // If the app targets Lollipop MR1 or older SDK we warn, otherwise crash. diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index bd6efe5242b2..8a99263637af 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -387,7 +387,7 @@ android:killAfterRestore="false" android:hardwareAccelerated="true" android:label="@string/app_label" - android:icon="@drawable/android14_patch_adaptive" + android:icon="@drawable/android15_patch_adaptive" android:process="com.android.systemui" android:supportsRtl="true" android:theme="@style/Theme.SystemUI" diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/values-pt-rBR/strings.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/values-pt-rBR/strings.xml index 160d310f5f7d..f12278a15a8b 100644 --- a/packages/SystemUI/accessibility/accessibilitymenu/res/values-pt-rBR/strings.xml +++ b/packages/SystemUI/accessibility/accessibilitymenu/res/values-pt-rBR/strings.xml @@ -2,7 +2,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="accessibility_menu_service_name" msgid="730136711554740131">"Menu de acessibilidade"</string> - <string name="accessibility_menu_intro" msgid="3164193281544042394">"\"Acessibilidade\" é um menu grande mostrado na tela para controlar seu dispositivo. Você pode bloquear o dispositivo, controlar o volume e o brilho, fazer capturas de tela e muito mais."</string> + <string name="accessibility_menu_intro" msgid="3164193281544042394">"Com este menu de tamanho grande na tela, você consegue bloquear o dispositivo, ajustar o volume e o brilho, fazer capturas de tela, entre outras funções de controle do aparelho."</string> <string name="assistant_label" msgid="6796392082252272356">"Assistente"</string> <string name="assistant_utterance" msgid="65509599221141377">"Google Assistente"</string> <string name="a11y_settings_label" msgid="3977714687248445050">"Configurações de acessibilidade"</string> diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/values-pt/strings.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/values-pt/strings.xml index 160d310f5f7d..f12278a15a8b 100644 --- a/packages/SystemUI/accessibility/accessibilitymenu/res/values-pt/strings.xml +++ b/packages/SystemUI/accessibility/accessibilitymenu/res/values-pt/strings.xml @@ -2,7 +2,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="accessibility_menu_service_name" msgid="730136711554740131">"Menu de acessibilidade"</string> - <string name="accessibility_menu_intro" msgid="3164193281544042394">"\"Acessibilidade\" é um menu grande mostrado na tela para controlar seu dispositivo. Você pode bloquear o dispositivo, controlar o volume e o brilho, fazer capturas de tela e muito mais."</string> + <string name="accessibility_menu_intro" msgid="3164193281544042394">"Com este menu de tamanho grande na tela, você consegue bloquear o dispositivo, ajustar o volume e o brilho, fazer capturas de tela, entre outras funções de controle do aparelho."</string> <string name="assistant_label" msgid="6796392082252272356">"Assistente"</string> <string name="assistant_utterance" msgid="65509599221141377">"Google Assistente"</string> <string name="a11y_settings_label" msgid="3977714687248445050">"Configurações de acessibilidade"</string> diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig index 85aa33aef26e..c61f996ae7cf 100644 --- a/packages/SystemUI/aconfig/systemui.aconfig +++ b/packages/SystemUI/aconfig/systemui.aconfig @@ -1076,6 +1076,16 @@ flag { } flag { + name: "enable_efficient_display_repository" + namespace: "systemui" + description: "Decide whether to use the new implementation of DisplayRepository that minimizes binder calls and background lock contention." + bug: "345472038" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { name: "notification_media_manager_background_execution" namespace: "systemui" description: "Decide whether to execute binder calls in background thread" diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt index a1f8f1b32f77..c4659cfc146b 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt @@ -55,6 +55,7 @@ import com.android.systemui.Flags.glanceableHubFullscreenSwipe import com.android.systemui.communal.shared.model.CommunalBackgroundType import com.android.systemui.communal.shared.model.CommunalScenes import com.android.systemui.communal.shared.model.CommunalTransitionKeys +import com.android.systemui.communal.ui.compose.Dimensions.SlideOffsetY import com.android.systemui.communal.ui.compose.extensions.allowGestures import com.android.systemui.communal.ui.viewmodel.CommunalViewModel import com.android.systemui.communal.util.CommunalColors @@ -68,6 +69,7 @@ object Communal { val Grid = ElementKey("CommunalContent") val LockIcon = ElementKey("CommunalLockIcon") val IndicationArea = ElementKey("CommunalIndicationArea") + val StatusBar = ElementKey("StatusBar") } } @@ -75,6 +77,15 @@ object AllElements : ElementMatcher { override fun matches(key: ElementKey, scene: SceneKey) = true } +private object TransitionDuration { + const val BETWEEN_HUB_AND_EDIT_MODE_MS = 1000 + const val EDIT_MODE_TO_HUB_CONTENT_MS = 167 + const val EDIT_MODE_TO_HUB_GRID_DELAY_MS = 167 + const val EDIT_MODE_TO_HUB_GRID_END_MS = + EDIT_MODE_TO_HUB_GRID_DELAY_MS + EDIT_MODE_TO_HUB_CONTENT_MS + const val HUB_TO_EDIT_MODE_CONTENT_MS = 250 +} + val sceneTransitions = transitions { to(CommunalScenes.Communal, key = CommunalTransitionKeys.SimpleFade) { spec = tween(durationMillis = 250) @@ -92,9 +103,34 @@ val sceneTransitions = transitions { fade(Communal.Elements.Grid) fade(Communal.Elements.IndicationArea) fade(Communal.Elements.LockIcon) + fade(Communal.Elements.StatusBar) } timestampRange(startMillis = 167, endMillis = 334) { fade(Communal.Elements.Scrim) } } + to(CommunalScenes.Blank, key = CommunalTransitionKeys.ToEditMode) { + spec = tween(durationMillis = TransitionDuration.BETWEEN_HUB_AND_EDIT_MODE_MS) + timestampRange(endMillis = TransitionDuration.HUB_TO_EDIT_MODE_CONTENT_MS) { + fade(Communal.Elements.Grid) + fade(Communal.Elements.IndicationArea) + fade(Communal.Elements.LockIcon) + } + fade(Communal.Elements.Scrim) + } + to(CommunalScenes.Communal, key = CommunalTransitionKeys.FromEditMode) { + spec = tween(durationMillis = TransitionDuration.BETWEEN_HUB_AND_EDIT_MODE_MS) + translate(Communal.Elements.Grid, y = SlideOffsetY) + timestampRange(endMillis = TransitionDuration.EDIT_MODE_TO_HUB_CONTENT_MS) { + fade(Communal.Elements.IndicationArea) + fade(Communal.Elements.LockIcon) + fade(Communal.Elements.Scrim) + } + timestampRange( + startMillis = TransitionDuration.EDIT_MODE_TO_HUB_GRID_DELAY_MS, + endMillis = TransitionDuration.EDIT_MODE_TO_HUB_GRID_END_MS + ) { + fade(Communal.Elements.Grid) + } + } } /** @@ -252,7 +288,10 @@ private fun BoxScope.AnimatedLinearGradient() { Box( Modifier.matchParentSize() .background(colors.primary) - .animatedRadialGradientBackground(colors.primary, colors.primaryContainer) + .animatedRadialGradientBackground( + toColor = colors.primary, + fromColor = colors.primaryContainer.copy(alpha = 0.6f) + ) ) BackgroundTopScrim() } 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 60b6f62dfa46..b353b5adec4e 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 @@ -16,13 +16,16 @@ package com.android.systemui.communal.ui.compose +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.layout.Layout import androidx.compose.ui.unit.IntRect import com.android.compose.animation.scene.SceneScope import com.android.compose.theme.LocalAndroidColorScheme +import com.android.systemui.communal.ui.compose.section.AmbientStatusBarSection import com.android.systemui.communal.ui.viewmodel.CommunalViewModel import com.android.systemui.communal.widgets.WidgetInteractionHandler import com.android.systemui.keyguard.ui.composable.blueprint.BlueprintAlignmentLines @@ -38,19 +41,24 @@ constructor( private val interactionHandler: WidgetInteractionHandler, private val dialogFactory: SystemUIDialogFactory, private val lockSection: LockSection, + private val ambientStatusBarSection: AmbientStatusBarSection, ) { - @Composable fun SceneScope.Content(modifier: Modifier = Modifier) { Layout( modifier = modifier.fillMaxSize(), content = { - CommunalHub( - viewModel = viewModel, - interactionHandler = interactionHandler, - dialogFactory = dialogFactory, - modifier = Modifier.element(Communal.Elements.Grid) - ) + Box(modifier = Modifier.fillMaxSize()) { + with(ambientStatusBarSection) { + AmbientStatusBar(modifier = Modifier.fillMaxWidth()) + } + CommunalHub( + viewModel = viewModel, + interactionHandler = interactionHandler, + dialogFactory = dialogFactory, + modifier = Modifier.element(Communal.Elements.Grid) + ) + } with(lockSection) { LockIcon( overrideColor = LocalAndroidColorScheme.current.onPrimaryContainer, 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 eccb0724a8d4..a1899fd8dc2e 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 @@ -26,7 +26,6 @@ import android.widget.RemoteViews import androidx.annotation.VisibleForTesting import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.AnimatedVisibilityScope -import androidx.compose.animation.ExperimentalAnimationApi import androidx.compose.animation.core.LinearEasing import androidx.compose.animation.core.Spring import androidx.compose.animation.core.animateFloatAsState @@ -34,6 +33,8 @@ import androidx.compose.animation.core.spring import androidx.compose.animation.core.tween import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut +import androidx.compose.animation.slideInVertically +import androidx.compose.animation.slideOutVertically import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.Image @@ -46,6 +47,7 @@ import androidx.compose.foundation.layout.BoxScope import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth @@ -128,6 +130,7 @@ import androidx.compose.ui.viewinterop.AndroidView import androidx.compose.ui.window.Popup import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.window.layout.WindowMetricsCalculator +import com.android.compose.animation.Easings.Emphasized import com.android.compose.modifiers.thenIf import com.android.compose.theme.LocalAndroidColorScheme import com.android.compose.ui.graphics.painter.rememberDrawablePainter @@ -176,6 +179,10 @@ fun CommunalHub( derivedStateOf { selectedKey.value != null || reorderingWidgets } } val isEmptyState by viewModel.isEmptyState.collectAsStateWithLifecycle(initialValue = false) + val isCommunalContentVisible by + viewModel.isCommunalContentVisible.collectAsStateWithLifecycle( + initialValue = !viewModel.isEditMode + ) val contentPadding = gridContentPadding(viewModel.isEditMode, toolbarSize) val contentOffset = beforeContentPadding(contentPadding).toOffset() @@ -248,47 +255,88 @@ fun CommunalHub( viewModel = viewModel, ) } else { - CommunalHubLazyGrid( - communalContent = communalContent, - viewModel = viewModel, - contentPadding = contentPadding, - contentOffset = contentOffset, - setGridCoordinates = { gridCoordinates = it }, - updateDragPositionForRemove = { offset -> - isPointerWithinEnabledRemoveButton( - removeEnabled = removeButtonEnabled, - offset = gridCoordinates?.let { it.positionInWindow() + offset }, - containerToCheck = removeButtonCoordinates + val slideOffsetInPx = + with(LocalDensity.current) { Dimensions.SlideOffsetY.toPx().toInt() } + AnimatedVisibility( + visible = isCommunalContentVisible, + enter = + fadeIn( + animationSpec = + tween(durationMillis = 83, delayMillis = 83, easing = LinearEasing) + ) + + slideInVertically( + animationSpec = tween(durationMillis = 1000, easing = Emphasized), + initialOffsetY = { -slideOffsetInPx } + ), + exit = + fadeOut( + animationSpec = tween(durationMillis = 167, easing = LinearEasing) + ) + + slideOutVertically( + animationSpec = tween(durationMillis = 1000, easing = Emphasized), + targetOffsetY = { -slideOffsetInPx } + ), + modifier = Modifier.fillMaxSize(), + ) { + Box { + CommunalHubLazyGrid( + communalContent = communalContent, + viewModel = viewModel, + contentPadding = contentPadding, + contentOffset = contentOffset, + setGridCoordinates = { gridCoordinates = it }, + updateDragPositionForRemove = { offset -> + isPointerWithinEnabledRemoveButton( + removeEnabled = removeButtonEnabled, + offset = + gridCoordinates?.let { it.positionInWindow() + offset }, + containerToCheck = removeButtonCoordinates + ) + }, + gridState = gridState, + contentListState = contentListState, + selectedKey = selectedKey, + widgetConfigurator = widgetConfigurator, + interactionHandler = interactionHandler, ) - }, - gridState = gridState, - contentListState = contentListState, - selectedKey = selectedKey, - widgetConfigurator = widgetConfigurator, - interactionHandler = interactionHandler, - ) + } + } } } - if (viewModel.isEditMode && onOpenWidgetPicker != null && onEditDone != null) { - Toolbar( - setToolbarSize = { toolbarSize = it }, - setRemoveButtonCoordinates = { removeButtonCoordinates = it }, - onEditDone = onEditDone, - onOpenWidgetPicker = onOpenWidgetPicker, - onRemoveClicked = { - val index = - selectedKey.value?.let { key -> - contentListState.list.indexOfFirst { it.key == key } + if (onOpenWidgetPicker != null && onEditDone != null) { + AnimatedVisibility( + visible = viewModel.isEditMode && isCommunalContentVisible, + enter = + fadeIn(animationSpec = tween(durationMillis = 250, easing = LinearEasing)) + + slideInVertically( + animationSpec = tween(durationMillis = 1000, easing = Emphasized), + ), + exit = + fadeOut(animationSpec = tween(durationMillis = 167, easing = LinearEasing)) + + slideOutVertically( + animationSpec = tween(durationMillis = 1000, easing = Emphasized) + ), + ) { + Toolbar( + setToolbarSize = { toolbarSize = it }, + setRemoveButtonCoordinates = { removeButtonCoordinates = it }, + onEditDone = onEditDone, + onOpenWidgetPicker = onOpenWidgetPicker, + onRemoveClicked = { + val index = + selectedKey.value?.let { key -> + contentListState.list.indexOfFirst { it.key == key } + } + index?.let { + contentListState.onRemove(it) + contentListState.onSaveList() + viewModel.setSelectedKey(null) } - index?.let { - contentListState.onRemove(it) - contentListState.onSaveList() - viewModel.setSelectedKey(null) - } - }, - removeEnabled = removeButtonEnabled - ) + }, + removeEnabled = removeButtonEnabled + ) + } } if (currentPopup == PopupType.CtaTile) { PopupOnDismissCtaTile(viewModel::onHidePopup) @@ -585,24 +633,23 @@ private fun Toolbar( ) .onSizeChanged { setToolbarSize(it) }, ) { - val spacerModifier = Modifier.width(ButtonDefaults.IconSpacing) - - if (!removeEnabled) { - Button( - modifier = Modifier.align(Alignment.CenterStart), - onClick = onOpenWidgetPicker, - colors = filledButtonColors(), - contentPadding = Dimensions.ButtonPadding - ) { - Icon(Icons.Default.Add, stringResource(R.string.hub_mode_add_widget_button_text)) - Spacer(spacerModifier) - Text( - text = stringResource(R.string.hub_mode_add_widget_button_text), - ) - } + ToolbarButton( + isPrimary = !removeEnabled, + modifier = Modifier.align(Alignment.CenterStart), + onClick = onOpenWidgetPicker, + ) { + Icon(Icons.Default.Add, stringResource(R.string.hub_mode_add_widget_button_text)) + Text( + text = stringResource(R.string.hub_mode_add_widget_button_text), + ) } - if (removeEnabled) { + AnimatedVisibility( + modifier = Modifier.align(Alignment.Center), + visible = removeEnabled, + enter = fadeIn(), + exit = fadeOut() + ) { Button( onClick = onRemoveClicked, colors = filledButtonColors(), @@ -610,33 +657,97 @@ private fun Toolbar( modifier = Modifier.graphicsLayer { alpha = removeButtonAlpha } .onGloballyPositioned { setRemoveButtonCoordinates(it) } - .align(Alignment.Center) ) { - RemoveButtonContent(spacerModifier) + Row( + horizontalArrangement = + Arrangement.spacedBy( + ButtonDefaults.IconSpacing, + Alignment.CenterHorizontally + ), + verticalAlignment = Alignment.CenterVertically + ) { + Icon(Icons.Default.Close, stringResource(R.string.button_to_remove_widget)) + Text( + text = stringResource(R.string.button_to_remove_widget), + ) + } } } - if (!removeEnabled) { - Button( - modifier = Modifier.align(Alignment.CenterEnd), - onClick = onEditDone, - colors = filledButtonColors(), - contentPadding = Dimensions.ButtonPadding + ToolbarButton( + isPrimary = !removeEnabled, + modifier = Modifier.align(Alignment.CenterEnd), + onClick = onEditDone, + ) { + Icon( + Icons.Default.Check, + stringResource(id = R.string.hub_mode_editing_exit_button_text) + ) + Text( + text = stringResource(R.string.hub_mode_editing_exit_button_text), + ) + } + } +} + +/** + * Toolbar button that displays as a filled button if primary, and an outline button if secondary. + */ +@Composable +private fun ToolbarButton( + isPrimary: Boolean = true, + onClick: () -> Unit, + modifier: Modifier = Modifier, + content: @Composable RowScope.() -> Unit +) { + val colors = LocalAndroidColorScheme.current + AnimatedVisibility( + visible = isPrimary, + modifier = modifier, + enter = fadeIn(), + exit = fadeOut() + ) { + Button( + onClick = onClick, + colors = filledButtonColors(), + contentPadding = Dimensions.ButtonPadding, + ) { + Row( + horizontalArrangement = + Arrangement.spacedBy(ButtonDefaults.IconSpacing, Alignment.CenterHorizontally), + verticalAlignment = Alignment.CenterVertically ) { - Icon( - Icons.Default.Check, - stringResource(id = R.string.hub_mode_editing_exit_button_text) - ) - Spacer(spacerModifier) - Text( - text = stringResource(R.string.hub_mode_editing_exit_button_text), - ) + content() + } + } + } + + AnimatedVisibility( + visible = !isPrimary, + modifier = modifier, + enter = fadeIn(), + exit = fadeOut() + ) { + OutlinedButton( + onClick = onClick, + colors = + ButtonDefaults.outlinedButtonColors( + contentColor = colors.primary, + ), + border = BorderStroke(width = 2.0.dp, color = colors.primary), + contentPadding = Dimensions.ButtonPadding, + ) { + Row( + horizontalArrangement = + Arrangement.spacedBy(ButtonDefaults.IconSpacing, Alignment.CenterHorizontally), + verticalAlignment = Alignment.CenterVertically + ) { + content() } } } } -@OptIn(ExperimentalAnimationApi::class) @Composable private fun AnimatedVisibilityScope.ButtonToEditWidgets( onClick: () -> Unit, @@ -739,15 +850,6 @@ private fun PopupOnDismissCtaTile(onHidePopup: () -> Unit) { } @Composable -private fun RemoveButtonContent(spacerModifier: Modifier) { - Icon(Icons.Default.Close, stringResource(R.string.button_to_remove_widget)) - Spacer(spacerModifier) - Text( - text = stringResource(R.string.button_to_remove_widget), - ) -} - -@Composable private fun filledButtonColors(): ButtonColors { val colors = LocalAndroidColorScheme.current return ButtonDefaults.buttonColors( @@ -1277,6 +1379,7 @@ object Dimensions { horizontal = ToolbarButtonPaddingHorizontal, ) val IconSize = 40.dp + val SlideOffsetY = 30.dp } private object Colors { diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/section/AmbientStatusBarSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/section/AmbientStatusBarSection.kt new file mode 100644 index 000000000000..3b335fa3141e --- /dev/null +++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/section/AmbientStatusBarSection.kt @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.communal.ui.compose.section + +import android.view.LayoutInflater +import android.view.View +import android.widget.FrameLayout +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.viewinterop.AndroidView +import com.android.compose.animation.scene.SceneScope +import com.android.systemui.ambient.statusbar.dagger.AmbientStatusBarComponent +import com.android.systemui.ambient.statusbar.ui.AmbientStatusBarView +import com.android.systemui.communal.ui.compose.Communal +import com.android.systemui.res.R +import javax.inject.Inject + +class AmbientStatusBarSection +@Inject +constructor( + private val factory: AmbientStatusBarComponent.Factory, +) { + @Composable + fun SceneScope.AmbientStatusBar(modifier: Modifier = Modifier) { + AndroidView( + factory = { context -> + (LayoutInflater.from(context) + .inflate( + /* resource = */ R.layout.ambient_status_bar_view, + /* root = */ FrameLayout(context), + /* attachToRoot = */ false, + ) as AmbientStatusBarView) + .apply { + visibility = View.VISIBLE + factory.create(this).getController().apply { init() } + } + }, + modifier = modifier.element(Communal.Elements.StatusBar) + ) + } +} diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt index 10c403055c8e..68395b463b6a 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt @@ -59,6 +59,13 @@ val SceneContainerTransitions = transitions { goneToShadeTransition(durationScale = 0.9) } from(Scenes.Gone, to = Scenes.QuickSettings) { goneToQuickSettingsTransition() } + from( + Scenes.Gone, + to = Scenes.QuickSettings, + key = SlightlyFasterShadeCollapse, + ) { + goneToQuickSettingsTransition(durationScale = 0.9) + } from(Scenes.Gone, to = Scenes.QuickSettingsShade) { goneToQuickSettingsShadeTransition() } from(Scenes.Lockscreen, to = Scenes.Bouncer) { lockscreenToBouncerTransition() } from(Scenes.Lockscreen, to = Scenes.Communal) { lockscreenToCommunalTransition() } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt index ac3e015e52a9..b5a10ca1e478 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt @@ -19,7 +19,10 @@ package com.android.systemui.shade.ui.composable import android.view.ContextThemeWrapper import android.view.ViewGroup +import androidx.compose.foundation.background import androidx.compose.foundation.clickable +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.interaction.collectIsHoveredAsState import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -32,7 +35,9 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.widthIn +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.ColorScheme +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass import androidx.compose.runtime.Composable import androidx.compose.runtime.derivedStateOf @@ -40,6 +45,7 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.TransformOrigin import androidx.compose.ui.graphics.graphicsLayer @@ -58,6 +64,7 @@ import com.android.compose.animation.scene.SceneScope import com.android.compose.animation.scene.TransitionState import com.android.compose.animation.scene.ValueKey import com.android.compose.animation.scene.animateElementFloatAsState +import com.android.compose.modifiers.thenIf import com.android.compose.windowsizeclass.LocalWindowSizeClass import com.android.settingslib.Utils import com.android.systemui.battery.BatteryMeterView @@ -69,6 +76,7 @@ import com.android.systemui.compose.modifiers.sysuiResTag import com.android.systemui.privacy.OngoingPrivacyChip import com.android.systemui.res.R import com.android.systemui.scene.shared.model.Scenes +import com.android.systemui.shade.ui.composable.ShadeHeader.Colors.onScrimDim import com.android.systemui.shade.ui.composable.ShadeHeader.Dimensions.CollapsedHeight import com.android.systemui.shade.ui.composable.ShadeHeader.Values.ClockScale import com.android.systemui.shade.ui.viewmodel.ShadeHeaderViewModel @@ -79,7 +87,6 @@ import com.android.systemui.statusbar.phone.ui.TintedIconManager import com.android.systemui.statusbar.pipeline.mobile.ui.view.ModernShadeCarrierGroupMobileView import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.ShadeCarrierGroupMobileIconViewModel import com.android.systemui.statusbar.policy.Clock -import kotlin.math.max object ShadeHeader { object Elements { @@ -103,6 +110,8 @@ object ShadeHeader { object Colors { val ColorScheme.shadeHeaderText: Color get() = Color.White + val ColorScheme.onScrimDim: Color + get() = Color.DarkGray } object TestTags { @@ -130,7 +139,7 @@ fun SceneScope.CollapsedShadeHeader( val horizontalPadding = max(LocalScreenCornerRadius.current / 2f, Shade.Dimensions.HorizontalPadding) - val useExpandedFormat by + val useExpandedTextFormat by remember(cutoutLocation) { derivedStateOf { cutoutLocation != CutoutLocation.CENTER || @@ -138,6 +147,10 @@ fun SceneScope.CollapsedShadeHeader( } } + val isLargeScreenLayout = + LocalWindowSizeClass.current.widthSizeClass == WindowWidthSizeClass.Medium || + LocalWindowSizeClass.current.widthSizeClass == WindowWidthSizeClass.Expanded + val isPrivacyChipVisible by viewModel.isPrivacyChipVisible.collectAsStateWithLifecycle() // This layout assumes it is globally positioned at (0, 0) and is the @@ -182,22 +195,22 @@ fun SceneScope.CollapsedShadeHeader( Modifier.element(ShadeHeader.Elements.CollapsedContentEnd) .padding(horizontal = horizontalPadding) ) { + if (isLargeScreenLayout) { + ShadeCarrierGroup( + viewModel = viewModel, + modifier = Modifier.align(Alignment.CenterVertically), + ) + } SystemIconContainer( + viewModel = viewModel, + isClickable = isLargeScreenLayout, modifier = Modifier.align(Alignment.CenterVertically) ) { - when (LocalWindowSizeClass.current.widthSizeClass) { - WindowWidthSizeClass.Medium, - WindowWidthSizeClass.Expanded -> - ShadeCarrierGroup( - viewModel = viewModel, - modifier = Modifier.align(Alignment.CenterVertically), - ) - } StatusIcons( viewModel = viewModel, createTintedIconManager = createTintedIconManager, statusBarIconController = statusBarIconController, - useExpandedFormat = useExpandedFormat, + useExpandedFormat = useExpandedTextFormat, modifier = Modifier.align(Alignment.CenterVertically) .padding(end = 6.dp) @@ -206,7 +219,7 @@ fun SceneScope.CollapsedShadeHeader( BatteryIcon( createBatteryMeterViewController = createBatteryMeterViewController, - useExpandedFormat = useExpandedFormat, + useExpandedFormat = useExpandedTextFormat, modifier = Modifier.align(Alignment.CenterVertically), ) } @@ -322,7 +335,7 @@ fun SceneScope.ExpandedShadeHeader( modifier = Modifier.widthIn(max = 90.dp).align(Alignment.CenterVertically), ) Spacer(modifier = Modifier.weight(1f)) - SystemIconContainer { + SystemIconContainer(viewModel = viewModel, isClickable = false) { StatusIcons( viewModel = viewModel, createTintedIconManager = createTintedIconManager, @@ -531,12 +544,30 @@ private fun SceneScope.StatusIcons( @Composable private fun SystemIconContainer( + viewModel: ShadeHeaderViewModel, + isClickable: Boolean, modifier: Modifier = Modifier, content: @Composable RowScope.() -> Unit ) { - // TODO(b/298524053): add hover state for this container + val interactionSource = remember { MutableInteractionSource() } + val isHovered by interactionSource.collectIsHoveredAsState() + + val hoverModifier = Modifier + .clip(RoundedCornerShape(CollapsedHeight / 4)) + .background(MaterialTheme.colorScheme.onScrimDim) + Row( - modifier = modifier.height(CollapsedHeight), + modifier = modifier + .height(CollapsedHeight) + .padding(vertical = CollapsedHeight / 4) + .thenIf(isClickable) { + Modifier.clickable( + interactionSource = interactionSource, + indication = null, + onClick = { viewModel.onSystemIconContainerClicked() }, + ) + } + .thenIf(isHovered) { hoverModifier }, content = content, ) } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ButtonComponent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ButtonComponent.kt index e1ae80f13312..6d03118645f3 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ButtonComponent.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ButtonComponent.kt @@ -88,7 +88,7 @@ class ButtonComponent( } else { MaterialTheme.colorScheme.surface }, - shape = RoundedCornerShape(28.dp), + shape = RoundedCornerShape(20.dp), contentColor = if (viewModel.isActive) { MaterialTheme.colorScheme.onTertiaryContainer diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ToggleButtonComponent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ToggleButtonComponent.kt index 1b821d36dceb..bb2daecd3a25 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ToggleButtonComponent.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ToggleButtonComponent.kt @@ -91,7 +91,7 @@ class ToggleButtonComponent( contentDescription = label }, onClick = { onCheckedChange(!viewModel.isActive) }, - shape = RoundedCornerShape(28.dp), + shape = RoundedCornerShape(20.dp), colors = colors, contentPadding = PaddingValues(0.dp) ) { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/statusbar/ui/AmbientStatusBarViewControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/statusbar/ui/AmbientStatusBarViewControllerTest.java index d84d151ae984..201ed00acae3 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/statusbar/ui/AmbientStatusBarViewControllerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/statusbar/ui/AmbientStatusBarViewControllerTest.java @@ -146,6 +146,7 @@ public class AmbientStatusBarViewControllerTest extends SysuiTestCase { mDreamOverlayStateController, mUserTracker, mKosmos.getWifiInteractor(), + mKosmos.getCommunalSceneInteractor(), mLogBuffer); } @@ -272,6 +273,7 @@ public class AmbientStatusBarViewControllerTest extends SysuiTestCase { mDreamOverlayStateController, mUserTracker, mKosmos.getWifiInteractor(), + mKosmos.getCommunalSceneInteractor(), mLogBuffer); controller.onViewAttached(); verify(mView, never()).showIcon( diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt index e61b2d0f2e74..cf145471e55f 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/CommunalSceneStartableTest.kt @@ -111,6 +111,25 @@ class CommunalSceneStartableTest : SysuiTestCase() { } } + @Test + fun keyguardGoesAway_whenInEditMode_doesNotChangeScene() = + with(kosmos) { + testScope.runTest { + val scene by collectLastValue(communalSceneInteractor.currentScene) + communalSceneInteractor.changeScene(CommunalScenes.Communal) + assertThat(scene).isEqualTo(CommunalScenes.Communal) + + communalInteractor.setEditModeOpen(true) + fakeKeyguardTransitionRepository.sendTransitionSteps( + from = KeyguardState.ALTERNATE_BOUNCER, + to = KeyguardState.GONE, + testScope = this + ) + // Scene change will be handled in EditWidgetsActivity not here + assertThat(scene).isEqualTo(CommunalScenes.Communal) + } + } + @Ignore("Ignored until custom animations are implemented in b/322787129") @Test fun deviceDocked_forceCommunalScene() = diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt index e42a67b7c5b0..3d454a202220 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalInteractorTest.kt @@ -52,6 +52,7 @@ import com.android.systemui.communal.domain.model.CommunalTransitionProgressMode import com.android.systemui.communal.shared.model.CommunalContentSize import com.android.systemui.communal.shared.model.CommunalScenes import com.android.systemui.communal.shared.model.CommunalWidgetContentModel +import com.android.systemui.communal.shared.model.EditModeState import com.android.systemui.communal.widgets.EditWidgetsActivityStarter import com.android.systemui.coroutines.collectLastValue import com.android.systemui.flags.EnableSceneContainer @@ -121,6 +122,7 @@ class CommunalInteractorTest : SysuiTestCase() { private lateinit var communalPrefsRepository: FakeCommunalPrefsRepository private lateinit var editWidgetsActivityStarter: EditWidgetsActivityStarter private lateinit var sceneInteractor: SceneInteractor + private lateinit var communalSceneInteractor: CommunalSceneInteractor private lateinit var userTracker: FakeUserTracker private lateinit var activityStarter: ActivityStarter private lateinit var userManager: UserManager @@ -141,6 +143,7 @@ class CommunalInteractorTest : SysuiTestCase() { editWidgetsActivityStarter = kosmos.editWidgetsActivityStarter communalPrefsRepository = kosmos.fakeCommunalPrefsRepository sceneInteractor = kosmos.sceneInteractor + communalSceneInteractor = kosmos.communalSceneInteractor userTracker = kosmos.fakeUserTracker activityStarter = kosmos.activityStarter userManager = kosmos.userManager @@ -815,7 +818,11 @@ class CommunalInteractorTest : SysuiTestCase() { @Test fun testShowWidgetEditorStartsActivity() = testScope.runTest { + val editModeState by collectLastValue(communalSceneInteractor.editModeState) + underTest.showWidgetEditor() + + assertThat(editModeState).isEqualTo(EditModeState.STARTING) verify(editWidgetsActivityStarter).startActivity() } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractorTest.kt index a0e7781a07b0..6e48b999af47 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractorTest.kt @@ -24,6 +24,7 @@ import com.android.systemui.animation.ActivityTransitionAnimator import com.android.systemui.communal.data.repository.communalSceneRepository import com.android.systemui.communal.domain.model.CommunalTransitionProgressModel import com.android.systemui.communal.shared.model.CommunalScenes +import com.android.systemui.communal.shared.model.EditModeState import com.android.systemui.coroutines.collectLastValue import com.android.systemui.kosmos.testScope import com.android.systemui.testKosmos @@ -82,6 +83,27 @@ class CommunalSceneInteractorTest : SysuiTestCase() { } @Test + fun snapToSceneForActivity() = + testScope.runTest { + val currentScene by collectLastValue(underTest.currentScene) + assertThat(currentScene).isEqualTo(CommunalScenes.Blank) + + underTest.snapToSceneForActivityStart(CommunalScenes.Communal) + assertThat(currentScene).isEqualTo(CommunalScenes.Communal) + } + + @Test + fun snapToSceneForActivity_willNotChangeScene_forEditModeActivity() = + testScope.runTest { + val currentScene by collectLastValue(underTest.currentScene) + assertThat(currentScene).isEqualTo(CommunalScenes.Blank) + + underTest.setEditModeState(EditModeState.STARTING) + underTest.snapToSceneForActivityStart(CommunalScenes.Communal) + assertThat(currentScene).isEqualTo(CommunalScenes.Blank) + } + + @Test fun transitionProgress_fullProgress() = testScope.runTest { val transitionProgress by 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 84dbfd488fe7..d5fe2a1b2e47 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 @@ -38,14 +38,17 @@ import com.android.systemui.communal.data.repository.FakeCommunalWidgetRepositor import com.android.systemui.communal.data.repository.fakeCommunalMediaRepository import com.android.systemui.communal.data.repository.fakeCommunalTutorialRepository import com.android.systemui.communal.data.repository.fakeCommunalWidgetRepository +import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor import com.android.systemui.communal.domain.interactor.communalInteractor import com.android.systemui.communal.domain.interactor.communalSceneInteractor import com.android.systemui.communal.domain.interactor.communalSettingsInteractor import com.android.systemui.communal.domain.model.CommunalContentModel import com.android.systemui.communal.shared.log.CommunalUiEvent import com.android.systemui.communal.shared.model.CommunalWidgetContentModel +import com.android.systemui.communal.shared.model.EditModeState import com.android.systemui.communal.ui.viewmodel.CommunalEditModeViewModel import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor import com.android.systemui.kosmos.testDispatcher import com.android.systemui.kosmos.testScope import com.android.systemui.log.logcatLogBuffer @@ -86,6 +89,7 @@ class CommunalEditModeViewModelTest : SysuiTestCase() { private lateinit var widgetRepository: FakeCommunalWidgetRepository private lateinit var smartspaceRepository: FakeSmartspaceRepository private lateinit var mediaRepository: FakeCommunalMediaRepository + private lateinit var communalSceneInteractor: CommunalSceneInteractor private val testableResources = context.orCreateTestableResources @@ -99,6 +103,7 @@ class CommunalEditModeViewModelTest : SysuiTestCase() { widgetRepository = kosmos.fakeCommunalWidgetRepository smartspaceRepository = kosmos.fakeSmartspaceRepository mediaRepository = kosmos.fakeCommunalMediaRepository + communalSceneInteractor = kosmos.communalSceneInteractor kosmos.fakeUserTracker.set( userInfos = listOf(MAIN_USER_INFO), selectedUserIndex = 0, @@ -107,9 +112,10 @@ class CommunalEditModeViewModelTest : SysuiTestCase() { underTest = CommunalEditModeViewModel( - kosmos.communalSceneInteractor, + communalSceneInteractor, kosmos.communalInteractor, kosmos.communalSettingsInteractor, + kosmos.keyguardTransitionInteractor, mediaHost, uiEventLogger, logcatLogBuffer("CommunalEditModeViewModelTest"), @@ -172,6 +178,22 @@ class CommunalEditModeViewModelTest : SysuiTestCase() { } @Test + fun isCommunalContentVisible_isTrue_whenEditModeShowing() = + testScope.runTest { + val isCommunalContentVisible by collectLastValue(underTest.isCommunalContentVisible) + communalSceneInteractor.setEditModeState(EditModeState.SHOWING) + assertThat(isCommunalContentVisible).isEqualTo(true) + } + + @Test + fun isCommunalContentVisible_isFalse_whenEditModeNotShowing() = + testScope.runTest { + val isCommunalContentVisible by collectLastValue(underTest.isCommunalContentVisible) + communalSceneInteractor.setEditModeState(null) + assertThat(isCommunalContentVisible).isEqualTo(false) + } + + @Test fun deleteWidget() = testScope.runTest { tutorialRepository.setTutorialSettingState(Settings.Secure.HUB_MODE_TUTORIAL_COMPLETED) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractorTest.kt index cfc6b3304399..d20fec44d60f 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractorTest.kt @@ -32,10 +32,14 @@ package com.android.systemui.keyguard.domain.interactor +import android.platform.test.annotations.DisableFlags +import android.platform.test.annotations.EnableFlags import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest +import com.android.systemui.Flags import com.android.systemui.SysuiTestCase import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepository +import com.android.systemui.communal.domain.interactor.communalInteractor import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository @@ -76,6 +80,7 @@ class FromAlternateBouncerTransitionInteractorTest : SysuiTestCase() { } @Test + @DisableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR) fun transitionToGone_keyguardOccluded_biometricAuthenticated() = testScope.runTest { transitionRepository.sendTransitionSteps( @@ -96,6 +101,25 @@ class FromAlternateBouncerTransitionInteractorTest : SysuiTestCase() { } @Test + @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR) + fun transitionToGone_keyguardOccludedThenAltBouncer_authed_wmStateRefactor() = + testScope.runTest { + transitionRepository.sendTransitionSteps( + from = KeyguardState.OCCLUDED, + to = KeyguardState.ALTERNATE_BOUNCER, + testScope + ) + reset(transitionRepository) + + // Authentication results in calling startDismissKeyguardTransition. + kosmos.keyguardTransitionInteractor.startDismissKeyguardTransition() + runCurrent() + + assertThat(transitionRepository) + .startedTransition(from = KeyguardState.ALTERNATE_BOUNCER, to = KeyguardState.GONE) + } + + @Test fun noTransition_keyguardNotOccluded_biometricAuthenticated() = testScope.runTest { transitionRepository.sendTransitionSteps( @@ -143,4 +167,37 @@ class FromAlternateBouncerTransitionInteractorTest : SysuiTestCase() { to = KeyguardState.OCCLUDED ) } + + @Test + fun transitionToGone_whenOpeningGlanceableHubEditMode() = + testScope.runTest { + kosmos.fakeKeyguardBouncerRepository.setAlternateVisible(true) + runCurrent() + + // On Glanceable hub and edit mode activity is started + transitionRepository.sendTransitionSteps( + from = KeyguardState.GLANCEABLE_HUB, + to = KeyguardState.ALTERNATE_BOUNCER, + testScope + ) + reset(transitionRepository) + + kosmos.communalInteractor.setEditModeOpen(true) + runCurrent() + + // Auth and alternate bouncer is hidden + kosmos.fakeKeyguardBouncerRepository.setAlternateVisible(false) + advanceTimeBy(200) // advance past delay + + // Then no transition should occur yet + assertThat(transitionRepository).noTransitionsStarted() + + // When keyguard is going away + kosmos.fakeKeyguardRepository.setKeyguardGoingAway(true) + runCurrent() + + // Then transition to GONE should occur + assertThat(transitionRepository) + .startedTransition(from = KeyguardState.ALTERNATE_BOUNCER, to = KeyguardState.GONE) + } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorTest.kt index 6c5001ab9415..6eb9862fb4f1 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorTest.kt @@ -53,6 +53,7 @@ import com.android.systemui.power.domain.interactor.PowerInteractor 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.statusbar.domain.interactor.keyguardOcclusionInteractor import com.android.systemui.testKosmos import junit.framework.Assert.assertEquals import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -299,6 +300,7 @@ class FromAodTransitionInteractorTest : SysuiTestCase() { fun testTransitionToOccluded_onWake() = testScope.runTest { kosmos.fakeKeyguardRepository.setKeyguardOccluded(true) + kosmos.keyguardOcclusionInteractor.setWmNotifiedShowWhenLockedActivityOnTop(true) powerInteractor.setAwakeForTest() advanceTimeBy(100) // account for debouncing @@ -312,6 +314,7 @@ class FromAodTransitionInteractorTest : SysuiTestCase() { testScope.runTest { kosmos.fakeKeyguardRepository.setKeyguardShowing(false) kosmos.fakeKeyguardRepository.setKeyguardDismissible(true) + kosmos.keyguardTransitionInteractor.startDismissKeyguardTransition() powerInteractor.setAwakeForTest() advanceTimeBy(100) // account for debouncing diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt index addbdb664c77..7906a8244c5d 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt @@ -27,6 +27,7 @@ import com.android.systemui.SysuiTestCase import com.android.systemui.bouncer.data.repository.keyguardBouncerRepository import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.coroutines.collectValues import com.android.systemui.flags.EnableSceneContainer import com.android.systemui.keyguard.data.repository.fakeCommandQueue import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository @@ -191,6 +192,7 @@ class KeyguardInteractorTest : SysuiTestCase() { fun dismissAlpha() = testScope.runTest { val dismissAlpha by collectLastValue(underTest.dismissAlpha) + assertThat(dismissAlpha).isEqualTo(1f) keyguardTransitionRepository.sendTransitionSteps( from = KeyguardState.AOD, @@ -202,9 +204,9 @@ class KeyguardInteractorTest : SysuiTestCase() { // User begins to swipe up shadeRepository.setLegacyShadeExpansion(0.99f) - // When not dismissable, no alpha value (null) should emit + // When not dismissable, the last alpha value should still be present repository.setKeyguardDismissible(false) - assertThat(dismissAlpha).isNull() + assertThat(dismissAlpha).isEqualTo(1f) repository.setKeyguardDismissible(true) shadeRepository.setLegacyShadeExpansion(0.98f) @@ -212,9 +214,11 @@ class KeyguardInteractorTest : SysuiTestCase() { } @Test - fun dismissAlpha_whenShadeIsExpandedEmitsNull() = + fun dismissAlpha_whenShadeResetsEmitsOne() = testScope.runTest { - val dismissAlpha by collectLastValue(underTest.dismissAlpha) + val dismissAlpha by collectValues(underTest.dismissAlpha) + assertThat(dismissAlpha[0]).isEqualTo(1f) + assertThat(dismissAlpha.size).isEqualTo(1) keyguardTransitionRepository.sendTransitionSteps( from = KeyguardState.AOD, @@ -222,14 +226,50 @@ class KeyguardInteractorTest : SysuiTestCase() { testScope, ) - repository.setStatusBarState(StatusBarState.SHADE_LOCKED) - shadeRepository.setQsExpansion(1f) + // User begins to swipe up + repository.setStatusBarState(StatusBarState.KEYGUARD) + repository.setKeyguardDismissible(true) + shadeRepository.setLegacyShadeExpansion(0.98f) - repository.setKeyguardDismissible(false) - assertThat(dismissAlpha).isNull() + assertThat(dismissAlpha[1]).isGreaterThan(0.5f) + assertThat(dismissAlpha[1]).isLessThan(1f) + assertThat(dismissAlpha.size).isEqualTo(2) + + // Now reset the shade + shadeRepository.setLegacyShadeExpansion(1f) + assertThat(dismissAlpha[2]).isEqualTo(1f) + assertThat(dismissAlpha.size).isEqualTo(3) + } + + @Test + fun dismissAlpha_doesNotEmitWhileTransitioning() = + testScope.runTest { + val dismissAlpha by collectLastValue(underTest.dismissAlpha) + assertThat(dismissAlpha).isEqualTo(1f) + + keyguardTransitionRepository.sendTransitionSteps( + listOf( + TransitionStep( + from = KeyguardState.AOD, + to = KeyguardState.GONE, + value = 0f, + transitionState = TransitionState.STARTED, + ), + TransitionStep( + from = KeyguardState.AOD, + to = KeyguardState.GONE, + value = 0.1f, + transitionState = TransitionState.RUNNING, + ), + ), + testScope, + ) repository.setKeyguardDismissible(true) - assertThat(dismissAlpha).isNull() + shadeRepository.setLegacyShadeExpansion(0.98f) + + // Should still be one + assertThat(dismissAlpha).isEqualTo(1f) } @Test diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt index 9dc930babc10..6e16705b0739 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt @@ -197,7 +197,7 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() { @Test @EnableSceneContainer - fun surfaceBehindVisibility_fromLockscreenToGone_trueThroughout() = + fun surfaceBehindVisibility_fromLockscreenToGone_noUserInput_trueThroughout() = testScope.runTest { val isSurfaceBehindVisible by collectLastValue(underTest.value.surfaceBehindVisibility) val currentScene by collectLastValue(kosmos.sceneInteractor.currentScene) @@ -249,6 +249,43 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() { @Test @EnableSceneContainer + fun surfaceBehindVisibility_fromLockscreenToGone_withUserInput_falseUntilInputStops() = + testScope.runTest { + val isSurfaceBehindVisible by collectLastValue(underTest.value.surfaceBehindVisibility) + val currentScene by collectLastValue(kosmos.sceneInteractor.currentScene) + + // Before the transition, we start on Lockscreen so the surface should start invisible. + kosmos.setSceneTransition(ObservableTransitionState.Idle(Scenes.Lockscreen)) + assertThat(currentScene).isEqualTo(Scenes.Lockscreen) + assertThat(isSurfaceBehindVisible).isFalse() + + // Unlocked with fingerprint. + kosmos.deviceEntryFingerprintAuthRepository.setAuthenticationStatus( + SuccessFingerprintAuthenticationStatus(0, true) + ) + + // Start the transition to Gone, the surface should not be visible while + // isUserInputOngoing is true + val isUserInputOngoing = MutableStateFlow(true) + kosmos.setSceneTransition( + ObservableTransitionState.Transition( + fromScene = Scenes.Lockscreen, + toScene = Scenes.Gone, + isInitiatedByUserInput = true, + isUserInputOngoing = isUserInputOngoing, + progress = flowOf(0.51f), + currentScene = flowOf(Scenes.Gone), + ) + ) + assertThat(isSurfaceBehindVisible).isFalse() + + // When isUserInputOngoing becomes false, then the surface should become visible. + isUserInputOngoing.value = false + assertThat(isSurfaceBehindVisible).isTrue() + } + + @Test + @EnableSceneContainer fun surfaceBehindVisibility_fromBouncerToGone_becomesTrue() = testScope.runTest { val isSurfaceBehindVisible by collectLastValue(underTest.value.surfaceBehindVisibility) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/data/repository/MediaFilterRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/data/repository/MediaFilterRepositoryTest.kt index 73e6506711f2..bc0512a1468d 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/data/repository/MediaFilterRepositoryTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/data/repository/MediaFilterRepositoryTest.kt @@ -152,7 +152,6 @@ class MediaFilterRepositoryTest : SysuiTestCase() { underTest.setRecommendation(mediaRecommendation.copy(isActive = false)) assertThat(smartspaceMediaData).isNotEqualTo(mediaRecommendation) - assertThat(smartspaceMediaData?.isActive).isFalse() assertThat(underTest.isRecommendationActive()).isFalse() } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractorTest.kt index e87c8adc33a7..899122d4dd45 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractorTest.kt @@ -35,6 +35,7 @@ import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx import com.android.systemui.screenrecord.RecordingController import com.android.systemui.screenrecord.data.model.ScreenRecordModel +import com.android.systemui.screenrecord.data.repository.ScreenRecordRepositoryImpl import com.android.systemui.statusbar.phone.KeyguardDismissUtil import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.argumentCaptor @@ -72,11 +73,18 @@ class ScreenRecordTileUserActionInteractorTest : SysuiTestCase() { .thenReturn(dialog) } + private val screenRecordRepository = + ScreenRecordRepositoryImpl( + bgCoroutineContext = testScope.testScheduler, + recordingController = recordingController, + ) + private val underTest = ScreenRecordTileUserActionInteractor( context, testScope.testScheduler, testScope.testScheduler, + screenRecordRepository, recordingController, keyguardInteractor, keyguardDismissUtil, 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 ac66e6657a75..e40c8eecca0f 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 @@ -14,7 +14,7 @@ * limitations under the License. */ -@file:OptIn(ExperimentalCoroutinesApi::class, ExperimentalCoroutinesApi::class) +@file:OptIn(ExperimentalCoroutinesApi::class) package com.android.systemui.scene.domain.startable @@ -395,6 +395,7 @@ class SceneContainerStartableTest : SysuiTestCase() { ) assertThat(currentSceneKey).isEqualTo(Scenes.Gone) underTest.start() + runCurrent() kosmos.fakePowerRepository.updateWakefulness( rawState = WakefulnessState.STARTING_TO_SLEEP, @@ -1285,6 +1286,42 @@ class SceneContainerStartableTest : SysuiTestCase() { } @Test + fun switchToGone_whenSurfaceBehindLockscreenVisibleMidTransition() = + testScope.runTest { + val currentScene by collectLastValue(sceneInteractor.currentScene) + val transitionStateFlow = + prepareState( + authenticationMethod = AuthenticationMethodModel.None, + ) + underTest.start() + assertThat(currentScene).isEqualTo(Scenes.Lockscreen) + // Swipe to Gone, more than halfway + transitionStateFlow.value = + ObservableTransitionState.Transition( + fromScene = Scenes.Lockscreen, + toScene = Scenes.Gone, + currentScene = flowOf(Scenes.Gone), + progress = flowOf(0.51f), + isInitiatedByUserInput = true, + isUserInputOngoing = flowOf(true), + ) + runCurrent() + // Lift finger + transitionStateFlow.value = + ObservableTransitionState.Transition( + fromScene = Scenes.Lockscreen, + toScene = Scenes.Gone, + currentScene = flowOf(Scenes.Gone), + progress = flowOf(0.51f), + isInitiatedByUserInput = true, + isUserInputOngoing = flowOf(false), + ) + runCurrent() + + assertThat(currentScene).isEqualTo(Scenes.Gone) + } + + @Test fun switchToGone_extendUnlock() = testScope.runTest { val currentScene by collectLastValue(sceneInteractor.currentScene) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt index f89f18ac29f1..3ded8a346ce9 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt @@ -6,15 +6,27 @@ import android.provider.Settings import android.telephony.SubscriptionManager.PROFILE_CLASS_UNSET import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest +import com.android.compose.animation.scene.ObservableTransitionState +import com.android.compose.animation.scene.SceneKey import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor +import com.android.systemui.flags.EnableSceneContainer +import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository +import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus import com.android.systemui.kosmos.testScope import com.android.systemui.plugins.activityStarter +import com.android.systemui.scene.domain.interactor.sceneInteractor +import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.fakeMobileIconsInteractor import com.android.systemui.testKosmos import com.android.systemui.util.mockito.argThat import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test @@ -24,12 +36,16 @@ import org.mockito.ArgumentMatchers.anyInt import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations +@OptIn(ExperimentalCoroutinesApi::class) @SmallTest @RunWith(AndroidJUnit4::class) +@EnableSceneContainer class ShadeHeaderViewModelTest : SysuiTestCase() { private val kosmos = testKosmos() private val testScope = kosmos.testScope private val mobileIconsInteractor = kosmos.fakeMobileIconsInteractor + private val sceneInteractor = kosmos.sceneInteractor + private val deviceEntryInteractor = kosmos.deviceEntryInteractor private val underTest: ShadeHeaderViewModel = kosmos.shadeHeaderViewModel @@ -77,6 +93,30 @@ class ShadeHeaderViewModelTest : SysuiTestCase() { ) } + @Test + fun onSystemIconContainerClicked_locked_collapsesShadeToLockscreen() = + testScope.runTest { + setDeviceEntered(false) + setScene(Scenes.Shade) + + underTest.onSystemIconContainerClicked() + runCurrent() + + assertThat(sceneInteractor.currentScene.value).isEqualTo(Scenes.Lockscreen) + } + + @Test + fun onSystemIconContainerClicked_unlocked_collapsesShadeToGone() = + testScope.runTest { + setDeviceEntered(true) + setScene(Scenes.Shade) + + underTest.onSystemIconContainerClicked() + runCurrent() + + assertThat(sceneInteractor.currentScene.value).isEqualTo(Scenes.Gone) + } + companion object { private val SUB_1 = SubscriptionModel( @@ -93,6 +133,32 @@ class ShadeHeaderViewModelTest : SysuiTestCase() { profileClass = PROFILE_CLASS_UNSET, ) } + + private fun setScene(key: SceneKey) { + sceneInteractor.changeScene(key, "test") + sceneInteractor.setTransitionState( + MutableStateFlow<ObservableTransitionState>(ObservableTransitionState.Idle(key)) + ) + testScope.runCurrent() + } + + private fun TestScope.setDeviceEntered(isEntered: Boolean) { + if (isEntered) { + // Unlock the device marking the device has entered. + kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus( + SuccessFingerprintAuthenticationStatus(0, true) + ) + runCurrent() + } + setScene( + if (isEntered) { + Scenes.Gone + } else { + Scenes.Lockscreen + } + ) + assertThat(deviceEntryInteractor.isDeviceEntered.value).isEqualTo(isEntered) + } } private class IntentMatcherAction(private val action: String) : ArgumentMatcher<Intent> { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationStackAppearanceIntegrationTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationStackAppearanceIntegrationTest.kt index c4506f2d3caa..ca4434d233a6 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationStackAppearanceIntegrationTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/NotificationStackAppearanceIntegrationTest.kt @@ -121,10 +121,13 @@ class NotificationStackAppearanceIntegrationTest : SysuiTestCase() { sceneInteractor.setTransitionState(transitionState) val expandFraction by collectLastValue(scrollViewModel.expandFraction) assertThat(expandFraction).isEqualTo(0f) + + fakeSceneDataSource.changeScene(toScene = Scenes.Gone) val isScrollable by collectLastValue(scrollViewModel.isScrollable) assertThat(isScrollable).isFalse() fakeSceneDataSource.pause() + sceneInteractor.changeScene(Scenes.Shade, "reason") val transitionProgress = MutableStateFlow(0f) transitionState.value = @@ -159,8 +162,10 @@ class NotificationStackAppearanceIntegrationTest : SysuiTestCase() { sceneInteractor.setTransitionState(transitionState) val expandFraction by collectLastValue(scrollViewModel.expandFraction) assertThat(expandFraction).isEqualTo(1f) + + fakeSceneDataSource.changeScene(toScene = Scenes.Lockscreen) val isScrollable by collectLastValue(scrollViewModel.isScrollable) - assertThat(isScrollable).isFalse() + assertThat(isScrollable).isTrue() } @Test diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt index c35c165ba761..497484f90ca9 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt @@ -42,6 +42,7 @@ import com.android.systemui.keyguard.domain.interactor.keyguardInteractor import com.android.systemui.keyguard.shared.model.BurnInModel import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.KeyguardState.AOD +import com.android.systemui.keyguard.shared.model.KeyguardState.GONE import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN import com.android.systemui.keyguard.shared.model.StatusBarState import com.android.systemui.keyguard.shared.model.TransitionState @@ -842,6 +843,30 @@ class SharedNotificationContainerViewModelTest(flags: FlagsParameterization) : S } @Test + @DisableSceneContainer + fun updateBounds_fromGone_withoutTransitions() = + testScope.runTest { + // Start step is already at 1.0 + val runningStep = TransitionStep(GONE, AOD, 1.0f, TransitionState.RUNNING) + val finishStep = TransitionStep(GONE, AOD, 1.0f, TransitionState.FINISHED) + + val bounds by collectLastValue(underTest.bounds) + val top = 123f + val bottom = 456f + + kosmos.fakeKeyguardTransitionRepository.sendTransitionStep(runningStep) + runCurrent() + kosmos.fakeKeyguardTransitionRepository.sendTransitionStep(finishStep) + runCurrent() + keyguardRootViewModel.onNotificationContainerBoundsChanged(top, bottom) + runCurrent() + + assertThat(bounds).isEqualTo( + NotificationContainerBounds(top = top, bottom = bottom) + ) + } + + @Test fun alphaOnFullQsExpansion() = testScope.runTest { val viewState = ViewStateAccessor() diff --git a/packages/SystemUI/res/drawable/android15_patch_adaptive.xml b/packages/SystemUI/res/drawable/android15_patch_adaptive.xml new file mode 100644 index 000000000000..d9492000beeb --- /dev/null +++ b/packages/SystemUI/res/drawable/android15_patch_adaptive.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> + <background android:drawable="@drawable/android15_patch_adaptive_background"/> + <foreground android:drawable="@drawable/android15_patch_adaptive_foreground"/> + <monochrome android:drawable="@drawable/android15_patch_monochrome"/> +</adaptive-icon> diff --git a/packages/SystemUI/res/drawable/android15_patch_adaptive_background.xml b/packages/SystemUI/res/drawable/android15_patch_adaptive_background.xml new file mode 100644 index 000000000000..d4850d3d99c7 --- /dev/null +++ b/packages/SystemUI/res/drawable/android15_patch_adaptive_background.xml @@ -0,0 +1,75 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="108dp" + android:height="108dp" + android:viewportWidth="108" + android:viewportHeight="108"> + <!-- space (themed version) --> + <path + android:pathData="M0,0h108v108h-108z" + android:fillColor="@android:color/system_neutral1_800"/> + <!-- stars (themed version) --> + <group> + <path + android:pathData="M32,34 h1v1h-1z" + android:fillColor="@android:color/system_accent3_200"/> + <path + android:pathData="M33,61 h1v1h-1z" + android:fillColor="@android:color/system_accent3_200"/> + <path + android:pathData="M71,34 h1v1h-1z" + android:fillColor="@android:color/system_accent3_200"/> + <path + android:pathData="M62,56 h1v1h-1z" + android:fillColor="@android:color/system_accent3_200"/> + <path + android:pathData="M68,47 h1v1h-1z" + android:fillColor="@android:color/system_accent3_200"/> + <path + android:pathData="M72,55 h1v1h-1z" + android:fillColor="@android:color/system_accent3_200"/> + <path + android:pathData="M39,36 h1v1h-1z" + android:fillColor="@android:color/system_accent3_200"/> + + <path + android:pathData="M72,49 h2v2h-2z" + android:fillColor="@android:color/system_accent3_200"/> + <path + android:pathData="M46,53 h2v2h-2z" + android:fillColor="@android:color/system_accent3_200"/> + <path + android:pathData="M32,45 h2v2h-2z" + android:fillColor="@android:color/system_accent3_200"/> + <path + android:pathData="M43,37 h2v2h-2z" + android:fillColor="@android:color/system_accent3_200"/> + <path + android:pathData="M78,51 h2v2h-2z" + android:fillColor="@android:color/system_accent3_200"/> + <path + android:pathData="M34,51 h2v2h-2z" + android:fillColor="@android:color/system_accent3_200"/> + <path + android:pathData="M76,41 h2v2h-2z" + android:fillColor="@android:color/system_accent3_200"/> + <path + android:pathData="M39,46 h2v2h-2z" + android:fillColor="@android:color/system_accent3_200"/> + </group> +</vector> diff --git a/packages/SystemUI/res/drawable/android15_patch_adaptive_foreground.xml b/packages/SystemUI/res/drawable/android15_patch_adaptive_foreground.xml new file mode 100644 index 000000000000..34f6ee07a50b --- /dev/null +++ b/packages/SystemUI/res/drawable/android15_patch_adaptive_foreground.xml @@ -0,0 +1,60 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="108dp" + android:height="108dp" + android:viewportWidth="108" + android:viewportHeight="108"> + + <!-- zoomies (themed version) --> + <group> + <path + android:pathData="M53,42C52.21,50.58 46.46,68.95 32.11,74.63C19.22,79.75 5.77,82.32 1.19,83.19C0.68,83.29 0.28,83.36 0,83.42V108H54H108V83.42C107.72,83.36 107.32,83.29 106.81,83.19C102.23,82.32 88.78,79.75 75.89,74.63C61.54,68.95 55.79,50.58 55,42H54H53Z" + android:fillColor="@android:color/system_accent1_100" + android:fillType="evenOdd"/> + <path + android:pathData="M53.25,42C52.88,50.53 50.44,69.01 43.68,74.67C36.91,80.33 32.65,82.86 31.37,83.41L54,102.87L76.63,83.41C75.35,82.86 71.09,80.33 64.32,74.67C57.56,69.01 55.12,50.53 54.75,42H54H53.25Z" + android:fillColor="#ffffff" + android:fillType="evenOdd"/> + <path + android:pathData="M54,42m-1,0a1,1 0,1 1,2 0a1,1 0,1 1,-2 0" + android:fillColor="#ffffff"/> + </group> + <group> + <!-- head! it's like sputnik --> + <path + android:pathData="M54,94.25m-26.25,0a26.25,26.25 0,1 1,52.5 0a26.25,26.25 0,1 1,-52.5 0" + android:fillColor="#34A853"/> + <!-- ant --> + <path + android:pathData="M38,63.5L44.5,74.5" + android:strokeWidth="5" + android:fillColor="#00000000" + android:strokeColor="#34A853" + android:strokeLineCap="round"/> + <path + android:pathData="M70,63.5L63.5,74.5" + android:strokeWidth="5" + android:fillColor="#00000000" + android:strokeColor="#34A853" + android:strokeLineCap="round"/> + </group> + <!-- spaceship --> + <path + android:pathData="M54,34C52.34,34 51,35.29 51,36.88V40.44C51,40.75 51.25,41 51.56,41C51.87,41 52.13,40.75 52.13,40.44V39.48C52.13,38.87 52.63,38.37 53.25,38.37H54.75C55.37,38.37 55.87,38.87 55.87,39.48V40.44C55.87,40.75 56.13,41 56.44,41C56.75,41 57,40.75 57,40.44V36.88C57,35.29 55.66,34 54,34H54Z" + android:fillColor="#E9F3EB"/> +</vector> diff --git a/packages/SystemUI/res/drawable/android15_patch_monochrome.xml b/packages/SystemUI/res/drawable/android15_patch_monochrome.xml new file mode 100644 index 000000000000..a91cc864e7cd --- /dev/null +++ b/packages/SystemUI/res/drawable/android15_patch_monochrome.xml @@ -0,0 +1,119 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="108dp" + android:height="108dp" + android:viewportWidth="108" + android:viewportHeight="108"> + <group> + <path + android:pathData=" + M54,94.25 + m-26.25,0 + a26.25,26.25 0,1 1,52.5 0 + a26.25,26.25 0,1 1,-52.5 0 + " + android:fillColor="#000000"/> + <path + android:pathData="M38,63.5L44.5,74.5" + android:strokeWidth="5" + android:fillColor="#00000000" + android:strokeColor="#000000" + android:strokeLineCap="round"/> + <path + android:pathData="M70,63.5L63.5,74.5" + android:strokeWidth="5" + android:fillColor="#00000000" + android:strokeColor="#000000" + android:strokeLineCap="round"/> + + <path + android:pathData=" + M54,34 + C52.34,34 51,35.29 51,36.88 + V40.44 + C51,40.75 51.25,41 51.56,41 + C51.87,41 52.13,40.75 52.13,40.44 + V39.48 + C52.13,38.87 52.63,38.37 53.25,38.37 + H54.75 + C55.37,38.37 55.87,38.87 55.87,39.48 + V40.44 + C55.87,40.75 56.13,41 56.44,41 + C56.75,41 57,40.75 57,40.44 + V36.88 + C57,35.29 55.66,34 54,34 + H54 + Z + " + android:fillColor="#34A853"/> + <path + android:strokeWidth="1" + android:pathData="M54,40V67" + android:fillColor="#00000000" + android:strokeColor="#40FFFFFF" + /> + + <path + android:pathData="M32,34 h1v1h-1z" + android:fillColor="#ffffff"/> + <path + android:pathData="M33,61 h1v1h-1z" + android:fillColor="#ffffff"/> + <path + android:pathData="M71,34 h1v1h-1z" + android:fillColor="#ffffff"/> + <path + android:pathData="M62,56 h1v1h-1z" + android:fillColor="#ffffff"/> + <path + android:pathData="M68,47 h1v1h-1z" + android:fillColor="#ffffff"/> + <path + android:pathData="M72,55 h1v1h-1z" + android:fillColor="#ffffff"/> + <path + android:pathData="M39,36 h1v1h-1z" + android:fillColor="#ffffff"/> + + <path + android:pathData="M72,49 h2v2h-2z" + android:fillColor="#ffffff"/> + <path + android:pathData="M46,53 h2v2h-2z" + android:fillColor="#ffffff"/> + <path + android:pathData="M32,45 h2v2h-2z" + android:fillColor="#ffffff"/> + <path + android:pathData="M43,37 h2v2h-2z" + android:fillColor="#ffffff"/> + <path + android:pathData="M78,51 h2v2h-2z" + android:fillColor="#ffffff"/> + <path + android:pathData="M34,51 h2v2h-2z" + android:fillColor="#ffffff"/> + <path + android:pathData="M76,41 h2v2h-2z" + android:fillColor="#ffffff"/> + <path + android:pathData="M39,46 h2v2h-2z" + android:fillColor="#ffffff"/> + + </group> +</vector> diff --git a/packages/SystemUI/res/layout/super_notification_shade.xml b/packages/SystemUI/res/layout/super_notification_shade.xml index 221b791b8cb5..fbb07bed4b50 100644 --- a/packages/SystemUI/res/layout/super_notification_shade.xml +++ b/packages/SystemUI/res/layout/super_notification_shade.xml @@ -62,7 +62,9 @@ <com.android.systemui.keyguard.ui.view.KeyguardRootView android:id="@id/keyguard_root_view" android:layout_width="match_parent" - android:layout_height="match_parent" /> + android:layout_height="match_parent" + android:clipChildren="false" + /> <!-- Shared container for the notification stack. Can be positioned by either the keyguard_root_view or notification_panel --> diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml index c25d8cb4e3f8..a7dfdaa658d1 100644 --- a/packages/SystemUI/res/values-af/strings.xml +++ b/packages/SystemUI/res/values-af/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Skakel dit môre outomaties weer aan"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Kenmerke soos Kitsdeel en Kry My Toestel gebruik Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth sal môreoggend aanskakel"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Oudiodeling"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Deel tans oudio"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> batterykrag"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Oudio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Kopstuk"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Watter deel van jou toestelervaring is geraak?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Kies soort kwessie"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Skermopname"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"Werkverrigting"</string> + <string name="user_interface" msgid="3712869377953950887">"Gebruikerkoppelvlak"</string> + <string name="thermal" msgid="6758074791325414831">"Termies"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Eenhandmodus"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Gehoortoestelle"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktief"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Ontkoppel"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Gehoortoestelle"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Bind nuwe toestel saam"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Klik om nuwe toestel saam te bind"</string> @@ -1201,7 +1198,7 @@ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-fi sal vir nou nie outomaties koppel nie"</string> <string name="see_all_networks" msgid="3773666844913168122">"Sien alles"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Ontkoppel Ethernet om netwerke te wissel"</string> - <string name="wifi_scan_notify_message" msgid="3753839537448621794">"Om toestelervaring te verbeter, kan programme en dienste steeds enige tyd na wi‑fi-netwerke soek, selfs wanneer wi‑fi af is. Jy kan dit in Wi-fi-opsporinginstellings verander. "<annotation id="link">"Verander"</annotation></string> + <string name="wifi_scan_notify_message" msgid="3753839537448621794">"Om toestelervaring te verbeter, kan apps en dienste steeds enige tyd na wi‑fi-netwerke soek, selfs wanneer wi‑fi af is. Jy kan dit in Wi-fi-opsporinginstellings verander. "<annotation id="link">"Verander"</annotation></string> <string name="turn_off_airplane_mode" msgid="8425587763226548579">"Skakel vliegtuigmodus af"</string> <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> wil die volgende teël by Kitsinstellings voeg"</string> <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Voeg teël by"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"gevou"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"oopgevou"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Stilusbattery <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Koppel jou stilus aan ’n laaier"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Stilus se battery is amper pap"</string> <string name="video_camera" msgid="7654002575156149298">"Videokamera"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Soekkortpaaie"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Vou ikoon in"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Vou ikoon uit"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"of"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Sleutelbordlig"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Vlak %1$d van %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Huiskontroles"</string> diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml index ed6a3e16355e..396837e405ee 100644 --- a/packages/SystemUI/res/values-am/strings.xml +++ b/packages/SystemUI/res/values-am/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"ነገ እንደገና በራስ-ሰር አስጀምር"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"እንደ ፈጣን ማጋራት እና የእኔን መሣሪያ አግኝ ያሉ ባህሪዎች ብሉቱዝን ይጠቀማሉ"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"ብሉቱዝ ነገ ጠዋት ይበራል"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"የድምጽ ማጋራት"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"ድምጽን በማጋራት ላይ"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ባትሪ"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ኦዲዮ"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ማዳመጫ"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"የትኛው የመሣሪያዎ ተሞክሮ ክፍል ተጎድቷል?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"የችግሩን አይነት ይምረጡ"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"የማያ መቅረጫ"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"አፈጻጸም"</string> + <string name="user_interface" msgid="3712869377953950887">"የተጠቃሚ በይነገፅ"</string> + <string name="thermal" msgid="6758074791325414831">"ተርማል"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"የአንድ እጅ ሁነታ"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"የመስሚያ መሣሪያዎች"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"ገቢር"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"ግንኙነት ተቋርጧል"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"የመስማት ችሎታ መሣሪያ"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"አዲስ መሣሪያ ያጣምሩ"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"አዲስ መሣሪያ ለማጣመር ጠቅ ያድርጉ"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"የታጠፈ"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"የተዘረጋ"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"የብሮስፌ ባትሪ <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"ብሮስፌዎን ከኃይል መሙያ ጋር ያገናኙ"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"የብሮስፌ ባትሪ ዝቅተኛ ነው"</string> <string name="video_camera" msgid="7654002575156149298">"የቪድዮ ካሜራ"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"የፍለጋ አቋራጮች"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"መሰብሰቢያ አዶ"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"መዘርጊያ አዶ"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ወይም"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"የቁልፍ ሰሌዳ የጀርባ ብርሃን"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"ደረጃ %1$d ከ %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"የቤት ውስጥ ቁጥጥሮች"</string> diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml index 1502765e8fbf..254549fdc1ba 100644 --- a/packages/SystemUI/res/values-ar/strings.xml +++ b/packages/SystemUI/res/values-ar/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"تفعيل البلوتوث تلقائيًا مرة أخرى غدًا"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"يُستخدَم البلوتوث في ميزات مثل Quick Share و\"العثور على جهازي\""</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"سيتم تفعيل البلوتوث صباح الغد"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"مشاركة الصوت"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"مشاركة الصوت"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"مستوى طاقة البطارية <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"صوت"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"سماعة الرأس"</string> @@ -301,11 +303,11 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"المستخدم"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> <string name="quick_settings_internet_label" msgid="6603068555872455463">"الإنترنت"</string> - <string name="quick_settings_networks_available" msgid="1875138606855420438">"الشبكات متوفرة"</string> + <string name="quick_settings_networks_available" msgid="1875138606855420438">"تتوفّر شبكات"</string> <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"الشبكات غير متوفرة"</string> <string name="quick_settings_wifi_detail_empty_text" msgid="483130889414601732">"لا تتوفر أي شبكة Wi-Fi"</string> <string name="quick_settings_wifi_secondary_label_transient" msgid="7501659015509357887">"جارٍ التفعيل…"</string> - <string name="quick_settings_cast_title" msgid="3033553249449938182">"الإرسال"</string> + <string name="quick_settings_cast_title" msgid="3033553249449938182">"البث"</string> <string name="quick_settings_casting" msgid="1435880708719268055">"جارٍ الإرسال"</string> <string name="quick_settings_cast_device_default_name" msgid="6988469571141331700">"جهاز لا يحمل اسمًا"</string> <string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"لا يتوفر أي جهاز"</string> @@ -366,10 +368,8 @@ <string name="thermal" msgid="6758074791325414831">"الأداء الحراري"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"وضع \"التصفح بيد واحدة\""</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"سماعات الأذن الطبية"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"متّصلة حاليًا"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"غير متّصلة"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"سماعات الأذن الطبية"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"إقران جهاز جديد"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"انقر لإقران جهاز جديد"</string> @@ -1324,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"اختصارات طلبات البحث"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"رمز التصغير"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"رمز التوسيع"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"أو"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"الإضاءة الخلفية للوحة المفاتيح"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"مستوى الإضاءة: %1$d من %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"إدارة المنزل آليًّا"</string> diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml index 00d673ea7231..dd7102382983 100644 --- a/packages/SystemUI/res/values-as/strings.xml +++ b/packages/SystemUI/res/values-as/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"কাইলৈ পুনৰ স্বয়ংক্ৰিয়ভাৱে অন কৰক"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Quick Share আৰু Find My Deviceৰ দৰে সুবিধাসমূহে ব্লুটুথ ব্যৱহাৰ কৰে"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"কাইলৈ পুৱা ব্লুটুথ অন হ’ব"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"অডিঅ’ শ্বেয়াৰ কৰা"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"অডিঅ’ শ্বেয়াৰ কৰি থকা হৈছে"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"বেটাৰী <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"অডিঅ’"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"হেডছেট"</string> @@ -366,10 +368,8 @@ <string name="thermal" msgid="6758074791325414831">"থাৰ্মেল"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"এখন হাতেৰে ব্যৱহাৰ কৰা ম’ড"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"শুনাৰ ডিভাইচ"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"সক্ৰিয় হৈ আছে"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"সংযোগ বিচ্ছিন্ন কৰা হ’ল"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"শুনাৰ ডিভাইচ"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"নতুন ডিভাইচ পেয়াৰ কৰক"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"নতুন ডিভাইচ পেয়াৰ কৰিবলৈ ক্লিক কৰক"</string> @@ -1265,8 +1265,8 @@ <string name="rear_display_bottom_sheet_confirm" msgid="1507591562761552899">"এতিয়াই স্ক্ৰীন সলনি কৰক"</string> <string name="rear_display_folded_bottom_sheet_title" msgid="3930008746560711990">"ফ’নটো আনফ’ল্ড কৰক"</string> <string name="rear_display_unfolded_bottom_sheet_title" msgid="6291111173057304055">"স্ক্ৰীন সলনি কৰিবনে?"</string> - <string name="rear_display_folded_bottom_sheet_description" msgid="6842767125783222695">"অধিক ৰিজ’লিউছনৰ বাবে, পিছফালৰ কেমেৰাটো ব্যৱহাৰ কৰক"</string> - <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"অধিক ৰিজ’লিউছনৰ বাবে, ফ’নটো লুটিয়াই দিয়ক"</string> + <string name="rear_display_folded_bottom_sheet_description" msgid="6842767125783222695">"অধিক ৰিজ’লিউশ্বনৰ বাবে, পিছফালৰ কেমেৰাটো ব্যৱহাৰ কৰক"</string> + <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"অধিক ৰিজ’লিউশ্বনৰ বাবে, ফ’নটো লুটিয়াই দিয়ক"</string> <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"জপাব পৰা ডিভাইচৰ জাপ খুলি থকা হৈছে"</string> <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"জপাব পৰা ডিভাইচৰ ওলোটাই থকা হৈছে"</string> <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"ফ’ল্ড কৰা"</string> @@ -1324,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"সন্ধানৰ শ্বৰ্টকাট"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"সংকোচন কৰাৰ চিহ্ন"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"বিস্তাৰ কৰাৰ চিহ্ন"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"অথবা"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"কীব’ৰ্ডৰ বেকলাইট"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$dৰ %1$d স্তৰ"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"ঘৰৰ সা-সৰঞ্জামৰ নিয়ন্ত্ৰণ"</string> diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml index b3c0058d8368..592bc06de2fd 100644 --- a/packages/SystemUI/res/values-az/strings.xml +++ b/packages/SystemUI/res/values-az/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Sabah avtomatik aktiv edin"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Cəld Paylaşım və Cihazın Tapılması kimi funksiyalar Bluetooth istifadə edir"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth sabah səhər aktiv ediləcək"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Audio paylaşma"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Audio paylaşılır"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> batareya"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Qulaqlıq"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Cihaz istifadəsinə necə təsir etdi?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Problem növü seçin"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Ekran qeydəalma"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"Performans"</string> + <string name="user_interface" msgid="3712869377953950887">"İstifadəçi interfeysi"</string> + <string name="thermal" msgid="6758074791325414831">"Termal"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Birəlli rejim"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Eşitmə cihazları"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktiv"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Bağlantı kəsildi"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Eşitmə cihazları"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Yeni cihaz birləşdirin"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Yeni cihaz birləşdirmək üçün klikləyin"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"qatlanmış"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"açıq"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Stilusun enerjisi: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Qələmi adapterə qoşun"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Qələm enerjisi azdır"</string> <string name="video_camera" msgid="7654002575156149298">"Videokamera"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Axtarış qısayolları"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"İkonanı yığcamlaşdırın"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"İkonanı genişləndirin"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"və ya"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Klaviatura işığı"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Səviyyə %1$d/%2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Ev nizamlayıcıları"</string> diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml index 22afb40404dd..9b27943f5484 100644 --- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml +++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatski ponovo uključi sutra"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Funkcije kao što su Quick Share i Pronađi moj uređaj koriste Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth će se uključiti sutra ujutru"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Deljenje zvuka"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Deli se zvuk"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Nivo baterije je <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Slušalice"</string> @@ -366,10 +368,8 @@ <string name="thermal" msgid="6758074791325414831">"Termalna kamera"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Režim jednom rukom"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Slušni aparati"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktivno"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Veza je prekinuta"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Slušni aparati"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Upari novi uređaj"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kliknite da biste uparili nov uređaj"</string> @@ -1324,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Prečice pretrage"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona za skupljanje"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona za proširivanje"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ili"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Pozadinsko osvetljenje tastature"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"%1$d. nivo od %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Kontrole za dom"</string> diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml index 4fe7c5a0d09f..ca763cfac724 100644 --- a/packages/SystemUI/res/values-be/strings.xml +++ b/packages/SystemUI/res/values-be/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Аўтаматычнае ўключэнне заўтра"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Bluetooth выкарыстоўваецца такімі функцыямі і сэрвісамі, як Хуткае абагульванне і Знайсці прыладу"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth уключыцца заўтра раніцай"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Абагульванне аўдыя"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Ідзе абагульванне аўдыя"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Узровень зараду: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Гук"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Гарнітура"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"З чым была звязана праблема, якая вам сустрэлася?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Выберыце тып праблемы"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Запіс экрана"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"Прадукцыйнасць"</string> + <string name="user_interface" msgid="3712869377953950887">"Карыстальніцкі інтэрфейс"</string> + <string name="thermal" msgid="6758074791325414831">"Тэрмальныя паказчыкі"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Рэжым кіравання адной рукой"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Слыхавыя апараты"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Актыўныя"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Адключаны"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Слыхавыя апараты"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Спалучыць новую прыладу"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Націсніце, каб спалучыць новую прыладу"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"складзена"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"раскладзена"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Зарад акумулятара пяра – <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Падключыце пяро да зараднай прылады"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Нізкі ўзровень зараду пяра"</string> <string name="video_camera" msgid="7654002575156149298">"Відэакамера"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Пошук спалучэнняў клавіш"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Значок \"Згарнуць\""</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Значок \"Разгарнуць\""</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"або"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Падсветка клавіятуры"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Узровень %1$d з %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Кіраванне домам"</string> diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml index 2cd65db11d54..d50b11605743 100644 --- a/packages/SystemUI/res/values-bg/strings.xml +++ b/packages/SystemUI/res/values-bg/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Автоматично включване отново утре"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Функции като „Бързо споделяне“ и „Намиране на устройството ми“ използват Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth ще се включи утре сутрин"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Споделяне на звука"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Звукът се споделя"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Батерия: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудио"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Слушалки"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"С какво имахте проблем?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Изберете тип проблем"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Запис на екрана"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"Ефективност"</string> + <string name="user_interface" msgid="3712869377953950887">"Потребителски интерфейс"</string> + <string name="thermal" msgid="6758074791325414831">"Термално"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Режим за работа с една ръка"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Слухови апарати"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Активно"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Няма връзка"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Слухови апарати"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Сдвояване на ново устройство"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Кликнете за сдвояване на ново устройство"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"затворено"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"отворено"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Батерия на писалката: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Свържете писалката към зарядно устройство"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Батерията на писалката е изтощена"</string> <string name="video_camera" msgid="7654002575156149298">"Видеокамера"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Търсете клавишни комбинации"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Икона за свиване"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Икона за разгъване"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"или"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Подсветка на клавиатурата"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Ниво %1$d от %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Контроли за дома"</string> diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml index f8e350a3d6ad..bb41b1b14b29 100644 --- a/packages/SystemUI/res/values-bn/strings.xml +++ b/packages/SystemUI/res/values-bn/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"আগামীকাল আবার অটোমেটিক চালু হবে"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"দ্রুত শেয়ার ও Find My Device-এর মতো ফিচার ব্লুটুথ ব্যবহার করে"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"ব্লুটুথ আগামীকাল সকালে চালু হয়ে যাবে"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"অডিও শেয়ারিং"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"অডিও শেয়ার করা হচ্ছে"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"চার্জ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"অডিও"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"হেডসেট"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"ডিভাইস ব্যবহার করার সময় কোথায় অসুবিধা হয়েছিল?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"সমস্যার প্রকার বেছে নিন"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"স্ক্রিন রেকর্ড"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"পারফর্ম্যান্স"</string> + <string name="user_interface" msgid="3712869377953950887">"ইউজার ইন্টারফেস"</string> + <string name="thermal" msgid="6758074791325414831">"থার্মাল"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"এক হাতে ব্যবহার করার মোড"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"হিয়ারিং ডিভাইস"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"অ্যাক্টিভ"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"ডিসকানেক্ট হয়ে গেছে"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"হিয়ারিং ডিভাইস"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"নতুন ডিভাইস পেয়ার করুন"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"নতুন ডিভাইস পেয়ার করতে ক্লিক করুন"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"ফোল্ড করা রয়েছে"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"ফোল্ড করা নেই"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"স্টাইলাস ব্যাটারি <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"কোনও চার্জারের সাথে আপনার স্টাইলাস কানেক্ট করুন"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"স্টাইলাস ব্যাটারিতে চার্জ কম আছে"</string> <string name="video_camera" msgid="7654002575156149298">"ভিডিও ক্যামেরা"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"সার্চ শর্টকাট"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"আইকন আড়াল করুন"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"আইকন বড় করুন"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"অথবা"</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-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml index 60c936e04d18..4916d80f5245 100644 --- a/packages/SystemUI/res/values-bs/strings.xml +++ b/packages/SystemUI/res/values-bs/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatski uključi ponovo sutra"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Funkcije kao što su Quick Share i Pronađi moj uređaj koriste Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth će se uključiti sutra ujutro"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Dijeljenje zvuka"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Dijeljenje zvuka"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> baterije"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Zvuk"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Slušalice"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Koji dio uređaja je imao problem?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Odaberite vrstu problema"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Snimanje ekrana"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"Performanse"</string> + <string name="user_interface" msgid="3712869377953950887">"Korisnički interfejs"</string> + <string name="thermal" msgid="6758074791325414831">"Termalno"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Način rada jednom rukom"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Slušni aparati"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktivno"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Veza je prekinuta"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Slušni aparati"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Uparite novi uređaj"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kliknite da uparite novi uređaj"</string> @@ -1201,7 +1198,7 @@ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"WiFi se trenutno ne može automatski povezati"</string> <string name="see_all_networks" msgid="3773666844913168122">"Prikaži sve"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Da promijenite mrežu, isključite ethernet"</string> - <string name="wifi_scan_notify_message" msgid="3753839537448621794">"Radi poboljšanja iskustva s uređajem aplikacije i usluge i dalje mogu tražiti WiFi mreže bilo kada, čak i kada je WiFi isključen. Ovo možete promijeniti u Postavkama traženja WiFi mreže. "<annotation id="link">"Promijeni"</annotation></string> + <string name="wifi_scan_notify_message" msgid="3753839537448621794">"Radi poboljšanja iskustva s uređajem aplikacije i usluge i dalje mogu tražiti WiFi mreže bilo kada, čak i kada je WiFi isključen. Ovo možete promijeniti u postavkama traženja WiFi-ja. "<annotation id="link">"Promijeni"</annotation></string> <string name="turn_off_airplane_mode" msgid="8425587763226548579">"Isključi način rada u avionu"</string> <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> želi dodati sljedeću karticu u Brze postavke"</string> <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Dodaj karticu"</string> @@ -1210,7 +1207,7 @@ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# aplikacija je aktivna}one{# aplikacija je aktivna}few{# aplikacije su aktivne}other{# aplikacija je aktivno}}"</string> <string name="fgs_dot_content_description" msgid="2865071539464777240">"Nove informacije"</string> <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Aktivne aplikacije"</string> - <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Ove aplikacije su aktivne i pokrenute, čak i kada ih ne koristite. Ovim se poboljšava njihova funkcionalnost, ali može uticati i na vijek trajanja baterije."</string> + <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Ove aplikacije su aktivne i pokrenute, čak i kada ih ne koristite. Ovim se poboljšava njihova funkcionalnost, ali to može uticati i na vijek trajanja baterije."</string> <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Zaustavi"</string> <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Zaustavljeno"</string> <string name="clipboard_edit_text_done" msgid="4551887727694022409">"Gotovo"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"sklopljeno"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"otklopljeno"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Baterija pisaljke <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Priključite pisaljku na punjač"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Baterija pisaljke je slaba"</string> <string name="video_camera" msgid="7654002575156149298">"Video kamera"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Prečica pretraživanja"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona sužavanja"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona proširivanja"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ili"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Pozadinsko osvjetljenje tastature"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"%1$d. nivo od %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Kontrole za dom"</string> diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml index a82d6f10a7d8..5afad1b93805 100644 --- a/packages/SystemUI/res/values-ca/strings.xml +++ b/packages/SystemUI/res/values-ca/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Torna\'l a activar automàticament demà"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Les funcions com Quick Share i Troba el meu dispositiu utilitzen el Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"El Bluetooth s\'activarà demà al matí"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Compartició d\'àudio"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"S\'està compartint l\'àudio"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> de bateria"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Àudio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Auriculars"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"L\'experiència amb el dispositiu s\'ha vist afectada?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Selecciona el tipus de problema"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Gravació de pantalla"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"Rendiment"</string> + <string name="user_interface" msgid="3712869377953950887">"Interfície d\'usuari"</string> + <string name="thermal" msgid="6758074791325414831">"Tèrmic"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Mode d\'una mà"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Audiòfons"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Actiu"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Desconnectat"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Audiòfons"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Vincula un dispositiu nou"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Fes clic per vincular un dispositiu nou"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"plegat"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"desplegat"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Bateria del llapis òptic: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Connecta el llapis òptic a un carregador"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Bateria del llapis òptic baixa"</string> <string name="video_camera" msgid="7654002575156149298">"Càmera de vídeo"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Dreceres de cerca"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Replega la icona"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Desplega la icona"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"o"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Retroil·luminació del teclat"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Nivell %1$d de %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Controls de la llar"</string> diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml index 9b540ff06f63..b5293c0057a9 100644 --- a/packages/SystemUI/res/values-cs/strings.xml +++ b/packages/SystemUI/res/values-cs/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Zítra znovu automaticky zapnout"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Bluetooth využívají funkce jako Quick Share a Najdi moje zařízení."</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth se zapne zítra ráno."</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Sdílení zvuku"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Sdílení zvuku"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Baterie: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Zvuk"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Sluchátka"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Co v zařízení bylo ovlivněno?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Vyberte druh problém"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Záznam obrazovky"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"Výkon"</string> + <string name="user_interface" msgid="3712869377953950887">"Uživatelské rozhraní"</string> + <string name="thermal" msgid="6758074791325414831">"Termovize"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Režim jedné ruky"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Naslouchátka"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktivní"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Odpojeno"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Naslouchátka"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Spárovat nové zařízení"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kliknutím spárujete nové zařízení"</string> @@ -923,7 +920,7 @@ <string name="mobile_data" msgid="4564407557775397216">"Mobilní data"</string> <string name="mobile_data_text_format" msgid="6806501540022589786">"<xliff:g id="ID_1">%1$s</xliff:g> – <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="mobile_carrier_text_format" msgid="8912204177152950766">"<xliff:g id="CARRIER_NAME">%1$s</xliff:g>, <xliff:g id="MOBILE_DATA_TYPE">%2$s</xliff:g>"</string> - <string name="wifi_is_off" msgid="5389597396308001471">"Wi-Fi je vypnuta"</string> + <string name="wifi_is_off" msgid="5389597396308001471">"Wi-Fi je vypnutá"</string> <string name="bt_is_off" msgid="7436344904889461591">"Bluetooth je vypnuto"</string> <string name="dnd_is_off" msgid="3185706903793094463">"Režim Nerušit je vypnut"</string> <string name="dnd_is_on" msgid="7009368176361546279">"Režim Nerušit je zapnutý"</string> @@ -933,7 +930,7 @@ <string name="running_foreground_services_title" msgid="5137313173431186685">"Aplikace běžící na pozadí"</string> <string name="running_foreground_services_msg" msgid="3009459259222695385">"Klepnutím zobrazíte podrobnosti o využití baterie a dat"</string> <string name="mobile_data_disable_title" msgid="5366476131671617790">"Vypnout mobilní data?"</string> - <string name="mobile_data_disable_message" msgid="8604966027899770415">"Prostřednictvím <xliff:g id="CARRIER">%s</xliff:g> nebudete moci používat data ani internet. Internet bude dostupný pouze přes Wi-Fi."</string> + <string name="mobile_data_disable_message" msgid="8604966027899770415">"Prostřednictvím operátora <xliff:g id="CARRIER">%s</xliff:g> nebudete moct používat data ani internet. Internet bude dostupný pouze přes Wi-Fi."</string> <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"vašeho operátora"</string> <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"Přepnout zpět na operátora <xliff:g id="CARRIER">%s</xliff:g>?"</string> <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"Mobilní data se nebudou automaticky přepínat podle dostupnosti"</string> @@ -1211,7 +1208,7 @@ <string name="fgs_dot_content_description" msgid="2865071539464777240">"Nové informace"</string> <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Aktivní aplikace"</string> <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Tyto aplikace jsou spuštěné a aktivní, i když je nepoužíváte. Zlepšuje to jejich funkčnost, ale může to mít dopad na výdrž baterie."</string> - <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Konec"</string> + <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Zastavit"</string> <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Zastaveno"</string> <string name="clipboard_edit_text_done" msgid="4551887727694022409">"Hotovo"</string> <string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"Zkopírováno"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"složené"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"rozložené"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Baterie dotykového pera <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Připojte dotykové pero k nabíječce"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Slabá baterie dotykového pera"</string> <string name="video_camera" msgid="7654002575156149298">"Videokamera"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Vyhledat zkratky"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona sbalení"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona rozbalení"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"nebo"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Podsvícení klávesnice"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Úroveň %1$d z %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Ovládání domácnosti"</string> diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml index 57353f7f785d..6e2323be3921 100644 --- a/packages/SystemUI/res/values-da/strings.xml +++ b/packages/SystemUI/res/values-da/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Aktivér automatisk igen i morgen"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Funktioner som f.eks. Quick Share og Find min enhed anvender Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth aktiveres i morgen tidlig"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Lyddeling"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Deler lyd"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> batteri"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Lyd"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Hvilken del af din enhedsoplevelse blev påvirket?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Vælg problemtype"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Skærmoptagelse"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"Ydeevne"</string> + <string name="user_interface" msgid="3712869377953950887">"Brugerflade"</string> + <string name="thermal" msgid="6758074791325414831">"Termisk"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Enhåndstilstand"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Høreapparater"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktivt"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Afbrudt"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Høreapparater"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Par ny enhed"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Klik for at parre en ny enhed"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"foldet"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"foldet ud"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Batteriniveau på styluspen: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Slut din styluspen til en oplader"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Lavt batteriniveau på styluspen"</string> <string name="video_camera" msgid="7654002575156149298">"Videokamera"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Genveje til søgning"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikon for Skjul"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikon for Udvid"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"eller"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Tastaturets baggrundslys"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Niveau %1$d af %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Hjemmestyring"</string> diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml index e7fa9b18b5e6..bf28a88250a6 100644 --- a/packages/SystemUI/res/values-de/strings.xml +++ b/packages/SystemUI/res/values-de/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Morgen automatisch wieder aktivieren"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Funktionen wie „Quick Share“ und „Mein Gerät finden“ verwenden Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth wird morgen früh aktiviert"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Audiofreigabe"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Audioinhalte werden freigegeben"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Akkustand: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Welche Bereiche des Geräts waren betroffen?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Art des Problems auswählen"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Bildschirmaufnahme"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"Leistung"</string> + <string name="user_interface" msgid="3712869377953950887">"Benutzeroberfläche"</string> + <string name="thermal" msgid="6758074791325414831">"Überhitzung"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Einhandmodus"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Hörgeräte"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktiv"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Nicht verbunden"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Hörgeräte"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Neues Gerät koppeln"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Klicken, um neues Gerät zu koppeln"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"zugeklappt"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"aufgeklappt"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Akkustand des Eingabestifts: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Schließe deinen Eingabestift an ein Ladegerät an"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Stylus-Akkustand niedrig"</string> <string name="video_camera" msgid="7654002575156149298">"Videokamera"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Tastenkürzel suchen"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Symbol „Minimieren“"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Symbol „Maximieren“"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"oder"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Tastaturbeleuchtung"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Level %1$d von %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Smart-Home-Steuerung"</string> diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml index 23d704da4f3b..3dd5fc0f1c8c 100644 --- a/packages/SystemUI/res/values-el/strings.xml +++ b/packages/SystemUI/res/values-el/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Αυτόματη ενεργοποίηση ξανά αύριο"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Λειτουργίες όπως το Quick Share και η Εύρεση συσκευής χρησιμοποιούν το Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Το Bluetooth θα ενεργοποιηθεί αύριο το πρωί"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Κοινή χρήση ήχου"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Κοινή χρήση ήχου"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Μπαταρία <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Ήχος"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Ακουστικά"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Ποιο κομμάτι της εμπειρίας συσκευής επηρεάστηκε;"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Επιλογή τύπου προβλήματος"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Εγγραφή οθόνης"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"Απόδοση"</string> + <string name="user_interface" msgid="3712869377953950887">"Διεπαφή χρήστη"</string> + <string name="thermal" msgid="6758074791325414831">"Θερμικό"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Λειτουργία ενός χεριού"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Συσκευές ακοής"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Ενεργά"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Αποσυνδεδεμένα"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Συσκευές ακοής"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Σύζευξη νέας συσκευής"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Κάντε κλικ για σύζευξη νέας συσκευής"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"διπλωμένη"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"ξεδιπλωμένη"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Μπαταρία γραφίδας <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Συνδέστε τη γραφίδα σε έναν φορτιστή"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Χαμηλή στάθμη μπαταρίας γραφίδας"</string> <string name="video_camera" msgid="7654002575156149298">"Βιντεοκάμερα"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Συντομεύσεις αναζήτησης"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Εικονίδιο σύμπτυξης"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Εικονίδιο ανάπτυξης"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ή"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Οπίσθιος φωτισμός πληκτρολογίου"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Επίπεδο %1$d από %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Οικιακοί έλεγχοι"</string> diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml index d71ff1cc42be..e830f3c2a8cd 100644 --- a/packages/SystemUI/res/values-en-rAU/strings.xml +++ b/packages/SystemUI/res/values-en-rAU/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatically turn on again tomorrow"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Features like Quick Share and Find My Device use Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth will turn on tomorrow morning"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Audio sharing"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Sharing audio"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> battery"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string> @@ -366,10 +368,8 @@ <string name="thermal" msgid="6758074791325414831">"Thermal"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"One-handed mode"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Hearing devices"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Active"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Disconnected"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Hearing devices"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Pair new device"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Click to pair new device"</string> @@ -1324,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Search shortcuts"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Collapse icon"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Expand icon"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"or"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Keyboard backlight"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Level %1$d of %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Home controls"</string> diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml index 44e9d25a4d7f..046ce28b6488 100644 --- a/packages/SystemUI/res/values-en-rCA/strings.xml +++ b/packages/SystemUI/res/values-en-rCA/strings.xml @@ -281,8 +281,8 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatically turn on again tomorrow"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Features like Quick Share and Find My Device use Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth will turn on tomorrow morning"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Audio Sharing"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Sharing Audio"</string> + <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Share audio"</string> + <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Sharing audio"</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> battery"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string> @@ -1322,8 +1322,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Search shortcuts"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Collapse icon"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Expand icon"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"or"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Keyboard backlight"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Level %1$d of %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Home Controls"</string> diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml index d71ff1cc42be..e830f3c2a8cd 100644 --- a/packages/SystemUI/res/values-en-rGB/strings.xml +++ b/packages/SystemUI/res/values-en-rGB/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatically turn on again tomorrow"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Features like Quick Share and Find My Device use Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth will turn on tomorrow morning"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Audio sharing"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Sharing audio"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> battery"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string> @@ -366,10 +368,8 @@ <string name="thermal" msgid="6758074791325414831">"Thermal"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"One-handed mode"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Hearing devices"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Active"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Disconnected"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Hearing devices"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Pair new device"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Click to pair new device"</string> @@ -1324,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Search shortcuts"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Collapse icon"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Expand icon"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"or"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Keyboard backlight"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Level %1$d of %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Home controls"</string> diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml index d71ff1cc42be..e830f3c2a8cd 100644 --- a/packages/SystemUI/res/values-en-rIN/strings.xml +++ b/packages/SystemUI/res/values-en-rIN/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatically turn on again tomorrow"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Features like Quick Share and Find My Device use Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth will turn on tomorrow morning"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Audio sharing"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Sharing audio"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> battery"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string> @@ -366,10 +368,8 @@ <string name="thermal" msgid="6758074791325414831">"Thermal"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"One-handed mode"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Hearing devices"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Active"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Disconnected"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Hearing devices"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Pair new device"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Click to pair new device"</string> @@ -1324,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Search shortcuts"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Collapse icon"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Expand icon"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"or"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Keyboard backlight"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Level %1$d of %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Home controls"</string> diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml index c2f709878881..07907a640da4 100644 --- a/packages/SystemUI/res/values-en-rXC/strings.xml +++ b/packages/SystemUI/res/values-en-rXC/strings.xml @@ -281,8 +281,8 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatically turn on again tomorrow"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Features like Quick Share and Find My Device use Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth will turn on tomorrow morning"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Audio Sharing"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Sharing Audio"</string> + <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Share audio"</string> + <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Sharing audio"</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> battery"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string> @@ -1322,8 +1322,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Search shortcuts"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Collapse icon"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Expand icon"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"or"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Keyboard backlight"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Level %1$d of %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Home Controls"</string> diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml index fd800ad30ee6..c622ebec0c84 100644 --- a/packages/SystemUI/res/values-es-rUS/strings.xml +++ b/packages/SystemUI/res/values-es-rUS/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Activar automáticamente mañana"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Las funciones como Quick Share y Encontrar mi dispositivo usan Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"El Bluetooth se activará mañana a la mañana"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Uso compartido de audio"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Compartiendo audio"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> de batería"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Auriculares"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"¿Qué parte de tu exp. del disp. se vio afectada?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Seleccionar tipo de problema"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Grabadora de pant."</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"Rendimiento"</string> + <string name="user_interface" msgid="3712869377953950887">"Interfaz de usuario"</string> + <string name="thermal" msgid="6758074791325414831">"Térmico"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modo una mano"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Dispositivos auditivos"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Activos"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Desconectados"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Dispositivos auditivos"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Vincular dispositivo nuevo"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Haz clic para vincular un dispositivo nuevo"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"plegado"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"desplegado"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Batería de la pluma stylus: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Conecta tu pluma stylus a un cargador"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"La pluma stylus tiene poca batería"</string> <string name="video_camera" msgid="7654002575156149298">"Videocámara"</string> @@ -1328,10 +1324,9 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Buscar combinaciones de teclas"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ícono de contraer"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ícono de expandir"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"o"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Retroiluminación del teclado"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Nivel %1$d de %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Controles de la casa"</string> - <string name="home_controls_dream_description" msgid="4644150952104035789">"Accede rápidamente a controles de la casa como prot. de pantalla"</string> + <string name="home_controls_dream_description" msgid="4644150952104035789">"Usa rápidamente los controles de la casa como protector de pantalla"</string> </resources> diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml index 5bcf582afb13..c021b456e2fc 100644 --- a/packages/SystemUI/res/values-es/strings.xml +++ b/packages/SystemUI/res/values-es/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Volver a activar automáticamente mañana"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Las funciones como Quick Share y Encontrar mi dispositivo usan Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"El Bluetooth se activará mañana por la mañana"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Compartir audio"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Compartiendo audio"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> de batería"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Auriculares"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"¿Qué parte de tu experiencia se ha visto afectada?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Selecciona el tipo de problema"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Grabar pantalla"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"Rendimiento"</string> + <string name="user_interface" msgid="3712869377953950887">"Interfaz de usuario"</string> + <string name="thermal" msgid="6758074791325414831">"Térmico"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modo Una mano"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Audífonos"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Activos"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Desconectados"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Audífonos"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Emparejar nuevo dispositivo"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Haz clic para emparejar un nuevo dispositivo"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"plegado"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"desplegado"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Batería del lápiz óptico al <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Conecta tu lápiz óptico a un cargador"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Batería del lápiz óptico baja"</string> <string name="video_camera" msgid="7654002575156149298">"Videocámara"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Atajos de búsqueda"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Icono de contraer"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Icono de desplegar"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"o"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Retroiluminación del teclado"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Nivel %1$d de %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Controles de la casa"</string> diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml index e7d5e2f42797..003c92577d80 100644 --- a/packages/SystemUI/res/values-et/strings.xml +++ b/packages/SystemUI/res/values-et/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Lülita automaatselt homme uuesti sisse"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Sellised funktsioonid nagu Kiirjagamine ja Leia mu seade kasutavad Bluetoothi."</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth lülitub sisse homme hommikul"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Heli jagamine"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Heli jagamine"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> akut"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Heli"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Peakomplekt"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Millist seadme kasutuskogemuse osa see mõjutas?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Valige probleemi tüüp"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Ekraanisalvestus"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"Jõudlus"</string> + <string name="user_interface" msgid="3712869377953950887">"Kasutajaliides"</string> + <string name="thermal" msgid="6758074791325414831">"Soojus"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Ühekäerežiim"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Kuuldeseadmed"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Ühendatud"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Ühendus on katkestatud"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Kuuldeseadmed"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Uue seadme sidumine"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Uue seadme sidumiseks klõpsake"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"kokku volditud"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"lahti volditud"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Elektronpliiatsi aku <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Ühendage elektronpliiats laadijaga"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Elektronpliiatsi akutase on madal"</string> <string name="video_camera" msgid="7654002575156149298">"Videokaamera"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Otsingu otseteed"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ahendamisikoon"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Laiendamisikoon"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"või"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Klaviatuuri taustavalgustus"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Tase %1$d/%2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Kodu juhtelemendid"</string> diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml index 5e4c5acdfc12..66b79b80fcbf 100644 --- a/packages/SystemUI/res/values-eu/strings.xml +++ b/packages/SystemUI/res/values-eu/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Aktibatu automatikoki berriro bihar"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Quick Share, Bilatu nire gailua eta beste eginbide batzuek Bluetootha erabiltzen dute"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bihar goizean aktibatuko da Bluetootha"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Audioa partekatzea"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Audioa partekatzen"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Bateria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audioa"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Entzungailua"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Gailuaren erabileraren zer alderdiri eragin dio?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Hautatu arazo mota"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Pantaila-grabaketa"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"Errendimendua"</string> + <string name="user_interface" msgid="3712869377953950887">"Erabiltzaile-interfazea"</string> + <string name="thermal" msgid="6758074791325414831">"Termikoa"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Esku bakarreko modua"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Entzumen-gailuak"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktibo"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Deskonektatuta"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Entzumen-gailuak"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Parekatu beste gailu bat"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Egin klik beste gailu bat parekatzeko"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"tolestuta"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"tolestu gabe"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Arkatzaren bateria: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Konektatu arkatza kargagailu batera"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Arkatzak bateria gutxi du"</string> <string name="video_camera" msgid="7654002575156149298">"Bideokamera"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Bilatu lasterbideak"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Tolesteko ikonoa"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Zabaltzeko ikonoa"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"edo"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Teklatuaren hondoko argia"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"%1$d/%2$d maila"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Etxeko gailuen kontrola"</string> diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml index ad24c1b45a24..cdbb6851acc0 100644 --- a/packages/SystemUI/res/values-fa/strings.xml +++ b/packages/SystemUI/res/values-fa/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"فردا دوباره بهطور خودکار روشن شود"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"ویژگیهایی مثل «همرسانی سریع» و «پیدا کردن دستگاهم» از بلوتوث استفاده میکنند"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"بلوتوث فردا صبح روشن خواهد شد"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"اشتراک صدا"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"درحال اشتراکگذاری صدا"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"شارژ باتری <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"صوت"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"هدست"</string> @@ -361,24 +363,19 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"کدام بخش تجربه استفاده از دستگاه تحتتأثیر قرار گرفت؟"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"انتخاب نوع مشکل"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"ضبط صفحهنمایش"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"عملکرد"</string> + <string name="user_interface" msgid="3712869377953950887">"میانای کاربر"</string> + <string name="thermal" msgid="6758074791325414831">"حرارتی"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"حالت یکدستی"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"سمعک"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"فعال"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"اتصال قطع شد"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"سمعک"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"جفت کردن دستگاه جدید"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"برای جفت کردن دستگاه جدید، کلیک کنید"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"پیشتنظیم بهروزرسانی نشد"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"پیشتنظیم"</string> - <string name="live_caption_title" msgid="8916875614623730005">"زیرنویس زنده"</string> + <string name="live_caption_title" msgid="8916875614623730005">"زیرنویس ناشنوایان زنده"</string> <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> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"تاشده"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"تانشده"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"شارژ باتری قلم <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"قلم را به شارژر وصل کنید"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"باتری قلم ضعیف است"</string> <string name="video_camera" msgid="7654002575156149298">"دوربین ویدیویی"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"جستجوی میانبرها"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"نماد جمع کردن"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"نماد ازهم بازکردن"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"یا"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"نور پسزمینه صفحهکلید"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"سطح %1$d از %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"کنترل خانه هوشمند"</string> diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml index 0fed3d546eae..bea39cb6e704 100644 --- a/packages/SystemUI/res/values-fi/strings.xml +++ b/packages/SystemUI/res/values-fi/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Laita automaattisesti päälle taas huomenna"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Quick Share, Paikanna laite ja tietyt muut ominaisuudet käyttävät Bluetoothia"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth menee päälle huomisaamuna"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Audionjako"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Audiota jaetaan"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Akun taso <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Ääni"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Mitä osaa käyttökokemuksesta ongelma koski?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Valitse ongelman tyyppi"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Näytön tallentaja"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"Suorituskyky"</string> + <string name="user_interface" msgid="3712869377953950887">"Käyttöliittymä"</string> + <string name="thermal" msgid="6758074791325414831">"Lämpökamera"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Yhden käden moodi"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Kuulolaitteet"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktiivinen"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Yhteys katkaistu"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Kuulolaitteet"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Muodosta uusi laitepari"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Muodosta uusi laitepari klikkaamalla"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"taitettu"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"taittamaton"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Näyttökynän akku <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Yhdistä näyttökynä laturiin"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Näyttökynän akku vähissä"</string> <string name="video_camera" msgid="7654002575156149298">"Videokamera"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Pikahaut"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Tiivistyskuvake"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Laajennuskuvake"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"tai"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Näppämistön taustavalo"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Taso %1$d/%2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Kodin ohjaus"</string> diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml index 95c788aab6e7..95b25936f8a4 100644 --- a/packages/SystemUI/res/values-fr-rCA/strings.xml +++ b/packages/SystemUI/res/values-fr-rCA/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Activer le Bluetooth automatiquement demain"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Les fonctionnalités comme Partage rapide et Localiser mon appareil utilisent le Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Le Bluetooth s\'activera demain matin"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Partage audio"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Partage de l\'audio en cours…"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Pile : <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Écouteurs"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Quelle composante de l\'appareil a été affectée?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Sélectionner un type"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Enregistrement écran"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"Performance"</string> + <string name="user_interface" msgid="3712869377953950887">"Interface utilisateur"</string> + <string name="thermal" msgid="6758074791325414831">"Thermique"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Mode Une main"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Appareils auditifs"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Actives"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Déconnectées"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Appareils auditifs"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Associer un nouvel appareil"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Cliquez ici pour associer un nouvel appareil"</string> @@ -1209,8 +1206,8 @@ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Choisir l\'utilisateur"</string> <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# application est active}one{# application est active}many{# d\'applications sont actives}other{# applications sont actives}}"</string> <string name="fgs_dot_content_description" msgid="2865071539464777240">"Nouvelle information"</string> - <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Applications actives"</string> - <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Ces applications sont actives et s\'exécutent même lorsque vous ne les utilisez pas. Cela améliore leur fonctionnalité, mais peut également affecter l\'autonomie de la pile."</string> + <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Applis actives"</string> + <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Ces applis sont actives et s\'exécutent même lorsque vous ne les utilisez pas. Cela améliore leur fonctionnalité, mais peut également affecter l\'autonomie de la pile."</string> <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Arrêter"</string> <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Arrêtée"</string> <string name="clipboard_edit_text_done" msgid="4551887727694022409">"OK"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"plié"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"déplié"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Pile du stylet <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Connectez votre stylet à un chargeur"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Pile du stylet faible"</string> <string name="video_camera" msgid="7654002575156149298">"Caméra"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Recherchez des raccourcis"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Icône Réduire"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Icône Développer"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ou"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Rétroéclairage du clavier"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Niveau %1$d de %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Domotique"</string> diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml index b7abd0ec5a5b..1b8ddc102220 100644 --- a/packages/SystemUI/res/values-fr/strings.xml +++ b/packages/SystemUI/res/values-fr/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Réactiver automatiquement demain"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Certaines fonctionnalités, telles que Quick Share et Localiser mon appareil, utilisent le Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Le Bluetooth sera activé demain matin"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Partage audio"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Audio partagé"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> de batterie"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Casque"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Quel problème avez-vous rencontré avec votre appareil ?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Sélectionnez un type de problème"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Enregistrement de l\'écran"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"Performances"</string> + <string name="user_interface" msgid="3712869377953950887">"Interface utilisateur"</string> + <string name="thermal" msgid="6758074791325414831">"Thermique"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Mode une main"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Appareils auditifs"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Actifs"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Déconnectés"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Appareils auditifs"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Associer un nouvel appareil"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Cliquer pour associer un nouvel appareil"</string> @@ -1212,7 +1209,7 @@ <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Applis actives"</string> <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Ces applis sont actives et s\'exécutent même lorsque vous ne les utilisez pas. Cela améliore leur fonctionnement, mais peut également affecter l\'autonomie de la batterie."</string> <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Arrêter"</string> - <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Arrêtée"</string> + <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Arrêté"</string> <string name="clipboard_edit_text_done" msgid="4551887727694022409">"OK"</string> <string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"Copié"</string> <string name="clipboard_edit_source" msgid="9156488177277788029">"De <xliff:g id="APPNAME">%1$s</xliff:g>"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"plié"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"déplié"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Batterie du stylet à <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Connectez votre stylet à un chargeur"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"La batterie du stylet est faible"</string> <string name="video_camera" msgid="7654002575156149298">"Caméra"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Raccourcis de recherche"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Icône Réduire"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Icône Développer"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ou"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Rétroéclairage du clavier"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Niveau %1$d sur %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Contrôle de la maison"</string> diff --git a/packages/SystemUI/res/values-fr/tiles_states_strings.xml b/packages/SystemUI/res/values-fr/tiles_states_strings.xml index 23c124cce37a..fcdd9f0e6e3a 100644 --- a/packages/SystemUI/res/values-fr/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-fr/tiles_states_strings.xml @@ -58,8 +58,8 @@ </string-array> <string-array name="tile_states_flashlight"> <item msgid="3465257127433353857">"Indisponible"</item> - <item msgid="5044688398303285224">"Désactivée"</item> - <item msgid="8527389108867454098">"Activée"</item> + <item msgid="5044688398303285224">"Désactivé"</item> + <item msgid="8527389108867454098">"Activé"</item> </string-array> <string-array name="tile_states_rotation"> <item msgid="4578491772376121579">"Indisponible"</item> diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml index 55dd05a58bb3..1d0f4c476f1e 100644 --- a/packages/SystemUI/res/values-gl/strings.xml +++ b/packages/SystemUI/res/values-gl/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Volver activar automaticamente mañá"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"As funcións como Quick Share e Localizar o meu dispositivo utilizan o Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"O Bluetooth activarase mañá á mañá"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Audio compartido"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Compartindo audio"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> de batería"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Auriculares"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Cal foi o problema na experiencia co dispositivo?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Selecciona o tipo de problema"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Gravación de pant."</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"Rendemento"</string> + <string name="user_interface" msgid="3712869377953950887">"Interface de usuario"</string> + <string name="thermal" msgid="6758074791325414831">"Térmico"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modo dunha soa man"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Dispositivos auditivos"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Activos"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Desconectados"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Dispositivos auditivos"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Vincular dispositivo novo"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Fai clic para vincular un novo dispositivo"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"dispositivo pregado"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"dispositivo despregado"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Batería do lapis óptico: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Conecta o lapis óptico a un cargador"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"O lapis óptico ten pouca batería"</string> <string name="video_camera" msgid="7654002575156149298">"Videocámara"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Atallos de busca"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Icona de contraer"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Icona de despregar"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ou"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Retroiluminación do teclado"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Nivel %1$d de %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Controis domóticos"</string> diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml index 425a76869587..7a4cfd1c91ce 100644 --- a/packages/SystemUI/res/values-gu/strings.xml +++ b/packages/SystemUI/res/values-gu/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"આવતીકાલે ફરીથી ઑટોમૅટિક રીતે ચાલુ કરો"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"ક્વિક શેર અને Find My Device જેવી સુવિધાઓ બ્લૂટૂથનો ઉપયોગ કરે છે"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"બ્લૂટૂથ આવતીકાલે સવારે ચાલુ થશે"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"ઑડિયો શેરિંગ"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"ઑડિયો શેર કરી રહ્યાં છીએ"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> બૅટરી"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ઑડિયો"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"હૅડસેટ"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"ડિવાઇસ સંબંધી તમારા અનુભવના કયા ભાગને અસર થઈ હતી?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"સમસ્યાનો પ્રકાર પસંદ કરો"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"સ્ક્રીન રેકોર્ડ કરો"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"પર્ફોર્મન્સ"</string> + <string name="user_interface" msgid="3712869377953950887">"યૂઝર ઇન્ટરફેસ"</string> + <string name="thermal" msgid="6758074791325414831">"થર્મલ"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"એક-હાથે વાપરો મોડ"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"સાંભળવામાં મદદ આપતા ડિવાઇસ"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"સક્રિય"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"ડિસ્કનેક્ટેડ છે"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"સાંભળવામાં મદદ આપતા ડિવાઇસ"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"નવા ડિવાઇસ સાથે જોડાણ કરો"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"નવા ડિવાઇસ સાથે જોડાણ કરવા માટે ક્લિક કરો"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"ફોલ્ડ કરેલું"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"અનફોલ્ડ કરેલું"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"સ્ટાઇલસની બૅટરી <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"તમારા સ્ટાઇલસને ચાર્જર સાથે કનેક્ટ કરો"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"સ્ટાઇલસની બૅટરીમાં ચાર્જ ઓછો છે"</string> <string name="video_camera" msgid="7654002575156149298">"વીડિયો કૅમેરા"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"શૉર્ટકટ શોધો"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"\'નાનું કરો\'નું આઇકન"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"\'મોટું કરો\'નું આઇકન"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"અથવા"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"કીબોર્ડની બૅકલાઇટ"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$dમાંથી %1$d લેવલ"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"ઘરેલું સાધનોના નિયંત્રણો"</string> diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml index 8f7b87cbc23e..896af2487564 100644 --- a/packages/SystemUI/res/values-hi/strings.xml +++ b/packages/SystemUI/res/values-hi/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"कल फिर से अपने-आप चालू हो जाए"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"क्विक शेयर और Find My Device जैसी सुविधाएं, ब्लूटूथ का इस्तेमाल करती हैं"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"ब्लूटूथ कल सुबह चालू होगा"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"ऑडियो शेयर करने की सुविधा"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"ऑडियो शेयर किया जा रहा है"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> बैटरी"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ऑडियो"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"हेडसेट"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"आपके डिवाइस की कौनसी सुविधा पर असर पड़ा था?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"समस्या का टाइप चुनें"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"स्क्रीन रिकॉर्डर"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"परफ़ॉर्मेंस"</string> + <string name="user_interface" msgid="3712869377953950887">"यूज़र इंटरफ़ेस"</string> + <string name="thermal" msgid="6758074791325414831">"थर्मल"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"वन-हैंडेड मोड"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"कान की मशीनें"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"ऐक्टिव"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"डिसकनेक्ट हो गया"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"कान की मशीनें"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"नया डिवाइस जोड़ें"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"नया डिवाइस जोड़ने के लिए क्लिक करें"</string> @@ -1112,7 +1109,7 @@ <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"<xliff:g id="COUNT">%1$d</xliff:g> डिवाइस चुने गए"</string> <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(डिसकनेक्ट हो गया)"</string> <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"स्विच नहीं किया जा सकता. फिर से कोशिश करने के लिए टैप करें."</string> - <string name="media_output_dialog_pairing_new" msgid="5098212763195577270">"किसी डिवाइस को कनेक्ट करें"</string> + <string name="media_output_dialog_pairing_new" msgid="5098212763195577270">"कोई डिवाइस कनेक्ट करें"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"इस सेशन को कास्ट करने के लिए, कृपया ऐप्लिकेशन खोलें."</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"अनजान ऐप्लिकेशन"</string> <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"कास्ट करना बंद करें"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"डिवाइस फ़ोल्ड किया गया"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"डिवाइस अनफ़ोल्ड किया गया"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"स्टाइलस की बैटरी <xliff:g id="PERCENTAGE">%s</xliff:g> है"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"अपने स्टाइलस को चार्ज करें"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"स्टाइलस की बैटरी कम है"</string> <string name="video_camera" msgid="7654002575156149298">"वीडियो कैमरा"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"सर्च शॉर्टकट"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"छोटा करने का आइकॉन"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"बड़ा करने का आइकॉन"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"या"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"कीबोर्ड की बैकलाइट"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d में से %1$d लेवल"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"होम कंट्रोल"</string> diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml index bc84c1d0bc65..acf253dd00a5 100644 --- a/packages/SystemUI/res/values-hr/strings.xml +++ b/packages/SystemUI/res/values-hr/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatski uključi ponovno sutra"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Značajke kao što su brzo dijeljenje i Pronađi moj uređaj koriste Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth će se uključiti sutra ujutro"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Zajedničko slušanje"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Zajedničko slušanje"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> baterije"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Slušalice"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Na koji dio doživljaja na uređaju to utjecalo?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Odaberite vrstu problema"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Snimanje zaslona"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"Izvedba"</string> + <string name="user_interface" msgid="3712869377953950887">"Korisničko sučelje"</string> + <string name="thermal" msgid="6758074791325414831">"Termalno"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Način rada jednom rukom"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Slušna pomagala"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktivno"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Nije povezano"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Slušna pomagala"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Uparite novi uređaj"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kliknite da biste uparili novi uređaj"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"zatvoreno"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"otvoreno"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Baterija olovke <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Priključite pisaljku na punjač"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Slaba baterija pisaljke"</string> <string name="video_camera" msgid="7654002575156149298">"Videokamera"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Prečaci za pretraživanje"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona za sažimanje"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona za proširivanje"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ili"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Pozadinsko osvjetljenje tipkovnice"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Razina %1$d od %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Upravljanje uređajima"</string> diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml index 4b36026b5f06..c73511e18677 100644 --- a/packages/SystemUI/res/values-hu/strings.xml +++ b/packages/SystemUI/res/values-hu/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatikus visszakapcsolás holnap"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Egyes funkciók (például a Quick Share és a Készülékkereső) Bluetootht használnak"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"A Bluetooth holnap reggel bekapcsol"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Hang megosztása"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Hang megosztása…"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Akkumulátor: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Hang"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string> @@ -292,7 +294,7 @@ <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> - <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Képernyővédő"</string> + <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Képernyőkímélő"</string> <string name="quick_settings_camera_label" msgid="5612076679385269339">"Hozzáférés a kamerához"</string> <string name="quick_settings_mic_label" msgid="8392773746295266375">"Mikrofonelérés"</string> <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Rendelkezésre áll"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Az eszközhasználati élmény mely része érintett?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Problématípus kiválasztása"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Képernyőrögzítés"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"Teljesítmény"</string> + <string name="user_interface" msgid="3712869377953950887">"Kezelőfelület"</string> + <string name="thermal" msgid="6758074791325414831">"Termikus"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Egykezes mód"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Hallókészülékek"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktív"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Leválasztva"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Hallókészülékek"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Új eszköz párosítása"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kattintson új eszköz párosításához"</string> @@ -601,7 +598,7 @@ <string name="screen_pinning_exit" msgid="4553787518387346893">"Alkalmazás kitűzése megszüntetve"</string> <string name="stream_voice_call" msgid="7468348170702375660">"Telefonhívás"</string> <string name="stream_system" msgid="7663148785370565134">"Rendszer"</string> - <string name="stream_ring" msgid="7550670036738697526">"Csörgetés"</string> + <string name="stream_ring" msgid="7550670036738697526">"Csörgés"</string> <string name="stream_music" msgid="2188224742361847580">"Média"</string> <string name="stream_alarm" msgid="16058075093011694">"Ébresztő"</string> <string name="stream_notification" msgid="7930294049046243939">"Értesítés"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"összehajtva"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"kihajtva"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Érintőceruza töltöttségi szintje: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Tegye töltőre az érintőceruzát"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Az érintőceruza töltöttsége alacsony"</string> <string name="video_camera" msgid="7654002575156149298">"Videokamera"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Billentyűparancsok keresése"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Összecsukás ikon"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Kibontás ikon"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"vagy"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"A billentyűzet háttérvilágítása"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Fényerő: %2$d/%1$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Otthon vezérlése"</string> diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml index d8e36ad93c04..f1462be4261a 100644 --- a/packages/SystemUI/res/values-hy/strings.xml +++ b/packages/SystemUI/res/values-hy/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Վաղը նորից ավտոմատ միացնել"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Bluetooth-ն օգտագործում են, օրինակ, Quick Share և «Գտնել իմ սարքը» գործառույթները"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth-ը կմիանա վաղն առավոտյան"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Աուդիոյի փոխանցում"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Աուդիոն փոխանցվում է"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Մարտկոցի լիցքը՝ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Աուդիո"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Ականջակալ"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Սարքի ո՞ր մասի հետ է կապված խնդիրը։"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Ընտրեք խնդրի տեսակը"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Էկրանի տեսագրում"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"Արդյունավետություն"</string> + <string name="user_interface" msgid="3712869377953950887">"Օգտատիրական ինտերֆեյս"</string> + <string name="thermal" msgid="6758074791325414831">"Ջերմատեսիլ"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Մեկ ձեռքի ռեժիմ"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Լսողական սարքեր"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Ակտիվ է"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Անջատված է"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Լսողական սարքեր"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Նոր սարքի զուգակցում"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Սեղմեք՝ նոր սարք զուգակցելու համար"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"ծալված"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"բացված"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Ստիլուսի մարտկոցի լիցքը՝ <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Ձեր ստիլուսը միացրեք լիցքավորիչի"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Ստիլուսի մարտկոցի լիցքի ցածր մակարդակ"</string> <string name="video_camera" msgid="7654002575156149298">"Տեսախցիկ"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Դյուրանցումների որոնում"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ծալել պատկերակը"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ծավալել պատկերակը"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"կամ"</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-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml index 22d0de5ecc0c..7525e9aea79a 100644 --- a/packages/SystemUI/res/values-in/strings.xml +++ b/packages/SystemUI/res/values-in/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Otomatis aktifkan lagi besok"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Fitur seperti Quick Share dan Temukan Perangkat Saya menggunakan Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth akan dinyalakan besok pagi"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Berbagi Audio"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Berbagi Audio"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Baterai <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Bagian pengalaman perangkat mana yang terpengaruh?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Pilih jenis masalah"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Perekaman layar"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"Performa"</string> + <string name="user_interface" msgid="3712869377953950887">"Antarmuka Pengguna"</string> + <string name="thermal" msgid="6758074791325414831">"Termal"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Mode satu tangan"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Alat bantu dengar"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktif"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Terputus"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Alat bantu dengar"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Sambungkan perangkat baru"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Klik untuk menyambungkan perangkat baru"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"ditutup"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"dibuka"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Baterai stilus <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Hubungkan stilus ke pengisi daya"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Baterai stilus lemah"</string> <string name="video_camera" msgid="7654002575156149298">"Kamera video"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Pintasan penelusuran"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikon ciutkan"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikon luaskan"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"atau"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Lampu latar keyboard"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Tingkat %1$d dari %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Kontrol Rumah"</string> diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml index b631c47363d4..38507900ebac 100644 --- a/packages/SystemUI/res/values-is/strings.xml +++ b/packages/SystemUI/res/values-is/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Kveikja sjálfkrafa aftur á morgun"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Eiginleikar eins og Flýtideiling og Finna tækið mitt nota Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Kveikt verður á Bluetooth í fyrramálið"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Hljóði deilt"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Deilir hljóði"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> rafhlöðuhleðsla"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Hljóð"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Höfuðtól"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Hvað í tækjaupplifuninni varð fyrir áhrifum?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Veldu gerð vandamáls"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Skjáupptaka"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"Afköst"</string> + <string name="user_interface" msgid="3712869377953950887">"Notandaviðmót"</string> + <string name="thermal" msgid="6758074791325414831">"Varmi"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Einhent stilling"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Heyrnartæki"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Virk"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Aftengd"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Heyrnartæki"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Para nýtt tæki"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Smelltu til að para nýtt tæki"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"samanbrotið"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"opið"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Pennarafhlaða <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Tengdu pennann við hleðslutæki"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Rafhlaða pennans er að tæmast"</string> <string name="video_camera" msgid="7654002575156149298">"Kvikmyndatökuvél"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Leitarflýtileiðir"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Minnka tákn"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Stækka tákn"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"eða"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Baklýsing lyklaborðs"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Stig %1$d af %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Heimastýringar"</string> diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml index 5494b165bda3..a0d54c267c7b 100644 --- a/packages/SystemUI/res/values-it/strings.xml +++ b/packages/SystemUI/res/values-it/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Riattiva automaticamente domani"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Funzionalità come Quick Share e Trova il mio dispositivo usano il Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Il Bluetooth verrà attivato domani mattina"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Condivisione audio"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Condivisione audio in corso…"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Batteria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Auricolare"</string> @@ -366,10 +368,8 @@ <string name="thermal" msgid="6758074791325414831">"Termico"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modalità a una mano"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Apparecchi acustici"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Attivi"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Disconnessi"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Protesi uditive"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Accoppia nuovo dispositivo"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Fai clic per accoppiare un nuovo dispositivo"</string> @@ -1324,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Scorciatoie per la ricerca"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Icona Comprimi"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Icona Espandi"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"oppure"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Retroilluminazione della tastiera"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Livello %1$d di %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Controlli della casa"</string> diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml index 2ad95fb93e82..f773b84a0e67 100644 --- a/packages/SystemUI/res/values-iw/strings.xml +++ b/packages/SystemUI/res/values-iw/strings.xml @@ -278,11 +278,13 @@ <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"נשמר"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ניתוק"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"הפעלה"</string> - <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"החיבור יופעל שוב אוטומטית מחר"</string> + <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"יופעל שוב אוטומטית מחר"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"תכונות כמו \'שיתוף מהיר\' ו\'איפה המכשיר שלי\' משתמשות ב-Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"חיבור ה-Bluetooth יופעל מחר בבוקר"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"שיתוף אודיו"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"מתבצע שיתוף של האודיו"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> סוללה"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"אודיו"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"אוזניות"</string> @@ -366,10 +368,8 @@ <string name="thermal" msgid="6758074791325414831">"תרמי"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"מצב שימוש ביד אחת"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"מכשירי שמיעה"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"פעיל"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"מנותק"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"מכשירי שמיעה"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"התאמה של מכשיר חדש"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"צריך ללחוץ כדי להתאים מכשיר חדש"</string> @@ -620,7 +620,7 @@ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"בקרת רעש"</string> <string name="volume_panel_spatial_audio_title" msgid="3367048857932040660">"אודיו מרחבי"</string> <string name="volume_panel_spatial_audio_off" msgid="4177490084606772989">"השבתה"</string> - <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"מצב קבוע"</string> + <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"מצב סטטי"</string> <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"מעקב אחר תנועות הראש"</string> <string name="volume_ringer_change" msgid="3574969197796055532">"יש להקיש כדי לשנות את מצב תוכנת הצלצול"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"השתקה"</string> @@ -1198,7 +1198,7 @@ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"ה-Wi-Fi לא יתחבר באופן אוטומטי בינתיים"</string> <string name="see_all_networks" msgid="3773666844913168122">"הצגת הכול"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"כדי לעבור בין רשתות, צריך לנתק את האתרנט"</string> - <string name="wifi_scan_notify_message" msgid="3753839537448621794">"כדי לשפר את חוויית השימוש במכשיר, אפליקציות ושירותים יוכלו לחפש רשתות Wi-Fi בכל שלב, גם כאשר ה-Wi-Fi כבוי. אפשר לשנות זאת בהגדרות של חיפוש נקודות Wi-Fi. "<annotation id="link">"שינוי"</annotation></string> + <string name="wifi_scan_notify_message" msgid="3753839537448621794">"כדי לשפר את חוויית השימוש במכשיר, אפליקציות ושירותים יוכלו לחפש רשתות Wi-Fi בכל שלב, גם כאשר ה-Wi-Fi כבוי. אפשר לשנות את זה בהגדרות של חיפוש נקודות Wi-Fi. "<annotation id="link">"שינוי"</annotation></string> <string name="turn_off_airplane_mode" msgid="8425587763226548579">"השבתה של מצב טיסה"</string> <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"אפליקציית <xliff:g id="APPNAME">%1$s</xliff:g> מבקשת להוסיף להגדרות המהירות את הלחצן הבא"</string> <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"להוסיף לחצן"</string> @@ -1324,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"קיצורי דרך לחיפוש"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"סמל הכיווץ"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"סמל ההרחבה"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"או"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"התאורה האחורית במקלדת"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"רמה %1$d מתוך %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"שליטה במכשירים"</string> diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml index 15c1f8b4ca77..6b5d08241582 100644 --- a/packages/SystemUI/res/values-ja/strings.xml +++ b/packages/SystemUI/res/values-ja/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"明日自動的に ON に戻す"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Quick Share や「デバイスを探す」などの機能は Bluetooth を使用します"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"明日の朝に Bluetooth が ON になります"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"音声の共有"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"音声を共有中"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"バッテリー <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"オーディオ"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ヘッドセット"</string> @@ -366,10 +368,8 @@ <string name="thermal" msgid="6758074791325414831">"温度"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"片手モード"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"補聴器"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"アクティブ"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"未接続"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"補聴器"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"新しいデバイスとペア設定"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"クリックすると、新しいデバイスをペア設定できます"</string> @@ -1324,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"検索ショートカット"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"閉じるアイコン"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"開くアイコン"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"または"</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-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml index 4d17179f9ead..399c1f0dc4c4 100644 --- a/packages/SystemUI/res/values-ka/strings.xml +++ b/packages/SystemUI/res/values-ka/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"ხელახლა ავტომატურად ჩართვა ხვალ"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"ისეთი ფუნქციები, როგორიცაა სწრაფი გაზიარება და ჩემი მოწყობილობის პოვნა, იყენებს Bluetooth-ს"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth ჩაირთვება ხვალ დილით"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"აუდიოს გაზიარება"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"აუდიოს გაზიარება"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ბატარეა"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"აუდიო"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ყურსაცვამი"</string> @@ -366,16 +368,14 @@ <string name="thermal" msgid="6758074791325414831">"თერმული"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"ცალი ხელის რეჟიმი"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"სმენის აპარატები"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"აქტიური"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"კავშირი გაწყვეტილია"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"სმენის აპარატები"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"ახალი მოწყობილობის დაწყვილება"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"დააწკაპუნეთ ახალი მოწყობილობის დასაწყვილებლად"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"წინასწარ დაყენებული პარამეტრების განახლება ვერ მოხერხდა"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"წინასწარ დაყენებული"</string> - <string name="live_caption_title" msgid="8916875614623730005">"პირდაპირი სუბტიტრები"</string> + <string name="live_caption_title" msgid="8916875614623730005">"ავტოსუბტიტრები"</string> <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> @@ -1324,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ძიების მალსახმობები"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"ხატულის ჩაკეცვა"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ხატულის გაფართოება"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ან"</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-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml index 08e9fa203463..a3e61790692a 100644 --- a/packages/SystemUI/res/values-kk/strings.xml +++ b/packages/SystemUI/res/values-kk/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Ертең автоматты түрде қосылсын"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Quick Share және Find My Device сияқты функциялар Bluetooth-ты пайдаланады."</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth ертең таңертең қосылады."</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Аудио бөлісу"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Аудио бөлісу әрекеті орындалып жатыр."</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Батарея деңгейі: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Aудио"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Гарнитура"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Құрылғы қызметінің қандай түріне әсер етті?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Мәселе түрін таңдаңыз."</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Экранды жазу"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"Өнімділік режимі"</string> + <string name="user_interface" msgid="3712869377953950887">"Пайдаланушы интерфейсі"</string> + <string name="thermal" msgid="6758074791325414831">"Термовизия"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Бір қолмен басқару режимі"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Есту құрылғылары"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Қосулы"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Ажыратулы"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Есту құрылғылары"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Жаңа құрылғыны жұптау"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Жаңа құрылғыны жұптау үшін басыңыз."</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"жабық"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"ашық"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Стилус батареясы: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Стилусты зарядтағышқа жалғаңыз."</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Стилус батареясының заряды аз"</string> <string name="video_camera" msgid="7654002575156149298">"Бейнекамера"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Іздеу жылдам пәрмендері"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Жию белгішесі"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Жаю белгішесі"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"немесе"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Пернетақта жарығы"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Деңгей: %1$d/%2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Үй басқару элементтері"</string> diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml index d6538bdf9bfe..7c6f12720cd6 100644 --- a/packages/SystemUI/res/values-km/strings.xml +++ b/packages/SystemUI/res/values-km/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"បើកដោយស្វ័យប្រវត្តិម្ដងទៀតនៅថ្ងៃស្អែក"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"មុខងារដូចជា Quick Share និង \"រកឧបករណ៍របស់ខ្ញុំ\" ប្រើប៊្លូធូស"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"ប៊្លូធូសនឹងបើកនៅព្រឹកស្អែក"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"ការស្ដាប់សំឡេងរួមគ្នា"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"កំពុងស្ដាប់សំឡេងរួមគ្នា"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"ថ្ម <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"សំឡេង"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"កាស"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"តើផ្នែកអ្វីនៃបទពិសោធប្រើប្រាស់ឧបករណ៍របស់អ្នកបានរងការប៉ះពាល់?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"ជ្រើសរើសប្រភេទបញ្ហា"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"ការថតវីដេអូអេក្រង់"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"ប្រតិបត្តិការ"</string> + <string name="user_interface" msgid="3712869377953950887">"ផ្ទៃប៉ះ"</string> + <string name="thermal" msgid="6758074791325414831">"កម្ដៅ"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"មុខងារប្រើដៃម្ខាង"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"ឧបករណ៍ជំនួយការស្ដាប់"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"សកម្ម"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"បានផ្ដាច់"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"ឧបករណ៍ជំនួយការស្ដាប់"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"ផ្គូផ្គងឧបករណ៍ថ្មី"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"ចុច ដើម្បីផ្គូផ្គងឧបករណ៍ថ្មី"</string> @@ -1201,7 +1198,7 @@ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi នឹងមិនភ្ជាប់ដោយស្វ័យប្រវត្តិក្នុងពេលនេះទេ"</string> <string name="see_all_networks" msgid="3773666844913168122">"មើលទាំងអស់"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"ដើម្បីប្ដូរបណ្ដាញ សូមផ្ដាច់អ៊ីសឺរណិត"</string> - <string name="wifi_scan_notify_message" msgid="3753839537448621794">"ដើម្បីធ្វើឱ្យបទពិសោធន៍ប្រើប្រាស់ឧបករណ៍ប្រសើរឡើង កម្មវិធី និងសេវាកម្មនៅតែអាចស្កេនរកបណ្ដាញ Wi‑Fi បានគ្រប់ពេល ទោះបីជានៅពេលដែលបិទ Wi‑Fi ក៏ដោយ។ អ្នកអាចប្ដូរវាបាននៅក្នុងការកំណត់ការស្កេន Wi‑Fi។ "<annotation id="link">"ប្ដូរ"</annotation></string> + <string name="wifi_scan_notify_message" msgid="3753839537448621794">"ដើម្បីធ្វើឱ្យបទពិសោធប្រើប្រាស់ឧបករណ៍ប្រសើរឡើង កម្មវិធី និងសេវាកម្មនៅតែអាចស្កេនរកបណ្ដាញ Wi‑Fi បានគ្រប់ពេល ទោះបីជានៅពេលដែលបិទ Wi‑Fi ក៏ដោយ។ អ្នកអាចប្ដូរវាបាននៅក្នុងការកំណត់ការស្កេន Wi‑Fi។ "<annotation id="link">"ប្ដូរ"</annotation></string> <string name="turn_off_airplane_mode" msgid="8425587763226548579">"បិទមុខងារពេលជិះយន្តហោះ"</string> <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> ចង់បញ្ចូលប្រអប់ខាងក្រោមទៅក្នុងការកំណត់រហ័ស"</string> <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"បញ្ចូលប្រអប់"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"បត់"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"លា"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"ថ្មប៊ិក <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"ភ្ជាប់ប៊ិករបស់អ្នកជាមួយឆ្នាំងសាក"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"ថ្មប៊ិកនៅសល់តិច"</string> <string name="video_camera" msgid="7654002575156149298">"កាមេរ៉ាវីដេអូ"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ផ្លូវកាត់ការស្វែងរក"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"រូបតំណាង \"បង្រួម\""</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"រូបតំណាង \"ពង្រីក\""</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ឬ"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"ពន្លឺក្រោយក្ដារចុច"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"កម្រិតទី %1$d នៃ %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"ការគ្រប់គ្រងផ្ទះ"</string> diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml index 92a8b6be128b..31aa87504ca3 100644 --- a/packages/SystemUI/res/values-kn/strings.xml +++ b/packages/SystemUI/res/values-kn/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"ನಾಳೆ ಪುನಃ ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಆನ್ ಮಾಡಿ"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"ಕ್ವಿಕ್ ಶೇರ್ ಮತ್ತು Find My Device ನಂತಹ ಫೀಚರ್ಗಳು ಬ್ಲೂಟೂತ್ ಅನ್ನು ಬಳಸುತ್ತವೆ"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"ಬ್ಲೂಟೂತ್ ನಾಳೆ ಬೆಳಗ್ಗೆ ಆನ್ ಆಗುತ್ತದೆ"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"ಆಡಿಯೋ ಹಂಚಿಕೊಳ್ಳುವಿಕೆ"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"ಆಡಿಯೋವನ್ನು ಹಂಚಿಕೊಳ್ಳಲಾಗುತ್ತಿದೆ"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ಬ್ಯಾಟರಿ"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ಆಡಿಯೋ"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ಹೆಡ್ಸೆಟ್"</string> @@ -361,24 +363,19 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"ಸಾಧನ ಬಳಸುವಾಗ ನೀವು ಯಾವ ರೀತಿಯ ಸಮಸ್ಯೆ ಎದುರಿಸುತ್ತೀರಿ?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"ಸಮಸ್ಯೆಯ ಪ್ರಕಾರವನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"ಸ್ಕ್ರೀನ್ ರೆಕಾರ್ಡ್"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"ಪರ್ಫಾರ್ಮೆನ್ಸ್"</string> + <string name="user_interface" msgid="3712869377953950887">"ಬಳಕೆದಾರ ಇಂಟರ್ಫೇಸ್"</string> + <string name="thermal" msgid="6758074791325414831">"ಥರ್ಮಲ್"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"ಒಂದು ಕೈ ಮೋಡ್"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"ಶ್ರವಣ ಸಾಧನಗಳು"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"ಸಕ್ರಿಯವಾಗಿದೆ"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"ಡಿಸ್ಕನೆಕ್ಟ್ ಆಗಿದೆ"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"ಶ್ರವಣ ಸಾಧನಗಳು"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"ಹೊಸ ಸಾಧನವನ್ನು ಪೇರ್ ಮಾಡಿ"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"ಹೊಸ ಸಾಧನವನ್ನು ಜೋಡಿಸಲು ಕ್ಲಿಕ್ ಮಾಡಿ"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"ಪ್ರಿಸೆಟ್ ಅನ್ನು ಅಪ್ಡೇಟ್ ಮಾಡಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"ಪ್ರಿಸೆಟ್"</string> - <string name="live_caption_title" msgid="8916875614623730005">"ಲೈವ್ ಶೀರ್ಷಿಕೆ"</string> + <string name="live_caption_title" msgid="8916875614623730005">"ಲೈವ್ ಕ್ಯಾಪ್ಶನ್"</string> <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> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"ಫೋಲ್ಡ್ ಮಾಡಿರುವುದು"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"ಅನ್ಫೋಲ್ಡ್ ಮಾಡಿರುವುದು"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"ಸ್ಟೈಲಸ್ ಬ್ಯಾಟರಿ <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"ನಿಮ್ಮ ಸ್ಟೈಲಸ್ ಅನ್ನು ಚಾರ್ಜರ್ಗೆ ಕನೆಕ್ಟ್ ಮಾಡಿ"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"ಸ್ಟೈಲಸ್ ಬ್ಯಾಟರಿ ಕಡಿಮೆಯಿದೆ"</string> <string name="video_camera" msgid="7654002575156149298">"ವೀಡಿಯೊ ಕ್ಯಾಮರಾ"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ಹುಡುಕಾಟದ ಶಾರ್ಟ್ಕಟ್ಗಳು"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"ಕುಗ್ಗಿಸುವ ಐಕಾನ್"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ವಿಸ್ತೃತಗೊಳಿಸುವ ಐಕಾನ್"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ಅಥವಾ"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"ಕೀಬೋರ್ಡ್ ಬ್ಯಾಕ್ಲೈಟ್"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d ರಲ್ಲಿ %1$d ಮಟ್ಟ"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"ಮನೆ ನಿಯಂತ್ರಣಗಳು"</string> diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml index b141b48de023..91266b7aea10 100644 --- a/packages/SystemUI/res/values-ko/strings.xml +++ b/packages/SystemUI/res/values-ko/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"내일 다시 자동으로 사용 설정"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Quick Share, 내 기기 찾기 등의 기능에서 블루투스를 사용합니다."</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"블루투스가 내일 아침에 켜집니다."</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"오디오 공유"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"오디오 공유 중"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"배터리 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"오디오"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"헤드셋"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"기기 경험의 어떤 부분에 영향이 있었나요?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"문제 유형 선택"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"화면 녹화"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"성능"</string> + <string name="user_interface" msgid="3712869377953950887">"사용자 인터페이스"</string> + <string name="thermal" msgid="6758074791325414831">"열화상"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"한 손 사용 모드"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"청각 보조 기기"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"활성"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"연결 끊김"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"청각 보조 기기"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"새 기기와 페어링"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"새 기기와 페어링하려면 클릭하세요"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"접은 상태"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"펼친 상태"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"스타일러스 배터리 <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"스타일러스를 충전기에 연결하세요"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"스타일러스 배터리 부족"</string> <string name="video_camera" msgid="7654002575156149298">"비디오 카메라"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"검색 바로가기"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"접기 아이콘"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"확장 아이콘"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"또는"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"키보드 백라이트"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d단계 중 %1$d단계"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"홈 컨트롤"</string> diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml index dc7227ee4779..a3fd0a2e1f1f 100644 --- a/packages/SystemUI/res/values-ky/strings.xml +++ b/packages/SystemUI/res/values-ky/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Эртең автоматтык түрдө кайра күйгүзүү"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Bluetooth Тез бөлүшүү жана Түзмөгүм кайда? сыяктуу функцияларда колдонулат"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth эртең таңда күйөт"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Чогуу угуу"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Чогуу угулууда"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Батареянын деңгээли <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудио"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Гарнитура"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Түзмөгүңүздүн кайсы бөлүгүнө кедергиси тийди?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Маселенин түрүн тандоо"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Экрандан видео жаздырып алуу"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"Иштин майнаптуулугу"</string> + <string name="user_interface" msgid="3712869377953950887">"Колдонуучунун интерфейси"</string> + <string name="thermal" msgid="6758074791325414831">"Жылуулук"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Бир кол режими"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Угуу аппараттары"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Жигердүү"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Ажыратылды"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Угуу аппараттары"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Жаңы түзмөк кошуу"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Жаңы түзмөк кошуу үчүн басыңыз"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"бүктөлгөн"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"ачылган"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Стилустун батареясы: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Стилусту кубаттаңыз"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Стилустун батареясы отурайын деп калды"</string> <string name="video_camera" msgid="7654002575156149298">"Видео камера"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Ыкчам баскычтарды издөө"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Жыйыштыруу сүрөтчөсү"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Жайып көрсөтүү сүрөтчөсү"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"же"</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-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml index 58940842729b..31182fd7f740 100644 --- a/packages/SystemUI/res/values-lo/strings.xml +++ b/packages/SystemUI/res/values-lo/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"ເປີດໃຊ້ໂດຍອັດຕະໂນມັດອີກຄັ້ງມື້ອື່ນ"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"ຄຸນສົມບັດຕ່າງໆໃຊ້ Bluetooth ເຊັ່ນ: ການແຊຣ໌ດ່ວນ ແລະ ຊອກຫາອຸປະກອນຂອງຂ້ອຍ"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth ຈະເປີດມື້ອື່ນເຊົ້າ"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"ການແບ່ງປັນສຽງ"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"ກຳລັງແບ່ງປັນສຽງ"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"ແບັດເຕີຣີ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ສຽງ"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ຊຸດຫູຟັງ"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"ສ່ວນໃດຂອງປະສົບການອຸປະກອນຂອງທ່ານໄດ້ຮັບຜົນກະທົບ?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"ເລືອກປະເພດບັນຫາ"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"ບັນທຶກໜ້າຈໍ"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"ປະສິດທິພາບ"</string> + <string name="user_interface" msgid="3712869377953950887">"ສ່ວນຕິດຕໍ່ຜູ້ໃຊ້"</string> + <string name="thermal" msgid="6758074791325414831">"ຄວາມຮ້ອນ"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"ໂໝດມືດຽວ"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"ອຸປະກອນຊ່ວຍຟັງ"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"ນຳໃຊ້ຢູ່"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"ຕັດການເຊື່ອມຕໍ່ແລ້ວ"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"ອຸປະກອນຊ່ວຍຟັງ"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"ຈັບຄູ່ອຸປະກອນໃໝ່"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"ຄລິກເພື່ອຈັບຄູ່ອຸປະກອນໃໝ່"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"ພັບແລ້ວ"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"ກາງອອກແລ້ວ"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"ແບັດເຕີຣີປາກກາ <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"ເຊື່ອມຕໍ່ປາກກາຂອງທ່ານກັບສາຍສາກ"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"ແບັດເຕີຣີປາກກາເຫຼືອໜ້ອຍ"</string> <string name="video_camera" msgid="7654002575156149298">"ກ້ອງວິດີໂອ"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ທາງລັດການຊອກຫາ"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"ໄອຄອນຫຍໍ້ລົງ"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ໄອຄອນຂະຫຍາຍ"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ຫຼື"</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-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml index 2c9eed8e6d53..62c291135b07 100644 --- a/packages/SystemUI/res/values-lt/strings.xml +++ b/packages/SystemUI/res/values-lt/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatiškai vėl įjungti rytoj"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Tokioms funkcijoms kaip „Spartusis bendrinimas“ ir „Rasti įrenginį“ naudojamas „Bluetooth“ ryšys"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"„Bluetooth“ ryšys bus įjungtas rytoj ryte"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Garso įrašų bendrinimas"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Bendrinamas garso įrašas"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Akumuliatorius: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Garsas"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Virtualiosios realybės įrenginys"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Kuri įrenginio funkcija buvo paveikta?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Pasirinkite problemos tipą"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Ekrano įrašas"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"Našumas"</string> + <string name="user_interface" msgid="3712869377953950887">"Naudotojo sąsaja"</string> + <string name="thermal" msgid="6758074791325414831">"Šiluminis"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Vienos rankos režimas"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Klausos įrenginiai"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktyvus"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Atjungtas"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Klausos įrenginiai"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Susieti naują įrenginį"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Spustelėkite, kad susietumėte naują įrenginį"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"sulenkta"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"nesulenkta"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Liko rašiklio akumuliatoriaus energijos: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Prijunkite rašiklį prie kroviklio"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Senka rašiklio akumuliatorius"</string> <string name="video_camera" msgid="7654002575156149298">"Vaizdo kamera"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Paieškos šaukiniai"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Sutraukimo piktograma"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Išskleidimo piktograma"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"arba"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Klaviatūros foninis apšvietimas"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"%1$d lygis iš %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Namų sistemos valdymas"</string> diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml index 6a5d0681d7df..8d0b43c9a925 100644 --- a/packages/SystemUI/res/values-lv/strings.xml +++ b/packages/SystemUI/res/values-lv/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automātiski atkal ieslēgt rīt"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Tādas funkcijas kā “Ātrā kopīgošana” un “Atrast ierīci” izmanto Bluetooth savienojumu"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth savienojums tiks ieslēgts rīt no rīta"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Kopīgot audio"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Notiek audio kopīgošana"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Akumulators: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Austiņas"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Kuras ierīces funkcijas tika ietekmētas?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Atlasiet problēmas veidu"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Ekrāna ierakstīšana"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"Veiktspēja"</string> + <string name="user_interface" msgid="3712869377953950887">"Lietotāja saskarne"</string> + <string name="thermal" msgid="6758074791325414831">"Ierīces temperatūra"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Vienas rokas režīms"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Dzirdes aparāti"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktīvs"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Atvienots"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Dzirdes aparāti"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Savienot pārī jaunu ierīci"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Noklikšķiniet, lai savienotu pārī jaunu ierīci"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"aizvērta"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"atvērta"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Skārienekrāna pildspalvas akumulatora uzlādes līmenis: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Pievienojiet skārienekrāna pildspalvu lādētājam"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Zems skārienekrāna pildspalvas akumulatora līmenis"</string> <string name="video_camera" msgid="7654002575156149298">"Videokamera"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Meklēšanas saīsnes"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Sakļaušanas ikona"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Izvēršanas ikona"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"vai"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Tastatūras fona apgaismojums"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Līmenis numur %1$d, kopā ir %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Mājas kontrolierīces"</string> diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml index 7c410965bfa2..1c889e6b6b68 100644 --- a/packages/SystemUI/res/values-mk/strings.xml +++ b/packages/SystemUI/res/values-mk/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Автоматски вклучи повторно утре"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Функциите како „Брзо споделување“ и „Најди го мојот уред“ користат Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth ќе се вклучи утре наутро"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Споделување аудио"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Се споделува аудио"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Батерија: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудио"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Слушалки"</string> @@ -301,7 +303,7 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Корисник"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> <string name="quick_settings_internet_label" msgid="6603068555872455463">"Интернет"</string> - <string name="quick_settings_networks_available" msgid="1875138606855420438">"Мрежите се достапни"</string> + <string name="quick_settings_networks_available" msgid="1875138606855420438">"Достапни се мрежи"</string> <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"Не се достапни мрежи"</string> <string name="quick_settings_wifi_detail_empty_text" msgid="483130889414601732">"Нема достапни Wi-Fi мрежи"</string> <string name="quick_settings_wifi_secondary_label_transient" msgid="7501659015509357887">"Се вклучува…"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Кој дел од доживувањето на уредот беше засегнат?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Изберете тип проблем"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Снимање екран"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"Изведба"</string> + <string name="user_interface" msgid="3712869377953950887">"Кориснички интерфејс"</string> + <string name="thermal" msgid="6758074791325414831">"Термално"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Режим со една рака"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Слушни апарати"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Активно"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Не е поврзано"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Слушни апарати"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Спари нов уред"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Кликнете за да спарите нов уред"</string> @@ -689,8 +686,8 @@ <string name="notification_automatic_title" msgid="3745465364578762652">"Автоматски"</string> <string name="notification_channel_summary_low" msgid="4860617986908931158">"Без звук или вибрации"</string> <string name="notification_conversation_summary_low" msgid="1734433426085468009">"Без звук или вибрации и се појавува подолу во делот со разговори"</string> - <string name="notification_channel_summary_default" msgid="777294388712200605">"Може да ѕвони или вибрира во зависност од поставките за уредот"</string> - <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Може да ѕвони или вибрира во зависност од поставките на уредот. Стандардно, разговорите од <xliff:g id="APP_NAME">%1$s</xliff:g> се во балончиња."</string> + <string name="notification_channel_summary_default" msgid="777294388712200605">"Може да ѕвони или да вибрира во зависност од поставките за уредот"</string> + <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Може да ѕвони или да вибрира во зависност од поставките за уредот. Стандардно, разговорите од <xliff:g id="APP_NAME">%1$s</xliff:g> се во балончиња."</string> <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Дозволете системот да определи дали известувањево треба да испушти звук или да вибрира"</string> <string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>Статус:</b> поставено на „Стандардно“"</string> <string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>Статус:</b> намалено на „Тивко“"</string> @@ -1210,7 +1207,7 @@ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{Активна е # апликација}one{Активни се # апликација}other{Активни се # апликации}}"</string> <string name="fgs_dot_content_description" msgid="2865071539464777240">"Нови информации"</string> <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Активни апликации"</string> - <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Овие апликации се активни и работат, дури и кога не ги користите. Ова ја подобрува нивната функционалност, но може да влијае и на траењето на батеријата."</string> + <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Овие апликации се активни и работат дури и кога не ги користите. Ова ја подобрува нивната функционалност, но може и да влијае на траењето на батеријата."</string> <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Запри"</string> <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Запрено"</string> <string name="clipboard_edit_text_done" msgid="4551887727694022409">"Готово"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"затворен"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"отворен"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Батерија на пенкало: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Поврзете го пенкалото со полнач"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Слаба батерија на пенкало"</string> <string name="video_camera" msgid="7654002575156149298">"Видеокамера"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Кратенки за пребарување"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Икона за собирање"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Икона за проширување"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"или"</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-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml index 540358c359d7..3dd91f7cfd2b 100644 --- a/packages/SystemUI/res/values-ml/strings.xml +++ b/packages/SystemUI/res/values-ml/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"നാളെ വീണ്ടും സ്വയമേവ ഓണാക്കുക"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"ക്വിക്ക് ഷെയർ, Find My Device പോലുള്ള ഫീച്ചറുകൾ Bluetooth ഉപയോഗിക്കുന്നു"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth നാളെ രാവിലെ ഓണാകും"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"ഓഡിയോ പങ്കിടൽ"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"ഓഡിയോ പങ്കിടുന്നു"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ബാറ്ററി"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ഓഡിയോ"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ഹെഡ്സെറ്റ്"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"നിങ്ങളുടെ ഉപകരണ അനുഭവത്തിന്റെ ഏത് ഭാഗമാണ് ബാധിച്ചത്?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"പ്രശ്ന തരം തിരഞ്ഞെടുക്കുക"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"സ്ക്രീൻ റെക്കോർഡ്"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"പ്രകടനം"</string> + <string name="user_interface" msgid="3712869377953950887">"ഉപയോക്തൃ ഇന്റർഫേസ്"</string> + <string name="thermal" msgid="6758074791325414831">"തെർമൽ"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"ഒറ്റക്കൈ മോഡ്"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"കേൾവിക്കുള്ള ഉപകരണങ്ങൾ"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"സജീവം"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"വിച്ഛേദിച്ചിരിക്കുന്നു"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"കേൾവിക്കുള്ള ഉപകരണങ്ങൾ"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"പുതിയ ഉപകരണം ജോടിയാക്കുക"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"പുതിയ ഉപകരണം ജോടിയാക്കാൻ ക്ലിക്ക് ചെയ്യുക"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"ഫോൾഡ് ചെയ്തത്"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"അൺഫോൾഡ് ചെയ്തത്"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"സ്റ്റൈലസ് ബാറ്ററി <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"നിങ്ങളുടെ സ്റ്റൈലസ് ചാർജറുമായി കണക്റ്റ് ചെയ്യുക"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"സ്റ്റൈലസിന്റെ ബാറ്ററി ചാർജ് കുറവാണ്"</string> <string name="video_camera" msgid="7654002575156149298">"വീഡിയോ ക്യാമറ"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"തിരയൽ കുറുക്കുവഴികൾ"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"ചുരുക്കൽ ഐക്കൺ"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"വികസിപ്പിക്കൽ ഐക്കൺ"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"അല്ലെങ്കിൽ"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"കീബോഡ് ബാക്ക്ലൈറ്റ്"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d-ൽ %1$d-ാമത്തെ ലെവൽ"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"ഹോം കൺട്രോളുകൾ"</string> diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml index 47e6936ea5f1..553926383476 100644 --- a/packages/SystemUI/res/values-mn/strings.xml +++ b/packages/SystemUI/res/values-mn/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Маргааш автоматаар дахин асаах"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Түргэн хуваалцах, Миний төхөөрөмжийг олох зэрэг онцлогууд Bluetooth-г ашигладаг"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth-г маргааш өглөө асаана"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Аудио хуваалцах"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Аудио хуваалцаж байна"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> батарей"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудио"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Чихэвч"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Таны төхөөрөмжийн хэрэглээний аль хэсэгт нөлөөлсөн бэ?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Асуудлын төрөл сонгоно уу"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Дэлгэцийн бичлэг"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"Гүйцэтгэл"</string> + <string name="user_interface" msgid="3712869377953950887">"Хэрэглэгчийн интерфейс"</string> + <string name="thermal" msgid="6758074791325414831">"Дулааны"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Нэг гарын горим"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Сонсголын төхөөрөмжүүд"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Идэвхтэй"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Салсан"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Сонсголын төхөөрөмжүүд"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Шинэ төхөөрөмж хослуулах"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Шинэ төхөөрөмж хослуулахын тулд товшино уу"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"эвхсэн"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"дэлгэсэн"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Мэдрэгч үзгийн батарей <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Мэдрэгч үзгээ цэнэглэгчтэй холбоорой"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Мэдрэгч үзэгний батарей бага байна"</string> <string name="video_camera" msgid="7654002575156149298">"Видео камер"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Товчлолууд хайх"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Хураах дүрс тэмдэг"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Дэлгэх дүрс тэмдэг"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"эсвэл"</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-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml index 39f14133eb2b..aea4d5a4cf50 100644 --- a/packages/SystemUI/res/values-mr/strings.xml +++ b/packages/SystemUI/res/values-mr/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"उद्या पुन्हा आपोआप सुरू करा"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"क्विक शेअर आणि Find My Device यांसारखी वैशिष्ट्ये ब्लूटूथ वापरतात"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"ब्लूटूथ उद्या सकाळी सुरू होईल"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"ऑडिओ शेअरिंग"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"ऑडिओ शेअर करत आहे"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> बॅटरी"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ऑडिओ"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"हेडसेट"</string> @@ -366,10 +368,8 @@ <string name="thermal" msgid="6758074791325414831">"थर्मल"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"एकहाती मोड"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"श्रवणयंत्रे"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"ॲक्टिव्ह आहे"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"डिस्कनेक्ट केले आहे"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"श्रवणयंत्रे"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"नवीन डिव्हाइस पेअर करा"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"नवीन डिव्हाइस पेअर करण्यासाठी क्लिक करा"</string> @@ -1324,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"शोधण्यासाठी शॉर्टकट"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"कोलॅप्स करा आयकन"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"विस्तार करा आयकन"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"किंवा"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"कीबोर्ड बॅकलाइट"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d पैकी %1$d पातळी"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"होम कंट्रोल"</string> diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml index 8fce993b6d74..01310982f331 100644 --- a/packages/SystemUI/res/values-ms/strings.xml +++ b/packages/SystemUI/res/values-ms/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Dihidupkan lagi esok secara automatik"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Ciri seperti Quick Share dan Find My Device menggunakan Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth akan dihidupkan esok pagi"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Perkongsian Audio"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Berkongsi Audio"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> bateri"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Set Kepala"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Pengalaman peranti yang manakah yang terjejas?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Pilih jenis masalah"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Rakam skrin"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"Prestasi"</string> + <string name="user_interface" msgid="3712869377953950887">"Antara Muka Pengguna"</string> + <string name="thermal" msgid="6758074791325414831">"Terma"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Mod sebelah tangan"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Peranti pendengaran"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktif"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Diputuskan sambungan"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Peranti pendengaran"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Gandingkan peranti baharu"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Klik untuk menggandingkan peranti baharu"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"terlipat"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"tidak terlipat"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Bateri stilus <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Sambungkan stilus anda kepada pengecas"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Bateri stilus lemah"</string> <string name="video_camera" msgid="7654002575156149298">"Kamera video"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Pintasan carian"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Kuncupkan ikon"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Kembangkan ikon"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"atau"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Cahaya latar papan kekunci"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Tahap %1$d daripada %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Kawalan Rumah"</string> diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml index 926426120720..9b19b62c73db 100644 --- a/packages/SystemUI/res/values-my/strings.xml +++ b/packages/SystemUI/res/values-my/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"မနက်ဖြန် အလိုအလျောက် ပြန်ဖွင့်ရန်"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"‘အမြန် မျှဝေပါ’ နှင့် Find My Device ကဲ့သို့ တူးလ်များသည် ဘလူးတုသ်သုံးသည်"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"မနက်ဖြန်နံနက်တွင် ဘလူးတုသ် ပွင့်ပါမည်"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"အော်ဒီယို မျှဝေခြင်း"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"အော်ဒီယို မျှဝေနေသည်"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ဘက်ထရီ"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"အသံ"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"မိုက်ခွက်ပါနားကြပ်"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"စက်အသုံးပြုမှု၏ မည်သည့်အပိုင်းကို သက်ရောက်သလဲ။"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"ပြဿနာအမျိုးအစား ရွေးရန်"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"ဖန်သားပြင်ရိုက်ကူးရန်"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"စွမ်းဆောင်ရည်"</string> + <string name="user_interface" msgid="3712869377953950887">"သုံးသူအတွက် ကြားခံစနစ်"</string> + <string name="thermal" msgid="6758074791325414831">"အပူဓာတ်"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"လက်တစ်ဖက်သုံးမုဒ်"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"နားကြားကိရိယာ"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"သုံးနေသည်"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"ချိတ်ဆက်မထားပါ"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"နားကြားကိရိယာ"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"စက်အသစ်တွဲချိတ်ရန်"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"စက်အသစ် တွဲချိတ်ရန် နှိပ်ပါ"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"ခေါက်ထားသည်"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"ဖြန့်ထားသည်"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"စတိုင်လပ်စ် ဘက်ထရီ <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"စတိုင်လပ်စ်ကို အားသွင်းကိရိယာနှင့် ချိတ်ဆက်ခြင်း"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"စတိုင်လပ်စ် ဘက်ထရီ အားနည်းနေသည်"</string> <string name="video_camera" msgid="7654002575156149298">"ဗီဒီယိုကင်မရာ"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ရှာဖွေစာလုံး ဖြတ်လမ်း"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"လျှော့ပြရန် သင်္ကေတ"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ပိုပြရန် သင်္ကေတ"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"သို့မဟုတ်"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"ကီးဘုတ်နောက်မီး"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"အဆင့် %2$d အနက် %1$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"အိမ်ထိန်းချုပ်မှုများ"</string> diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml index 9c2193ef1a13..a8befb36d6c8 100644 --- a/packages/SystemUI/res/values-nb/strings.xml +++ b/packages/SystemUI/res/values-nb/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Slå på igjen i morgen automatisk"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Funksjoner som Quick Share og Finn enheten min bruker Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth slås på i morgen tidlig"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Lyddeling"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Deler lyd"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> batteri"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Lyd"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Hodetelefoner"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Hvilken del av enhetsopplevelsen din ble påvirket?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Velg problemtype"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Skjermopptak"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"Ytelse"</string> + <string name="user_interface" msgid="3712869377953950887">"Brukergrensesnitt"</string> + <string name="thermal" msgid="6758074791325414831">"Termisk"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Enhåndsmodus"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Høreapparater"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktiv"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Frakoblet"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Høreapparater"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Koble til en ny enhet"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Klikk for å koble til en ny enhet"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"lagt sammen"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"åpen"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Batteri i pekepennen: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Koble pekepennen til en lader"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Det er lite batteri i pekepennen"</string> <string name="video_camera" msgid="7654002575156149298">"Videokamera"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Snarveier til søk"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Skjul-ikon"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Vis-ikon"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"eller"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Bakgrunnslys for tastatur"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Nivå %1$d av %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Hjemkontroller"</string> diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml index 94b197b3f238..c26ea3e039dd 100644 --- a/packages/SystemUI/res/values-ne/strings.xml +++ b/packages/SystemUI/res/values-ne/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"भोलि फेरि स्वतः अन गर्नुहोस्"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"क्विक सेयर र Find My Device जस्ता सुविधाहरू प्रयोग गर्न ब्लुटुथ चाहिन्छ"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"ब्लुटुथ भोलि बिहान अन हुने छ"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"अडियो सेयरिङ"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"अडियो सेयर गरिँदै छ"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ब्याट्री"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"अडियो"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"हेडसेट"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"तपाईंको डिभाइसको कुन चाहिँ सुविधा प्रभावित भएको छ?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"समस्याको प्रकार चयन गर्नुहोस्"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"स्क्रिन रेकर्ड"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"पर्फर्मेन्स"</string> + <string name="user_interface" msgid="3712869377953950887">"युजर इन्टरफेस"</string> + <string name="thermal" msgid="6758074791325414831">"थर्मल"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"एक हाते मोड"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"हियरिङ डिभाइसहरू"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"सक्रिय छ"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"डिस्कनेक्ट गरिएको छ"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"हियरिङ डिभाइसहरू"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"नयाँ डिभाइस कनेक्ट गर्नुहोस्"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"नयाँ डिभाइसमा कनेक्ट गर्न क्लिक गर्नुहोस्"</string> @@ -382,9 +379,9 @@ <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> - <string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"यसो गर्नुभयो भने माइक्रोफोन प्रयोग गर्ने अनुमति दिइएका सबै एप तथा सेवाहरूका लागि सो अनुमति अनब्लक गरिन्छ।"</string> - <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"यसो गर्नुभयो भने क्यामेरा प्रयोग गर्ने अनुमति दिइएका सबै एप तथा सेवाहरूका लागि सो अनुमति अनब्लक गरिन्छ।"</string> - <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"यसो गर्नुभयो भने क्यामेरा वा माइक्रोफोन प्रयोग गर्ने अनुमति दिइएका सबै एप तथा सेवाहरूका लागि सो अनुमति अनब्लक गरिन्छ।"</string> + <string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"यसो गर्नुभयो भने माइक्रोफोन प्रयोग गर्ने अनुमति दिइएका सबै एप तथा सेवाको हकमा यो अनुमति अनब्लक गरिन्छ।"</string> + <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"यसो गर्नुभयो भने क्यामेरा प्रयोग गर्ने अनुमति दिइएका सबै एप तथा सेवाको हकमा यो अनुमति अनब्लक गरिन्छ।"</string> + <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"यसो गर्नुभयो भने क्यामेरा वा माइक्रोफोन प्रयोग गर्ने अनुमति दिइएका सबै एप तथा सेवाको हकमा यो अनुमति अनब्लक गरिन्छ।"</string> <string name="sensor_privacy_start_use_mic_blocked_dialog_title" msgid="2640140287496469689">"माइक्रोफोन ब्लक गरिएको छ"</string> <string name="sensor_privacy_start_use_camera_blocked_dialog_title" msgid="7398084286822440384">"क्यामेरा ब्लक गरिएको छ"</string> <string name="sensor_privacy_start_use_mic_camera_blocked_dialog_title" msgid="195236134743281973">"माइक र क्यामेरा ब्लक गरिएको छ"</string> @@ -932,7 +929,7 @@ <string name="qs_dnd_prompt_auto_rule_app" msgid="1841469944118486580">"कुनै स्वचालित नियम वा एपले बाधा नपुऱ्याउनुहोस् नामक विकल्पलाई सक्रिय गऱ्यो।"</string> <string name="running_foreground_services_title" msgid="5137313173431186685">"पृष्ठभूमिमा चल्ने एपहरू"</string> <string name="running_foreground_services_msg" msgid="3009459259222695385">"ब्याट्री र डेटाका प्रयोग सम्बन्धी विवरणहरूका लागि ट्याप गर्नुहोस्"</string> - <string name="mobile_data_disable_title" msgid="5366476131671617790">"मोबाइल डेटा निष्क्रिय पार्ने हो?"</string> + <string name="mobile_data_disable_title" msgid="5366476131671617790">"मोबाइल डेटा अफ गर्ने हो?"</string> <string name="mobile_data_disable_message" msgid="8604966027899770415">"तपाईं <xliff:g id="CARRIER">%s</xliff:g> मार्फत डेटा वा इन्टरनेट प्रयोग गर्न सक्नुहुने छैन। Wi-Fi मार्फत मात्र इन्टरनेट उपलब्ध हुने छ।"</string> <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"तपाईंको सेवा प्रदायक"</string> <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"फेरि <xliff:g id="CARRIER">%s</xliff:g> को मोबाइल डेटा अन गर्ने हो?"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"फोल्ड गरिएको"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"अनफोल्ड गरिएको"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"स्टाइलसको ब्याट्री <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"आफ्नो स्टाइलस चार्जरमा कनेक्ट गर्नुहोस्"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"स्टाइलसको ब्याट्री लो छ"</string> <string name="video_camera" msgid="7654002575156149298">"भिडियो क्यामेरा"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"खोजका सर्टकटहरू"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"\"कोल्याप्स गर्नुहोस्\" आइकन"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"\"एक्स्पान्ड गर्नुहोस्\" आइकन"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"वा"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"किबोर्ड ब्याकलाइट"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d मध्ये %1$d औँ स्तर"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"होम कन्ट्रोलहरू"</string> diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml index 8cdbf6139a7d..3eba8a8a1324 100644 --- a/packages/SystemUI/res/values-nl/strings.xml +++ b/packages/SystemUI/res/values-nl/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Morgen weer automatisch aanzetten"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Functies zoals Quick Share en Vind mijn apparaat gebruiken bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth wordt morgenochtend aangezet"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Audio delen"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Audio delen…"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> batterijniveau"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string> @@ -366,10 +368,8 @@ <string name="thermal" msgid="6758074791325414831">"Thermisch"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Bediening met 1 hand"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Hoortoestellen"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Actief"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Ontkoppeld"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Hoortoestellen"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Nieuw apparaat koppelen"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Klik om nieuw apparaat te koppelen"</string> @@ -1324,10 +1324,9 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Snelkoppelingen voor zoekopdrachten"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Icoon voor samenvouwen"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Icoon voor uitvouwen"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"of"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Achtergrondverlichting van toetsenbord"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Niveau %1$d van %2$d"</string> - <string name="home_controls_dream_label" msgid="6567105701292324257">"Huisbediening"</string> - <string name="home_controls_dream_description" msgid="4644150952104035789">"Snel toegang tot je huisbediening als screensaver"</string> + <string name="home_controls_dream_label" msgid="6567105701292324257">"Bediening voor in huis"</string> + <string name="home_controls_dream_description" msgid="4644150952104035789">"Gebruik bediening voor in huis als screensaver"</string> </resources> diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml index 287d1a71dd04..1e3ebab86b77 100644 --- a/packages/SystemUI/res/values-or/strings.xml +++ b/packages/SystemUI/res/values-or/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"ଆସନ୍ତାକାଲି ସ୍ୱତଃ ପୁଣି ଚାଲୁ ହେବ"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Quick Share ଏବଂ Find My Device ପରି ଫିଚରଗୁଡ଼ିକ ବ୍ଲୁଟୁଥ ବ୍ୟବହାର କରେ"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"ବ୍ଲୁଟୁଥ ଆସନ୍ତା କାଲି ସକାଳେ ଚାଲୁ ହେବ"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"ଅଡିଓ ସେୟାରିଂ"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"ଅଡିଓ ସେୟାର କରାଯାଉଛି"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ବ୍ୟାଟେରୀ"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ଅଡିଓ"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ହେଡସେଟ୍"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"ଆପଣଙ୍କ ଡିଭାଇସ ଅନୁଭୂତିର କେଉଁ ଅଂଶ ପ୍ରଭାବିତ ହୋଇଛି?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"ସମସ୍ୟାର ପ୍ରକାର ଚୟନ କରନ୍ତୁ"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"ସ୍କ୍ରିନ ରେକର୍ଡ"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"ପରଫରମାନ୍ସ"</string> + <string name="user_interface" msgid="3712869377953950887">"ୟୁଜର ଇଣ୍ଟରଫେସ"</string> + <string name="thermal" msgid="6758074791325414831">"ଥର୍ମାଲ"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"ଏକ-ହାତ ମୋଡ"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"ଶ୍ରବଣ ଡିଭାଇସଗୁଡ଼ିକ"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"ସକ୍ରିୟ"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"ଡିସକନେକ୍ଟ ହୋଇଛି"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"ଶ୍ରବଣ ଡିଭାଇସଗୁଡ଼ିକ"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"ନୂଆ ଡିଭାଇସ ପେୟାର କରନ୍ତୁ"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"ନୂଆ ଡିଭାଇସ ପେୟାର କରିବାକୁ କ୍ଲିକ କରନ୍ତୁ"</string> @@ -933,7 +930,7 @@ <string name="running_foreground_services_title" msgid="5137313173431186685">"ବ୍ୟାକଗ୍ରାଉଣ୍ଡରେ ଆପ୍ ଚାଲୁଛି"</string> <string name="running_foreground_services_msg" msgid="3009459259222695385">"ବ୍ୟାଟେରୀ ଏବଂ ଡାଟା ବ୍ୟବହାର ଉପରେ ବିବରଣୀ ପାଇଁ ଟାପ୍ କରନ୍ତୁ"</string> <string name="mobile_data_disable_title" msgid="5366476131671617790">"ମୋବାଇଲ୍ ଡାଟା ବନ୍ଦ କରିବେ?"</string> - <string name="mobile_data_disable_message" msgid="8604966027899770415">"ଡାଟା କିମ୍ବା ଇଣ୍ଟରନେଟ୍କୁ <xliff:g id="CARRIER">%s</xliff:g> ଦ୍ଵାରା ଆପଣଙ୍କର ଆକ୍ସେସ୍ ରହିବ ନାହିଁ। ଇଣ୍ଟରନେଟ୍ କେବଳ ୱାଇ-ଫାଇ ମାଧ୍ୟମରେ ଉପଲବ୍ଧ ହେବ।"</string> + <string name="mobile_data_disable_message" msgid="8604966027899770415">"ଡାଟା କିମ୍ବା ଇଣ୍ଟର୍ନେଟକୁ <xliff:g id="CARRIER">%s</xliff:g> ଦ୍ଵାରା ଆପଣଙ୍କର ଆକ୍ସେସ ରହିବ ନାହିଁ। ଇଣ୍ଟର୍ନେଟ କେବଳ ୱାଇ-ଫାଇ ମାଧ୍ୟମରେ ଉପଲବ୍ଧ ହେବ।"</string> <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"ଆପଣଙ୍କ କେରିଅର୍"</string> <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"<xliff:g id="CARRIER">%s</xliff:g>କୁ ପୁଣି ସ୍ୱିଚ କରିବେ?"</string> <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"ଉପଲବ୍ଧତା ଆଧାରରେ ମୋବାଇଲ ଡାଟା ସ୍ୱତଃ ସ୍ୱିଚ ହେବ ନାହିଁ"</string> @@ -1210,7 +1207,7 @@ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{#ଟି ଆପ ସକ୍ରିୟ ଅଛି}other{#ଟି ଆପ ସକ୍ରିୟ ଅଛି}}"</string> <string name="fgs_dot_content_description" msgid="2865071539464777240">"ନୂଆ ସୂଚନା"</string> <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"ସକ୍ରିୟ ଆପ୍ସ"</string> - <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"ଆପଣ ଏହି ଆପ୍ସକୁ ବ୍ୟବହାର କରୁନଥିଲେ ମଧ୍ୟ ସେଗୁଡ଼ିକ ସକ୍ରିୟ ରହିଥାଏ ଏବଂ ଚାଲୁଥାଏ। ଏହା ସେଗୁଡ଼ିକର କାର୍ଯ୍ୟକ୍ଷମତାକୁ ଉନ୍ନତ କରେ, କିନ୍ତୁ ଏହା ମଧ୍ୟ ବ୍ୟାଟେରୀ ଲାଇଫକୁ ପ୍ରଭାବିତ କରିପାରେ।"</string> + <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"ଆପଣ ଏହି ଆପ୍ସକୁ ବ୍ୟବହାର କରୁନଥିଲେ ମଧ୍ୟ ସେଗୁଡ଼ିକ ସକ୍ରିୟ ରହିଥାଏ ଏବଂ ଚାଲୁଥାଏ। ଏହା ସେଗୁଡ଼ିକର କାର୍ଯ୍ୟକ୍ଷମତାକୁ ଉନ୍ନତ କରେ, କିନ୍ତୁ ଏହା ମଧ୍ୟ ବେଟେରୀ ଲାଇଫକୁ ପ୍ରଭାବିତ କରିପାରେ।"</string> <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"ବନ୍ଦ କରନ୍ତୁ"</string> <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"ବନ୍ଦ ହୋଇଛି"</string> <string name="clipboard_edit_text_done" msgid="4551887727694022409">"ହୋଇଗଲା"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"ଫୋଲ୍ଡେଡ"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"ଅନଫୋଲ୍ଡେଡ"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"ଷ୍ଟାଇଲସ ବେଟେରୀ <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"ଏକ ଚାର୍ଜର ସହ ଆପଣଙ୍କ ଷ୍ଟାଇଲସକୁ କନେକ୍ଟ କରନ୍ତୁ"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"ଷ୍ଟାଇଲସ ବେଟେରୀର ଚାର୍ଜ କମ ଅଛି"</string> <string name="video_camera" msgid="7654002575156149298">"ଭିଡିଓ କେମେରା"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ସର୍ଚ୍ଚ ସର୍ଟକଟ"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"ଆଇକନକୁ ସଙ୍କୁଚିତ କରନ୍ତୁ"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ଆଇକନକୁ ବିସ୍ତାର କରନ୍ତୁ"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"କିମ୍ବା"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"କୀବୋର୍ଡ ବେକଲାଇଟ"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$dରୁ %1$d ନମ୍ବର ଲେଭେଲ"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"ହୋମ କଣ୍ଟ୍ରୋଲ୍ସ"</string> diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml index a16a8a5cbc8c..dddda72d53e3 100644 --- a/packages/SystemUI/res/values-pa/strings.xml +++ b/packages/SystemUI/res/values-pa/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"ਕੱਲ੍ਹ ਨੂੰ ਆਪਣੇ ਆਪ ਚਾਲੂ ਹੋ ਜਾਵੇਗਾ"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"ਕਵਿੱਕ ਸ਼ੇਅਰ ਅਤੇ Find My Device ਵਰਗੀਆਂ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਬਲੂਟੁੱਥ ਵਰਤਦੀਆਂ ਹਨ"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"ਬਲੂਟੁੱਥ ਕੱਲ੍ਹ ਸਵੇਰੇ ਚਾਲੂ ਹੋ ਜਾਵੇਗਾ"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"ਆਡੀਓ ਸਾਂਝਾਕਰਨ"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"ਆਡੀਓ ਨੂੰ ਸਾਂਝਾ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ਬੈਟਰੀ"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ਆਡੀਓ"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ਹੈੱਡਸੈੱਟ"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"ਤੁਹਾਡੇ ਡੀਵਾਈਸ ਦੀ ਕਿਹੜੀ ਸੁਵਿਧਾ ਪ੍ਰਭਾਵਿਤ ਹੋਈ ਸੀ?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"ਸਮੱਸਿਆ ਦੀ ਕਿਸਮ ਚੁਣੋ"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"ਸਕ੍ਰੀਨ ਰਿਕਾਰਡ"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"ਕਾਰਗੁਜ਼ਾਰੀ"</string> + <string name="user_interface" msgid="3712869377953950887">"ਯੂਜ਼ਰ ਇੰਟਰਫ਼ੇਸ"</string> + <string name="thermal" msgid="6758074791325414831">"ਥਰਮਲ"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"ਇੱਕ ਹੱਥ ਮੋਡ"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"ਸੁਣਨ ਵਾਲੇ ਡੀਵਾਈਸ"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"ਕਿਰਿਆਸ਼ੀਲ"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"ਡਿਸਕਨੈਕਟ ਹੈ"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"ਸੁਣਨ ਵਾਲੇ ਡੀਵਾਈਸ"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"ਨਵਾਂ ਡੀਵਾਈਸ ਜੋੜਾਬੱਧ ਕਰੋ"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"\'ਨਵਾਂ ਡੀਵਾਈਸ ਜੋੜਾਬੱਧ ਕਰੋ\' \'ਤੇ ਕਲਿੱਕ ਕਰੋ"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"ਫੋਲਡਯੋਗ ਡੀਵਾਈਸ"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"ਅਣਫੋਲਡਯੋਗ ਡੀਵਾਈਸ"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"ਸਟਾਈਲਸ ਦੀ ਬੈਟਰੀ <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"ਆਪਣੇ ਸਟਾਈਲਸ ਨੂੰ ਚਾਰਜਰ ਨਾਲ ਕਨੈਕਟ ਕਰੋ"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"ਸਟਾਈਲਸ ਦੀ ਬੈਟਰੀ ਘੱਟ ਹੈ"</string> <string name="video_camera" msgid="7654002575156149298">"ਵੀਡੀਓ ਕੈਮਰਾ"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ਖੋਜ ਸੰਬੰਧੀ ਸ਼ਾਰਟਕੱਟ"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"ਪ੍ਰਤੀਕ ਨੂੰ ਸਮੇਟੋ"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ਪ੍ਰਤੀਕ ਦਾ ਵਿਸਤਾਰ ਕਰੋ"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ਜਾਂ"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"ਕੀ-ਬੋਰਡ ਬੈਕਲਾਈਟ"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d ਵਿੱਚੋਂ %1$d ਪੱਧਰ"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"ਹੋਮ ਕੰਟਰੋਲ"</string> diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml index 58561f112456..2dbcf2028c50 100644 --- a/packages/SystemUI/res/values-pl/strings.xml +++ b/packages/SystemUI/res/values-pl/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatycznie włącz ponownie jutro"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Bluetootha używają funkcje takie jak szybkie udostępnianie czy Znajdź moje urządzenie"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth włączy się jutro rano"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Udostępnianie dźwięku"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Udostępniam dźwięk"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> naładowania baterii"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Dźwięk"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Zestaw słuchawkowy"</string> @@ -366,10 +368,8 @@ <string name="thermal" msgid="6758074791325414831">"Termografia"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Tryb jednej ręki"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Urządzenia słuchowe"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktywny"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Rozłączono"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Urządzenia słuchowe"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Sparuj nowe urządzenie"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kliknij, aby sparować nowe urządzenie"</string> @@ -1324,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Skróty do wyszukiwania"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona zwijania"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona rozwijania"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"lub"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Podświetlenie klawiatury"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Poziom %1$d z %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Sterowanie domem"</string> diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml index 8f5c5192133c..992db40d04a6 100644 --- a/packages/SystemUI/res/values-pt-rBR/strings.xml +++ b/packages/SystemUI/res/values-pt-rBR/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Ativar automaticamente de novo amanhã"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Recursos como o Quick Share e o Encontre Meu Dispositivo usam Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"O Bluetooth será ativado amanhã de manhã"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Compartilhamento de áudio"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Compartilhando áudio"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Bateria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Áudio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Fone de ouvido"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Que parte da sua experiência no dispositivo foi afetada?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Selecionar tipo de problema"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Gravação de tela"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"Desempenho"</string> + <string name="user_interface" msgid="3712869377953950887">"Interface do usuário"</string> + <string name="thermal" msgid="6758074791325414831">"Térmico"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modo uma mão"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Aparelhos auditivos"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Ativos"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Desconectados"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Aparelhos auditivos"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Parear novo dispositivo"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Clique para parear o novo dispositivo"</string> @@ -639,7 +636,7 @@ <string name="volume_panel_hint_muted" msgid="1124844870181285320">"som desativado"</string> <string name="volume_panel_hint_vibrate" msgid="4136223145435914132">"vibrar"</string> <string name="media_output_label_title" msgid="872824698593182505">"Tocando <xliff:g id="LABEL">%s</xliff:g> em"</string> - <string name="media_output_title_without_playing" msgid="3825663683169305013">"Onde o áudio vai tocar?"</string> + <string name="media_output_title_without_playing" msgid="3825663683169305013">"Áudio definido para"</string> <string name="media_output_title_ongoing_call" msgid="208426888064112006">"Ligando"</string> <string name="system_ui_tuner" msgid="1471348823289954729">"Sintonizador System UI"</string> <string name="status_bar" msgid="4357390266055077437">"Barra de status"</string> @@ -1210,7 +1207,7 @@ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# app está ativo}one{# apps está ativo}many{# de apps estão ativos}other{# apps estão ativos}}"</string> <string name="fgs_dot_content_description" msgid="2865071539464777240">"Nova informação"</string> <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Apps ativos"</string> - <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Esses apps ficam ativos e em execução mesmo quando não estão em uso. Isso melhora a funcionalidade deles, mas também pode afetar a duração da bateria."</string> + <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Esses apps ficam ativos e em execução mesmo quando não estão em uso. Isso melhora a funcionalidade deles, mas também afeta a duração da bateria."</string> <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Parar"</string> <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Parado"</string> <string name="clipboard_edit_text_done" msgid="4551887727694022409">"Concluído"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"fechado"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"aberto"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Bateria da stylus em <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Conecte sua stylus a um carregador"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Bateria da stylus fraca"</string> <string name="video_camera" msgid="7654002575156149298">"Filmadora"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Atalhos de pesquisa"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ícone \"Fechar\""</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ícone \"Abrir\""</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ou"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Luz de fundo do teclado"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Nível %1$d de %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Automação residencial"</string> diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml index df735727b893..fb0bbfa88850 100644 --- a/packages/SystemUI/res/values-pt-rPT/strings.xml +++ b/packages/SystemUI/res/values-pt-rPT/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Reativar amanhã automaticamente"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Funcionalidades como a Partilha rápida e o serviço Localizar o meu dispositivo usam o Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"O Bluetooth vai ser ativado amanhã de manhã"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Partilha de áudio"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"A partilhar áudio"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> de bateria"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Áudio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Ausc. c/ mic. integ."</string> @@ -366,10 +368,8 @@ <string name="thermal" msgid="6758074791325414831">"Térmico"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modo para uma mão"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Dispositivos auditivos"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Ativos"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Desligados"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Dispositivos auditivos"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Sincronizar novo dispositivo"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Clique para sincronizar um novo dispositivo"</string> @@ -930,7 +930,7 @@ <string name="running_foreground_services_title" msgid="5137313173431186685">"Apps em execução em segundo plano"</string> <string name="running_foreground_services_msg" msgid="3009459259222695385">"Toque para obter detalhes acerca da utilização da bateria e dos dados"</string> <string name="mobile_data_disable_title" msgid="5366476131671617790">"Desativar os dados móveis?"</string> - <string name="mobile_data_disable_message" msgid="8604966027899770415">"Não terá acesso a dados ou à Internet através do operador <xliff:g id="CARRIER">%s</xliff:g>. A Internet estará disponível apenas por Wi-Fi."</string> + <string name="mobile_data_disable_message" msgid="8604966027899770415">"Não vai ter acesso aos dados nem à Internet através do operador <xliff:g id="CARRIER">%s</xliff:g>. A Internet vai estar disponível só por Wi-Fi."</string> <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"o seu operador"</string> <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"Mudar de novo para <xliff:g id="CARRIER">%s</xliff:g>?"</string> <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"Os dados móveis não vão mudar automaticamente com base na disponibilidade"</string> @@ -1324,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Atalhos de pesquisa"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ícone de reduzir"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ícone de expandir"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ou"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Luz do teclado"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Nível %1$d de %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Controlos domésticos"</string> diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml index 8f5c5192133c..992db40d04a6 100644 --- a/packages/SystemUI/res/values-pt/strings.xml +++ b/packages/SystemUI/res/values-pt/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Ativar automaticamente de novo amanhã"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Recursos como o Quick Share e o Encontre Meu Dispositivo usam Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"O Bluetooth será ativado amanhã de manhã"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Compartilhamento de áudio"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Compartilhando áudio"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Bateria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Áudio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Fone de ouvido"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Que parte da sua experiência no dispositivo foi afetada?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Selecionar tipo de problema"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Gravação de tela"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"Desempenho"</string> + <string name="user_interface" msgid="3712869377953950887">"Interface do usuário"</string> + <string name="thermal" msgid="6758074791325414831">"Térmico"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modo uma mão"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Aparelhos auditivos"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Ativos"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Desconectados"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Aparelhos auditivos"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Parear novo dispositivo"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Clique para parear o novo dispositivo"</string> @@ -639,7 +636,7 @@ <string name="volume_panel_hint_muted" msgid="1124844870181285320">"som desativado"</string> <string name="volume_panel_hint_vibrate" msgid="4136223145435914132">"vibrar"</string> <string name="media_output_label_title" msgid="872824698593182505">"Tocando <xliff:g id="LABEL">%s</xliff:g> em"</string> - <string name="media_output_title_without_playing" msgid="3825663683169305013">"Onde o áudio vai tocar?"</string> + <string name="media_output_title_without_playing" msgid="3825663683169305013">"Áudio definido para"</string> <string name="media_output_title_ongoing_call" msgid="208426888064112006">"Ligando"</string> <string name="system_ui_tuner" msgid="1471348823289954729">"Sintonizador System UI"</string> <string name="status_bar" msgid="4357390266055077437">"Barra de status"</string> @@ -1210,7 +1207,7 @@ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# app está ativo}one{# apps está ativo}many{# de apps estão ativos}other{# apps estão ativos}}"</string> <string name="fgs_dot_content_description" msgid="2865071539464777240">"Nova informação"</string> <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Apps ativos"</string> - <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Esses apps ficam ativos e em execução mesmo quando não estão em uso. Isso melhora a funcionalidade deles, mas também pode afetar a duração da bateria."</string> + <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Esses apps ficam ativos e em execução mesmo quando não estão em uso. Isso melhora a funcionalidade deles, mas também afeta a duração da bateria."</string> <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Parar"</string> <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Parado"</string> <string name="clipboard_edit_text_done" msgid="4551887727694022409">"Concluído"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"fechado"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"aberto"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Bateria da stylus em <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Conecte sua stylus a um carregador"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Bateria da stylus fraca"</string> <string name="video_camera" msgid="7654002575156149298">"Filmadora"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Atalhos de pesquisa"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ícone \"Fechar\""</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ícone \"Abrir\""</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ou"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Luz de fundo do teclado"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Nível %1$d de %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Automação residencial"</string> diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml index 366480ed96db..ff07fe9efedb 100644 --- a/packages/SystemUI/res/values-ro/strings.xml +++ b/packages/SystemUI/res/values-ro/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Activează din nou automat mâine"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Funcții precum Quick Share și Găsește-mi dispozitivul folosesc Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth se va activa mâine dimineață"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Permiterea accesului la audio"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Se permite accesul la conținutul audio"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Nivelul bateriei: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Căști"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Ce parte a experienței pe dispozitiv a fost afectată?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Selectează tipul problemei"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Înregistrarea ecranului"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"Performanță"</string> + <string name="user_interface" msgid="3712869377953950887">"Interfața de utilizare"</string> + <string name="thermal" msgid="6758074791325414831">"Termal"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modul cu o mână"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Aparate auditive"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Activ"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Deconectat"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Aparate auditive"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Asociază un nou dispozitiv"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Dă clic pentru a asocia un nou dispozitiv"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"închis"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"deschis"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Bateria creionului: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Conectează-ți creionul la un încărcător"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Nivelul bateriei creionului este scăzut"</string> <string name="video_camera" msgid="7654002575156149298">"Cameră video"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Comenzi directe de căutare"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Pictograma de restrângere"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Pictograma de extindere"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"sau"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Iluminarea din spate a tastaturii"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Nivelul %1$d din %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Comenzi pentru locuință"</string> diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml index f48e42823f0d..1900a8eb9f67 100644 --- a/packages/SystemUI/res/values-ru/strings.xml +++ b/packages/SystemUI/res/values-ru/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Включить завтра автоматически"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Bluetooth используется в таких функциях и сервисах, как \"Быстрая отправка\" и \"Найти устройство\""</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth включится завтра утром"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Отправка аудио"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Включена отправка аудио"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Заряд: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудиоустройство"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Гарнитура"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"С чем связана проблема, с которой вы столкнулись?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Выберите тип проблемы"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Запись экрана"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"Производительность"</string> + <string name="user_interface" msgid="3712869377953950887">"Интерфейс"</string> + <string name="thermal" msgid="6758074791325414831">"Тепловизор"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Режим управления одной рукой"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Слуховые аппараты"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Активно"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Не подключено"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Слуховые аппараты"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Подключить новое устройство"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Нажмите, чтобы подключить новое устройство"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"устройство сложено"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"устройство разложено"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Батарея стилуса: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Поставьте стилус на зарядку."</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Низкий заряд батареи стилуса"</string> <string name="video_camera" msgid="7654002575156149298">"Видеокамера"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Найти быстрые клавиши"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Значок \"Свернуть\""</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Значок \"Развернуть\""</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"или"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Подсветка клавиатуры"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Уровень %1$d из %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Управление домом"</string> diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml index 0e621c6eae5f..5d41eed4cbdd 100644 --- a/packages/SystemUI/res/values-si/strings.xml +++ b/packages/SystemUI/res/values-si/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"හෙට ස්වයංක්රීයව නැවත ක්රියාත්මක කරන්න"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"ඉක්මන් බෙදා ගැනීම සහ මගේ උපාංගය සෙවීම වැනි විශේෂාංග බ්ලූටූත් භාවිත කරයි"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"බ්ලූටූත් හෙට උදේ සක්රීය වෙයි"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"ශ්රව්ය බෙදා ගැනීම"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"ශ්රව්යය බෙදා ගැනීම"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"බැටරිය <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ශ්රව්ය"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"හෙඩ්සෙටය"</string> @@ -361,24 +363,19 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"ඔබේ උපාංග අත්දැකීමේ කුමන කොටසට බලපෑවේ ද?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"ගැටලු වර්ගය තෝරන්න"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"තිර පටිගත කිරීම"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"කාර්ය සාධනය"</string> + <string name="user_interface" msgid="3712869377953950887">"පරිශීලක අතුරු මුහුණත"</string> + <string name="thermal" msgid="6758074791325414831">"තාප"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"තනි අත් ප්රකාරය"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"ශ්රවණ උපාංග"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"ක්රියාකාරී"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"විසන්ධි විය"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"ශ්රවණ උපාංග"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"නව උපාංගය යුගල කරන්න"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"නව උපාංගය යුගල කිරීමට ක්ලික් කරන්න"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"පෙර සැකසීම යාවත්කාලීන කළ නොහැකි විය"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"පෙරසැකසුම"</string> - <string name="live_caption_title" msgid="8916875614623730005">"සජීවී සිරස්තලය"</string> + <string name="live_caption_title" msgid="8916875614623730005">"සජීවී සිරස්තල"</string> <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> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"නැවූ"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"නොනැවූ"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"පන්හිඳ බැටරිය <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"ඔබේ පන්හිඳ චාජරයකට සම්බන්ධ කරන්න"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"පන්හිඳ බැටරිය අඩුයි"</string> <string name="video_camera" msgid="7654002575156149298">"වීඩියෝ කැමරාව"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"කෙටි මං සොයන්න"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"හැකුළුම් නිරූපකය"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"දිගහැරීම් නිරූපකය"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"හෝ"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"යතුරු පුවරු පසු ආලෝකය"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$dන් %1$d වැනි මට්ටම"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"නිවෙස් පාලන"</string> diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml index ba72fb491ac9..eef2a01aa55c 100644 --- a/packages/SystemUI/res/values-sk/strings.xml +++ b/packages/SystemUI/res/values-sk/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automaticky zajtra znova zapnúť"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Funkcie ako Quick Share a Nájdi moje zariadenie používajú Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth sa zapne zajtra ráno"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Zdieľanie zvuku"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Zdieľa sa zvuk"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Batéria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Zvuk"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Náhlavná súprava"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Čo v zariadení bolo ovplyvnené?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Vyberte typ problému"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Rekordér obrazovky"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"Výkon"</string> + <string name="user_interface" msgid="3712869377953950887">"Používateľské rozhranie"</string> + <string name="thermal" msgid="6758074791325414831">"Termálne"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Režim jednej ruky"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Načúvacie zariadenia"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktívne"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Odpojené"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Načúvacie zariadenia"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Spárovať nové zariadenie"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kliknutím spárujete nové zariadenie"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"zložené"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"rozložené"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Batéria dotykového pera: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Pripojte dotykové pero k nabíjačke"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Stav batérie dotykového pera je nízky"</string> <string name="video_camera" msgid="7654002575156149298">"Videokamera"</string> @@ -1328,10 +1324,9 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Vyhľadávacie odkazy"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona zbalenia"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona rozbalenia"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"alebo"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Podsvietenie klávesnice"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"%1$d. úroveň z %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Ovládanie domácnosti"</string> - <string name="home_controls_dream_description" msgid="4644150952104035789">"Rýchlejšie ovládanie domácnosti v šetriči obrazov."</string> + <string name="home_controls_dream_description" msgid="4644150952104035789">"Rýchly prístup k ovládaniu domácnosti z šetriča obrazovky"</string> </resources> diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml index c6c1439f284d..facf72bfcda9 100644 --- a/packages/SystemUI/res/values-sl/strings.xml +++ b/packages/SystemUI/res/values-sl/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Samodejno znova vklopi jutri"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Funkcije, kot sta Hitro deljenje in Poišči mojo napravo, uporabljajo Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth se bo vklopil jutri zjutraj"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Deljenje zvoka"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Poteka deljenje zvoka"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Baterija na <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Zvok"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Slušalke z mikrofonom"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Na kateri del izkušnje z napravo je to vplivalo?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Izberite vrsto težave"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Snemanje zaslona"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"Učinkovitost delovanja"</string> + <string name="user_interface" msgid="3712869377953950887">"Uporabniški vmesnik"</string> + <string name="thermal" msgid="6758074791325414831">"Toplotno"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Enoročni način"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Slušni pripomočki"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktivno"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Brez povezave"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Slušni pripomočki"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Seznanitev nove naprave"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kliknite za seznanitev nove naprave"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"zaprto"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"razprto"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Napolnjenost baterije pisala: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Povežite pisalo s polnilnikom."</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Skoraj prazna baterija pisala"</string> <string name="video_camera" msgid="7654002575156149298">"Videokamera"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Bližnjice za iskanje"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona za strnitev"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona za razširitev"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ali"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Osvetlitev tipkovnice"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Stopnja %1$d od %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Kontrolniki za dom"</string> diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml index dc9407f8fd31..533363240fa5 100644 --- a/packages/SystemUI/res/values-sq/strings.xml +++ b/packages/SystemUI/res/values-sq/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Aktivizoje automatikisht sërish nesër"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Veçoritë e tilla si \"Ndarja e shpejtë\" dhe \"Gjej pajisjen time\" përdorin Bluetooth-in"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth-i do të aktivizohet nesër në mëngjes"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Ndarja e audios"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Audioja po ndahet"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> bateri"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Kufje me mikrofon"</string> @@ -361,12 +363,9 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Cila pjesë e përvojës me pajisjen është prekur?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Zgjidh llojin e problemit"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Regjistrim i ekranit"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"Performanca"</string> + <string name="user_interface" msgid="3712869377953950887">"Ndërfaqja e përdoruesit"</string> + <string name="thermal" msgid="6758074791325414831">"Termike"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modaliteti i përdorimit me një dorë"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Pajisje ndihmëse për dëgjimin"</string> <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> @@ -1275,8 +1274,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"palosur"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"shpalosur"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Bateria e stilolapsit: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Lidhe stilolapsin me një karikues"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Bateria e stilolapsit në nivel të ulët"</string> <string name="video_camera" msgid="7654002575156149298">"Videokamera"</string> @@ -1328,8 +1326,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Kërko për shkurtoret"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona e palosjes"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona e zgjerimit"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ose"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Drita e sfondit e tastierës"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Niveli: %1$d nga %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Kontrollet e shtëpisë"</string> diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml index 1228eef8c88b..f35d7d86085c 100644 --- a/packages/SystemUI/res/values-sr/strings.xml +++ b/packages/SystemUI/res/values-sr/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Аутоматски поново укључи сутра"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Функције као што су Quick Share и Пронађи мој уређај користе Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth ће се укључити сутра ујутру"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Дељење звука"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Дели се звук"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Ниво батерије је <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудио"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Слушалице"</string> @@ -366,10 +368,8 @@ <string name="thermal" msgid="6758074791325414831">"Термална камера"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Режим једном руком"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Слушни апарати"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Активно"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Веза је прекинута"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Слушни апарати"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Упари нови уређај"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Кликните да бисте упарили нов уређај"</string> @@ -1324,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Пречице претраге"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Икона за скупљање"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Икона за проширивање"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"или"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Позадинско осветљење тастатуре"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"%1$d. ниво од %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Контроле за дом"</string> diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml index cf2b93cabe75..535a4aa6c162 100644 --- a/packages/SystemUI/res/values-sv/strings.xml +++ b/packages/SystemUI/res/values-sv/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Aktivera automatiskt igen i morgon"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Funktioner som Snabbdelning och Hitta min enhet använder Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth aktiveras i morgon bitti"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Ljuddelning"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Delar ljud"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> batteri"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Ljud"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string> @@ -366,10 +368,8 @@ <string name="thermal" msgid="6758074791325414831">"Termisk"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Enhandsläge"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Hörhjälpmedel"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktiva"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Frånkopplade"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Hörhjälpmedel"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Parkoppla en ny enhet"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Klicka för att parkoppla en ny enhet"</string> @@ -1324,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Sökgenvägar"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikonen Komprimera"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikonen Utöka"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"eller"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Bakgrundsbelysning för tangentbord"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Nivå %1$d av %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Hemstyrning"</string> diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml index 1ee1d0cf6165..98a7e03873ba 100644 --- a/packages/SystemUI/res/values-sw/strings.xml +++ b/packages/SystemUI/res/values-sw/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Iwashe tena kesho kiotomatiki"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Vipengele kama vile Kutuma Haraka na Tafuta Kifaa Changu hutumia Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth itawaka kesho asubuhi"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Kusikiliza Pamoja"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Mnasikiliza Pamoja"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Chaji ya betri ni <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Sauti"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Vifaa vya sauti"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Ni sehemu gani ya matumizi ya kifaa iliathiriwa?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Chagua aina ya tatizo"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Rekodi ya skrini"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"Utendaji"</string> + <string name="user_interface" msgid="3712869377953950887">"Kiolesura"</string> + <string name="thermal" msgid="6758074791325414831">"Joto"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Hali ya kutumia kwa mkono mmoja"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Vifaa vya kusikilizia"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Vimeunganishwa"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Havijaunganishwa"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Vifaa vya kusikilizia"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Unganisha kifaa kipya"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Bofya ili uunganishe kifaa kipya"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"kimekunjwa"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"kimefunguliwa"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Betri ya stylus <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Unganisha stylus yako kwenye chaja"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Chaji ya betri ya Stylus imepungua"</string> <string name="video_camera" msgid="7654002575156149298">"Kamera ya video"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Njia mkato za kutafutia"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Kunja aikoni"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Panua aikoni"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"au"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Mwanga chini ya kibodi"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Kiwango cha %1$d kati ya %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Dhibiti Vifaa Nyumbani"</string> diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml index 6e95c3b5e2dc..896b55f3ac41 100644 --- a/packages/SystemUI/res/values-ta/strings.xml +++ b/packages/SystemUI/res/values-ta/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"நாளைக்குத் தானாகவே மீண்டும் இயக்கப்படுதல்"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"விரைவுப் பகிர்தல், Find My Device போன்ற அம்சங்கள் புளூடூத்தைப் பயன்படுத்துகின்றன"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"நாளை காலை புளூடூத் இயக்கப்படும்"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"ஆடியோ பகிர்வு"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"ஆடியோ பகிரப்படுகிறது"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> பேட்டரி"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ஆடியோ"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ஹெட்செட்"</string> @@ -305,7 +307,7 @@ <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"நெட்வொர்க்குகள் கிடைக்கவில்லை"</string> <string name="quick_settings_wifi_detail_empty_text" msgid="483130889414601732">"வைஃபை நெட்வொர்க்குகள் இல்லை"</string> <string name="quick_settings_wifi_secondary_label_transient" msgid="7501659015509357887">"ஆன் செய்கிறது…"</string> - <string name="quick_settings_cast_title" msgid="3033553249449938182">"Cast"</string> + <string name="quick_settings_cast_title" msgid="3033553249449938182">"அலைபரப்பு"</string> <string name="quick_settings_casting" msgid="1435880708719268055">"அனுப்புகிறது"</string> <string name="quick_settings_cast_device_default_name" msgid="6988469571141331700">"பெயரிடப்படாத சாதனம்"</string> <string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"சாதனங்கள் இல்லை"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"சாதன அனுபவத்தின் எந்தப் பகுதி பாதிக்கப்பட்டது?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"சிக்கல் வகையைத் தேர்வுசெய்க"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"ஸ்கிரீன் ரெக்கார்டு"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"செயல்திறன்"</string> + <string name="user_interface" msgid="3712869377953950887">"பயனர் இடைமுகம்"</string> + <string name="thermal" msgid="6758074791325414831">"தெர்மல்"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"ஒற்றைக் கைப் பயன்முறை"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"செவித்துணைக் கருவிகள்"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"செயலில் உள்ளது"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"இணைக்கப்படவில்லை"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"செவித்துணைக் கருவிகள்"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"புதிய சாதனத்தை இணை"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"புதிய சாதனத்தை இணைக்க கிளிக் செய்யலாம்"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"மடக்கப்பட்டது"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"விரிக்கப்பட்டது"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"ஸ்டைலஸ் பேட்டரி <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"உங்கள் ஸ்டைலஸைச் சார்ஜருடன் இணையுங்கள்"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"ஸ்டைலஸின் பேட்டரி குறைவாக உள்ளது"</string> <string name="video_camera" msgid="7654002575156149298">"வீடியோ கேமரா"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"தேடல் ஷார்ட்கட்கள்"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"சுருக்குவதற்கான ஐகான்"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"விரிவாக்குவதற்கான ஐகான்"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"அல்லது"</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-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml index 64d6f9e2419c..b88386ff05c4 100644 --- a/packages/SystemUI/res/values-te/strings.xml +++ b/packages/SystemUI/res/values-te/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"రేపు మళ్లీ ఆటోమేటిక్గా ఆన్ చేస్తుంది"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"క్విక్ షేర్, Find My Device వంటి ఫీచర్లు బ్లూటూత్ను ఉపయోగిస్తాయి"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"బ్లూటూత్ రేపు ఉదయం ఆన్ అవుతుంది"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"ఆడియో షేరింగ్"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"ఆడియోను షేర్ చేస్తున్నారు"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> బ్యాటరీ"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ఆడియో"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"హెడ్సెట్"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"మీ పరికర అనుభవంలో ఏ భాగం ప్రభావితమైంది?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"సమస్య రకాన్ని ఎంచుకోండి"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"స్క్రీన్ రికార్డ్"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"పనితీరు"</string> + <string name="user_interface" msgid="3712869377953950887">"యూజర్ ఇంటర్ఫేస్"</string> + <string name="thermal" msgid="6758074791325414831">"థర్మల్"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"వన్-హ్యాండెడ్ మోడ్"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"వినికిడి పరికరాలు"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"యాక్టివ్"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"డిస్కనెక్ట్ అయ్యింది"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"వినికిడి పరికరాలు"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"కొత్త పరికరాన్ని పెయిర్ చేయండి"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"కొత్త పరికరాన్ని పెయిర్ చేయడానికి క్లిక్ చేయండి"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"మడిచే సదుపాయం గల పరికరం"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"మడిచే సదుపాయం లేని పరికరం"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"స్టయిలస్ బ్యాటరీ <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"మీ స్టైలస్ను ఛార్జర్కి కనెక్ట్ చేయండి"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"తక్కువ స్టైలస్ బ్యాటరీ"</string> <string name="video_camera" msgid="7654002575156149298">"వీడియో కెమెరా"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"సెర్చ్ షార్ట్కట్లు"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"కుదించండి చిహ్నం"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"విస్తరించండి చిహ్నం"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"లేదా"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"కీబోర్డ్ బ్యాక్లైట్"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$dలో %1$dవ స్థాయి"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"హోమ్ కంట్రోల్స్"</string> diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml index 8a766299f354..12c676ee482f 100644 --- a/packages/SystemUI/res/values-th/strings.xml +++ b/packages/SystemUI/res/values-th/strings.xml @@ -279,10 +279,12 @@ <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ยกเลิกการเชื่อมต่อ"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"เปิดใช้งาน"</string> <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"เปิดอีกครั้งโดยอัตโนมัติในวันพรุ่งนี้"</string> - <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"ฟีเจอร์ต่างๆ เช่น Quick Share และหาอุปกรณ์ของฉัน ใช้บลูทูธ"</string> + <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"ฟีเจอร์ต่างๆ เช่น Quick Share และ \"หาอุปกรณ์ของฉัน\" ใช้บลูทูธ"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"บลูทูธจะเปิดพรุ่งนี้เช้า"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"การแชร์เสียง"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"กำลังแชร์เสียง"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"แบตเตอรี่ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"เสียง"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ชุดหูฟัง"</string> @@ -366,10 +368,8 @@ <string name="thermal" msgid="6758074791325414831">"ความร้อน"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"โหมดมือเดียว"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"เครื่องช่วยฟัง"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"ใช้งานอยู่"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"ยกเลิกการเชื่อมต่อแล้ว"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"เครื่องช่วยฟัง"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"จับคู่อุปกรณ์ใหม่"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"คลิกเพื่อจับคู่อุปกรณ์ใหม่"</string> @@ -1324,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ค้นหาแป้นพิมพ์ลัด"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"ไอคอนยุบ"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ไอคอนขยาย"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"หรือ"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"ไฟแบ็กไลต์ของแป้นพิมพ์"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"ระดับที่ %1$d จาก %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"ระบบควบคุมอุปกรณ์สมาร์ทโฮม"</string> diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml index e74139f693a9..84a4541f3694 100644 --- a/packages/SystemUI/res/values-tl/strings.xml +++ b/packages/SystemUI/res/values-tl/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Awtomatikong i-on ulit bukas"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Gumagamit ng Bluetooth ang mga feature tulad ng Quick Share at Hanapin ang Aking Device"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Mag-o-on ang Bluetooth bukas ng umaga"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Pag-share ng Audio"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Ibinabahagi ang Audio"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> na baterya"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string> @@ -366,10 +368,8 @@ <string name="thermal" msgid="6758074791325414831">"Thermal"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"One-hand mode"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Mga hearing device"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktibo"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Nadiskonekta"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Mga hearing device"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Magpares ng bagong device"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"I-click para magpares ng bagong device"</string> @@ -1324,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Mga shortcut ng paghahanap"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"I-collapse ang icon"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"I-expand ang icon"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"o"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Backlight ng keyboard"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Level %1$d sa %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Mga Home Control"</string> diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml index a66fbedd4769..c2df2c24c61d 100644 --- a/packages/SystemUI/res/values-tr/strings.xml +++ b/packages/SystemUI/res/values-tr/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Yarın otomatik olarak tekrar aç"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Quick Share ve Cihazımı Bul gibi özellikler Bluetooth\'u kullanır"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth yarın sabah açılacak"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Ses Paylaşımı"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Ses paylaşılıyor"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Pil düzeyi <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Ses"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Mikrofonlu kulaklık"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Cihaz deneyiminiz ne şekilde etkilendi?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Sorun türünü seçin"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Ekran kaydedicisi"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"Performans"</string> + <string name="user_interface" msgid="3712869377953950887">"Kullanıcı Arayüzü"</string> + <string name="thermal" msgid="6758074791325414831">"Termal"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Tek el modu"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"İşitme cihazları"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Etkin"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Bağlı değil"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"İşitme cihazları"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Yeni cihaz eşle"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Yeni cihaz eşlemek için tıklayın"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"katlanmış"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"katlanmamış"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Ekran kalemi pili: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Ekran kaleminizi bir şarj cihazına bağlayın"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Ekran kaleminin pil seviyesi düşük"</string> <string name="video_camera" msgid="7654002575156149298">"Video kamera"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Arama kısayolları"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Daralt simgesi"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Genişlet simgesi"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"veya"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Klavye aydınlatması"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Seviye %1$d / %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Ev Kontrolleri"</string> diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml index 5262d744336d..5fcfb3ea41c5 100644 --- a/packages/SystemUI/res/values-uk/strings.xml +++ b/packages/SystemUI/res/values-uk/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Автоматично ввімкнути знову завтра"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Такі функції, як швидкий обмін і \"Знайти пристрій\", використовують Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth увімкнеться завтра вранці"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Надсилання аудіо"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Надсилання аудіо"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> заряду акумулятора"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудіопристрій"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Гарнітура"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"На який аспект роботи пристрою вплинула проблема?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Виберіть тип проблеми"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Запис відео з екрана"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"Продуктивність"</string> + <string name="user_interface" msgid="3712869377953950887">"Інтерфейс користувача"</string> + <string name="thermal" msgid="6758074791325414831">"Нагрівання"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Режим керування однією рукою"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Слухові апарати"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Під’єднано"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Від’єднано"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Слухові апарати"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Підключити новий пристрій"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Натисніть, щоб підключити новий пристрій"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"складений"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"розкладений"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Заряд акумулятора стилуса: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Підключіть стилус до зарядного пристрою"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Низький заряд акумулятора стилуса"</string> <string name="video_camera" msgid="7654002575156149298">"Відеокамера"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Комбінації клавіш для пошуку"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Значок згортання"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Значок розгортання"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"або"</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-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml index 0d3f1feb406f..f08dd7eb13b7 100644 --- a/packages/SystemUI/res/values-ur/strings.xml +++ b/packages/SystemUI/res/values-ur/strings.xml @@ -278,11 +278,13 @@ <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"محفوظ ہے"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"غیر منسلک کریں"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"فعال کریں"</string> - <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"کل دوبارہ خودکار طور پر آن ہوگا"</string> + <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"کل دوبارہ خودکار طور پر آن کریں"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"فوری اشتراک اور \'میرا آلہ ڈھونڈیں\' جیسی خصوصیات بلوٹوتھ کا استعمال کرتی ہیں"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"بلوٹوتھ کل صبح آن ہو جائے گا"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"آڈیو کا اشتراک"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"آڈیو کا اشتراک ہو رہا ہے"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> بیٹری"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"آڈیو"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ہیڈ سیٹ"</string> @@ -366,10 +368,8 @@ <string name="thermal" msgid="6758074791325414831">"تھرمل"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"ایک ہاتھ کی وضع"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"سماعت کے آلات"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"فعال"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"غیر منسلک ہے"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"سماعت کے آلات"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"نئے آلے کا جوڑا بنائیں"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"نئے آلے کا جوڑا بنانے کے لیے کلک کریں"</string> @@ -1328,8 +1328,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"تلاش کے شارٹ کٹس"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"آئیکن سکیڑیں"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"آئیکن پھیلائیں"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"یا"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"کی بورڈ بیک لائٹ"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d میں سے %1$d کا لیول"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"ہوم کنٹرولز"</string> diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml index 3f3983d21fb0..4a04a337a6cd 100644 --- a/packages/SystemUI/res/values-uz/strings.xml +++ b/packages/SystemUI/res/values-uz/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Ertaga yana avtomatik yoqilsin"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Tezkor ulashuv va Qurilmamni top kabi funksiyalar Bluetooth ishlatadi"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth ertaga ertalab yoqiladi"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Audio ulashuvi"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Audio ulashuvi yoniq"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Batareya quvvati: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Garnitura"</string> @@ -366,10 +368,8 @@ <string name="thermal" msgid="6758074791325414831">"Termal"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Ixcham rejim"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Eshitish qurilmalari"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Faol"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Uzildi"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Eshitish qurilmalari"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Yangi qurilmani ulash"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Yangi qurilmani ulash uchun bosing"</string> @@ -1324,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Tezkor tugmalar qidiruvi"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Yigʻish belgisi"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Yoyish belgisi"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"yoki"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Klaviatura orqa yoritkichi"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Daraja: %1$d / %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Uy boshqaruvi"</string> diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml index dc4fa1241559..45d9165472cc 100644 --- a/packages/SystemUI/res/values-vi/strings.xml +++ b/packages/SystemUI/res/values-vi/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Tự động bật lại vào ngày mai"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Các tính năng như Chia sẻ nhanh và Tìm thiết bị của tôi đều sử dụng Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth sẽ bật vào sáng mai"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Chia sẻ âm thanh"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Đang chia sẻ âm thanh"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> pin"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Âm thanh"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Tai nghe"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Bạn gặp loại vấn đề gì khi dùng thiết bị?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Chọn loại vấn đề"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Ghi màn hình"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"Hiệu suất"</string> + <string name="user_interface" msgid="3712869377953950887">"Giao diện người dùng"</string> + <string name="thermal" msgid="6758074791325414831">"Nhiệt"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Chế độ một tay"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Thiết bị trợ thính"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Đang hoạt động"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Đã ngắt kết nối"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Thiết bị trợ thính"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Ghép nối thiết bị mới"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Nhấp để ghép nối thiết bị mới"</string> @@ -933,7 +930,7 @@ <string name="running_foreground_services_title" msgid="5137313173431186685">"Ứng dụng đang chạy trong nền"</string> <string name="running_foreground_services_msg" msgid="3009459259222695385">"Nhấn để biết chi tiết về mức sử dụng dữ liệu và pin"</string> <string name="mobile_data_disable_title" msgid="5366476131671617790">"Tắt dữ liệu di động?"</string> - <string name="mobile_data_disable_message" msgid="8604966027899770415">"Bạn sẽ không có quyền sử dụng dữ liệu hoặc truy cập Internet thông qua chế độ <xliff:g id="CARRIER">%s</xliff:g>. Bạn chỉ có thể truy cập Internet thông qua Wi-Fi."</string> + <string name="mobile_data_disable_message" msgid="8604966027899770415">"Bạn sẽ không có quyền sử dụng dữ liệu hoặc truy cập Internet thông qua mạng <xliff:g id="CARRIER">%s</xliff:g>. Bạn chỉ có thể truy cập Internet thông qua Wi-Fi."</string> <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"nhà mạng của bạn"</string> <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"Chuyển về <xliff:g id="CARRIER">%s</xliff:g>?"</string> <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"Dữ liệu di động sẽ không tự động chuyển dựa trên tình trạng phủ sóng"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"gập"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"mở"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Mức pin bút cảm ứng <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Hãy kết nối bút cảm ứng với bộ sạc"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Bút cảm ứng bị yếu pin"</string> <string name="video_camera" msgid="7654002575156149298">"Máy quay video"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Lối tắt tìm kiếm"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Biểu tượng Thu gọn"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Biểu tượng Mở rộng"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"hoặc"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Đèn nền bàn phím"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Độ sáng %1$d/%2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Điều khiển nhà"</string> diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml index 9b69f9ef2086..98a1187bd661 100644 --- a/packages/SystemUI/res/values-zh-rCN/strings.xml +++ b/packages/SystemUI/res/values-zh-rCN/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"明天自动重新开启"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"快速分享、查找我的设备等功能会使用蓝牙"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"蓝牙将在明天早上开启"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"音频分享"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"正在分享音频"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> 的电量"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"音频"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"耳机"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"设备体验的哪个方面受到影响?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"选择问题类型"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"屏幕录制"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"性能"</string> + <string name="user_interface" msgid="3712869377953950887">"界面"</string> + <string name="thermal" msgid="6758074791325414831">"散热"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"单手模式"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"助听装置"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"已连接"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"已断开连接"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"助听装置"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"与新设备配对"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"点击即可与新设备配对"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"折叠状态"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"展开状态"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"触控笔电量为 <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"请将触控笔连接充电器"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"触控笔电池电量低"</string> <string name="video_camera" msgid="7654002575156149298">"摄像机"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"搜索快捷键"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"收起图标"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"展开图标"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"或"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"键盘背光"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"第 %1$d 级,共 %2$d 级"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"家居控制"</string> diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml index 5c164ab23c28..edc624ff9c26 100644 --- a/packages/SystemUI/res/values-zh-rHK/strings.xml +++ b/packages/SystemUI/res/values-zh-rHK/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"明天自動重新開啟"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"「快速共享」和「尋找我的裝置」等功能都會使用藍牙"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"藍牙將於明天上午開啟"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"音訊分享功能"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"正在分享音訊"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"電量:<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"音訊"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"耳機"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"哪些裝置使用體驗受影響?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"選取問題類型"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"螢幕錄影"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"效能"</string> + <string name="user_interface" msgid="3712869377953950887">"使用者介面"</string> + <string name="thermal" msgid="6758074791325414831">"熱能"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"單手模式"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"助聽器"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"已連線"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"已中斷連線"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"助聽器"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"配對新裝置"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"㩒一下就可以配對新裝置"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"已摺疊"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"已打開"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"觸控筆電量:<xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"將觸控筆連接充電器"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"觸控筆電量不足"</string> <string name="video_camera" msgid="7654002575156149298">"攝影機"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"搜尋快速鍵"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"收合圖示"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"展開圖示"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"或"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"鍵盤背光"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"第 %1$d 級,共 %2$d 級"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"智能家居"</string> diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml index 0d50ad57768d..567928276f46 100644 --- a/packages/SystemUI/res/values-zh-rTW/strings.xml +++ b/packages/SystemUI/res/values-zh-rTW/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"明天自動重新開啟"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"「快速分享」和「尋找我的裝置」等功能都需要使用藍牙技術"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"藍牙會在明天早上開啟"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"音訊分享"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"正在分享音訊"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"電量:<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"音訊"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"耳機"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"哪些裝置使用體驗受到影響?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"選取問題類型"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"螢幕錄影"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"效能"</string> + <string name="user_interface" msgid="3712869377953950887">"使用者介面"</string> + <string name="thermal" msgid="6758074791325414831">"熱成像"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"單手模式"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"助聽器"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"已連線"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"連線中斷"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"助聽器"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"配對新裝置"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"按一下即可配對新裝置"</string> @@ -380,7 +377,7 @@ <string name="hearing_devices_preset_label" msgid="7878267405046232358">"預設"</string> <string name="live_caption_title" msgid="8916875614623730005">"即時字幕"</string> <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_camera_dialog_title" msgid="8807639852654305227">"解除封鎖裝置相機?"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"要將裝置的相機和麥克風解除封鎖嗎?"</string> <string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"執行後,具備麥克風存取權的所有應用程式和服務,都將可使用麥克風。"</string> <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"執行後,具備相機存取權的所有應用程式和服務,都將可使用相機。"</string> @@ -933,7 +930,7 @@ <string name="running_foreground_services_title" msgid="5137313173431186685">"在背景執行的應用程式"</string> <string name="running_foreground_services_msg" msgid="3009459259222695385">"輕觸即可查看電池和數據用量詳情"</string> <string name="mobile_data_disable_title" msgid="5366476131671617790">"要關閉行動數據嗎?"</string> - <string name="mobile_data_disable_message" msgid="8604966027899770415">"你將無法透過「<xliff:g id="CARRIER">%s</xliff:g>」使用行動數據或網際網路。你只能透過 Wi-Fi 使用網際網路。"</string> + <string name="mobile_data_disable_message" msgid="8604966027899770415">"你將無法透過「<xliff:g id="CARRIER">%s</xliff:g>」使用行動數據或網際網路,只能利用 Wi-Fi 上網。"</string> <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"你的電信業者"</string> <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"要切換回「<xliff:g id="CARRIER">%s</xliff:g>」嗎?"</string> <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"行動數據不會依據可用性自動切換"</string> @@ -1210,7 +1207,7 @@ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# 個應用程式正在運作}other{# 個應用程式正在運作}}"</string> <string name="fgs_dot_content_description" msgid="2865071539464777240">"新資訊"</string> <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"運作中的應用程式"</string> - <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"這些應用程式會在沒有使用的情況下持續運作。應用程式的實用度會因此提升,但也可能影響電池續航力。"</string> + <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"這些應用程式即使是在閒置狀態下,也會持續運作。應用程式的功能會因而提升,但也可能影響電池續航力。"</string> <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"停止"</string> <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"已停止"</string> <string name="clipboard_edit_text_done" msgid="4551887727694022409">"完成"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"已摺疊"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"已展開"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"觸控筆電量:<xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"將觸控筆接上充電器"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"觸控筆電力不足"</string> <string name="video_camera" msgid="7654002575156149298">"攝影機"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"搜尋快速鍵"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"收合圖示"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"展開圖示"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"或"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"鍵盤背光"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"第 %1$d 級,共 %2$d 級"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"居家控制"</string> diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml index 2e03bd502214..a0a57acaa362 100644 --- a/packages/SystemUI/res/values-zu/strings.xml +++ b/packages/SystemUI/res/values-zu/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Vula ngokuzenzekela futhi kusasa"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Izakhi ezifana nokuthi Ukwabelana Ngokushesha kanye nokuthi Thola Idivayisi Yami zisebenzisa i-Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"IBluetooth izovuleka kusasa ekuseni"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Ukwabelana Ngokuqoshiwe"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Ukwabelana Ngomsindo"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ibhethri"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Umsindo"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Ihedisethi"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Kuthinteke yiphi ingxenye yokusebenzisa idivayisi?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Khetha uhlobo lwenkinga"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Irekhodi lesikrini"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"Ukusebenza"</string> + <string name="user_interface" msgid="3712869377953950887">"Okusetshenziswa Kubonwa"</string> + <string name="thermal" msgid="6758074791325414831">"Ithermal"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Imodi yesandla esisodwa"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Izinsizakuzwa"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Kuyasebenza"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Inqamukile"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Izinsizakuzwa"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Bhangqa idivayisi entsha"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Chofoza ukuze ubhangqe idivayisi entsha"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"kugoqiwe"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"kuvuliwe"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Ibhethri lestylus <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Xhuma i-stylus yakho kushaja"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Ibhethri le-stylus liphansi"</string> <string name="video_camera" msgid="7654002575156149298">"Ikhamera yevidiyo"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Sesha izinqamuleli"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Goqa isithonjana"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Nweba isithonjana"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"noma"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Ilambu lekhibhodi"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Ileveli %1$d ka-%2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Izilawuli Zasekhaya"</string> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index d979abbe0af9..e825fc5c5b9a 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -1122,6 +1122,8 @@ <dimen name="biometric_prompt_panel_max_width">640dp</dimen> <dimen name="biometric_prompt_land_small_horizontal_guideline_padding">344dp</dimen> <dimen name="biometric_prompt_two_pane_udfps_horizontal_guideline_padding">114dp</dimen> + <dimen name="biometric_prompt_two_pane_udfps_shorter_content_width">216dp</dimen> + <dimen name="biometric_prompt_two_pane_udfps_shorter_horizontal_guideline_padding">661dp</dimen> <dimen name="biometric_prompt_two_pane_medium_horizontal_guideline_padding">640dp</dimen> <dimen name="biometric_prompt_one_pane_medium_top_guideline_padding">119dp</dimen> <dimen name="biometric_prompt_one_pane_medium_horizontal_guideline_padding">0dp</dimen> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 0017db68f85b..6a54c2f72bcc 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -319,6 +319,29 @@ <string name="screenrecord_save_error">Error saving screen recording</string> <!-- A toast message shown when the screen recording cannot be started due to a generic error [CHAR LIMIT=NONE] --> <string name="screenrecord_start_error">Error starting screen recording</string> + <!-- Title for a dialog shown to the user that will let them stop recording their screen [CHAR LIMIT=50] --> + <string name="screenrecord_stop_dialog_title">Stop recording screen?</string> + <!-- Text telling a user that they will stop recording their screen if they click the "Stop recording" button [CHAR LIMIT=100] --> + <string name="screenrecord_stop_dialog_message">You will stop recording your screen</string> + <!-- Button to stop a screen recording [CHAR LIMIT=35] --> + <string name="screenrecord_stop_dialog_button">Stop recording</string> + + <!-- Title for a dialog shown to the user that will let them stop sharing their screen to another app on the device [CHAR LIMIT=50] --> + <string name="share_to_app_stop_dialog_title">Stop sharing screen?</string> + <!-- Text telling a user that they will stop sharing their screen if they click the "Stop sharing" button [CHAR LIMIT=100] --> + <string name="share_to_app_stop_dialog_message">You will stop sharing your screen</string> + <!-- Button to stop screen sharing [CHAR LIMIT=35] --> + <string name="share_to_app_stop_dialog_button">Stop sharing</string> + + <!-- Title for a dialog shown to the user that will let them stop casting their screen to a different device [CHAR LIMIT=50] --> + <string name="cast_to_other_device_stop_dialog_title">Stop casting screen?</string> + <!-- Text telling a user that they will stop casting their screen to a different device if they click the "Stop casting" button [CHAR LIMIT=100] --> + <string name="cast_to_other_device_stop_dialog_message">You will stop casting your screen</string> + <!-- Button to stop screen casting to a different device [CHAR LIMIT=35] --> + <string name="cast_to_other_device_stop_dialog_button">Stop casting</string> + + <!-- Button to close a dialog without doing any action [CHAR LIMIT=20] --> + <string name="close_dialog_button">Close</string> <!-- Notification title displayed for issue recording [CHAR LIMIT=50]--> <string name="issuerecord_title">Issue Recorder</string> @@ -693,7 +716,7 @@ <!-- QuickSettings: Accessibility label to activate a device [CHAR LIMIT=NONE]--> <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate">activate</string> <!-- QuickSettings: Bluetooth auto on tomorrow [CHAR LIMIT=NONE]--> - <string name="turn_on_bluetooth_auto_tomorrow">Automatically turn on again tomorrow</string> + <string name="turn_on_bluetooth_auto_tomorrow">Automatically turn on tomorrow</string> <!-- QuickSettings: Bluetooth auto on info text when disabled [CHAR LIMIT=NONE]--> <string name="turn_on_bluetooth_auto_info_disabled">Features like Quick Share and Find My Device use Bluetooth</string> <!-- QuickSettings: Bluetooth auto on info text when enabled [CHAR LIMIT=NONE]--> @@ -3538,6 +3561,15 @@ --> <string name="shortcut_helper_key_combinations_or_separator">or</string> + <!-- Label for button opening tutorial for back gesture on touchpad [CHAR LIMIT=NONE] --> + <string name="touchpad_tutorial_back_gesture_button">Back gesture</string> + <!-- Label for button opening tutorial for back gesture on touchpad [CHAR LIMIT=NONE] --> + <string name="touchpad_tutorial_home_gesture_button">Home gesture</string> + <!-- Label for button opening tutorial on using Action key from keyboard [CHAR LIMIT=NONE] --> + <string name="touchpad_tutorial_action_key_button">Action key</string> + <!-- Label for button finishing touchpad tutorial [CHAR LIMIT=NONE] --> + <string name="touchpad_tutorial_done_button">Done</string> + <!-- Content description for keyboard backlight brightness dialog [CHAR LIMIT=NONE] --> <string name="keyboard_backlight_dialog_title">Keyboard backlight</string> <!-- Content description for keyboard backlight brightness value [CHAR LIMIT=NONE] --> diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimator.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimator.kt index 7a8c82cee32a..4fd54566bfb6 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimator.kt +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimator.kt @@ -37,10 +37,17 @@ class UnfoldConstantTranslateAnimator( private lateinit var rootView: ViewGroup private var translationMax = 0f + /** + * Initializes the animator, it is allowed to call this method multiple times, for example + * to update the rootView or maximum translation + */ fun init(rootView: ViewGroup, translationMax: Float) { + if (!::rootView.isInitialized) { + progressProvider.addCallback(this) + } + this.rootView = rootView this.translationMax = translationMax - progressProvider.addCallback(this) } override fun onTransitionStarted() { diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt index 7170be6124d2..19d918f5c556 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt @@ -17,16 +17,21 @@ package com.android.keyguard import android.content.Context -import android.view.ViewGroup -import com.android.systemui.res.R +import android.view.View +import com.android.systemui.keyguard.MigrateClocksToBlueprint +import com.android.systemui.keyguard.ui.view.KeyguardRootView import com.android.systemui.plugins.statusbar.StatusBarStateController -import com.android.systemui.statusbar.StatusBarState.KEYGUARD +import com.android.systemui.res.R +import com.android.systemui.shared.R as sharedR +import com.android.systemui.shade.NotificationShadeWindowView import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator.Direction.END import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator.Direction.START import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator.ViewIdToTranslate +import com.android.systemui.statusbar.StatusBarState.KEYGUARD import com.android.systemui.unfold.SysUIUnfoldScope -import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider +import com.android.systemui.unfold.UnfoldTransitionProgressProvider +import com.android.systemui.unfold.dagger.NaturalRotation import javax.inject.Inject /** @@ -38,8 +43,10 @@ class KeyguardUnfoldTransition @Inject constructor( private val context: Context, + private val keyguardRootView: KeyguardRootView, + private val shadeWindowView: NotificationShadeWindowView, statusBarStateController: StatusBarStateController, - unfoldProgressProvider: NaturalRotationUnfoldProgressProvider, + @NaturalRotation unfoldProgressProvider: UnfoldTransitionProgressProvider, ) { /** Certain views only need to move if they are not currently centered */ @@ -50,27 +57,94 @@ constructor( private val filterKeyguard: () -> Boolean = { statusBarStateController.getState() == KEYGUARD } private val translateAnimator by lazy { + val smartSpaceViews = if (MigrateClocksToBlueprint.isEnabled) { + // Use scrollX instead of translationX as translation is already set by [AodBurnInLayer] + val scrollXTranslation = { view: View, translation: Float -> + view.scrollX = -translation.toInt() + } + + setOf( + ViewIdToTranslate( + viewId = sharedR.id.date_smartspace_view, + direction = START, + shouldBeAnimated = filterKeyguard, + translateFunc = scrollXTranslation, + ), + ViewIdToTranslate( + viewId = sharedR.id.bc_smartspace_view, + direction = START, + shouldBeAnimated = filterKeyguard, + translateFunc = scrollXTranslation, + ), + ViewIdToTranslate( + viewId = sharedR.id.weather_smartspace_view, + direction = START, + shouldBeAnimated = filterKeyguard, + translateFunc = scrollXTranslation, + ) + ) + } else { + setOf(ViewIdToTranslate( + viewId = R.id.keyguard_status_area, + direction = START, + shouldBeAnimated = filterKeyguard, + translateFunc = { view, value -> + (view as? KeyguardStatusAreaView)?.translateXFromUnfold = value + } + )) + } + UnfoldConstantTranslateAnimator( viewsIdToTranslate = setOf( - ViewIdToTranslate(R.id.keyguard_status_area, START, filterKeyguard, - { view, value -> - (view as? KeyguardStatusAreaView)?.translateXFromUnfold = value - }), ViewIdToTranslate( - R.id.lockscreen_clock_view_large, START, filterKeyguardAndSplitShadeOnly), - ViewIdToTranslate(R.id.lockscreen_clock_view, START, filterKeyguard), + viewId = R.id.lockscreen_clock_view_large, + direction = START, + shouldBeAnimated = filterKeyguardAndSplitShadeOnly + ), + ViewIdToTranslate( + viewId = R.id.lockscreen_clock_view, + direction = START, + shouldBeAnimated = filterKeyguard + ), ViewIdToTranslate( - R.id.notification_stack_scroller, END, filterKeyguardAndSplitShadeOnly), - ViewIdToTranslate(R.id.start_button, START, filterKeyguard), - ViewIdToTranslate(R.id.end_button, END, filterKeyguard)), - progressProvider = unfoldProgressProvider) + viewId = R.id.notification_stack_scroller, + direction = END, + shouldBeAnimated = filterKeyguardAndSplitShadeOnly + ) + ) + smartSpaceViews, + progressProvider = unfoldProgressProvider + ) } - /** Relies on the [parent] to locate views to translate. */ - fun setup(parent: ViewGroup) { + private val shortcutButtonsAnimator by lazy { + UnfoldConstantTranslateAnimator( + viewsIdToTranslate = + setOf( + ViewIdToTranslate( + viewId = R.id.start_button, + direction = START, + shouldBeAnimated = filterKeyguard + ), + ViewIdToTranslate( + viewId = R.id.end_button, + direction = END, + shouldBeAnimated = filterKeyguard + ) + ), + progressProvider = unfoldProgressProvider + ) + } + + /** Initializes the keyguard fold/unfold transition */ + fun setup() { val translationMax = context.resources.getDimensionPixelSize(R.dimen.keyguard_unfold_translation_x).toFloat() - translateAnimator.init(parent, translationMax) + + translateAnimator.init(shadeWindowView, translationMax) + + // Use keyguard root view as there is another instance of start/end buttons with the same ID + // outside of the keyguard root view + shortcutButtonsAnimator.init(keyguardRootView, translationMax) } } diff --git a/packages/SystemUI/src/com/android/systemui/ambient/statusbar/ui/AmbientStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/ambient/statusbar/ui/AmbientStatusBarViewController.java index a242d5a25238..abdc3338b7f2 100644 --- a/packages/SystemUI/src/com/android/systemui/ambient/statusbar/ui/AmbientStatusBarViewController.java +++ b/packages/SystemUI/src/com/android/systemui/ambient/statusbar/ui/AmbientStatusBarViewController.java @@ -30,6 +30,7 @@ import android.view.View; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; +import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dreams.DreamLogger; import com.android.systemui.dreams.DreamOverlayNotificationCountProvider; @@ -82,9 +83,11 @@ public class AmbientStatusBarViewController extends ViewController<AmbientStatus private final Executor mMainExecutor; private final List<DreamOverlayStatusBarItemsProvider.StatusBarItem> mExtraStatusBarItems = new ArrayList<>(); + private final CommunalSceneInteractor mCommunalSceneInteractor; private final DreamLogger mLogger; private boolean mIsAttached; + private boolean mCommunalVisible; // Whether dream entry animations are finished. private boolean mEntryAnimationsFinished = false; @@ -140,6 +143,7 @@ public class AmbientStatusBarViewController extends ViewController<AmbientStatus DreamOverlayStateController dreamOverlayStateController, UserTracker userTracker, WifiInteractor wifiInteractor, + CommunalSceneInteractor communalSceneInteractor, @DreamLog LogBuffer logBuffer) { super(view); mResources = resources; @@ -155,6 +159,7 @@ public class AmbientStatusBarViewController extends ViewController<AmbientStatus mDreamOverlayStateController = dreamOverlayStateController; mUserTracker = userTracker; mWifiInteractor = wifiInteractor; + mCommunalSceneInteractor = communalSceneInteractor; mLogger = new DreamLogger(logBuffer, TAG); // Register to receive show/hide updates for the system status bar. Our custom status bar @@ -172,6 +177,12 @@ public class AmbientStatusBarViewController extends ViewController<AmbientStatus network -> updateWifiUnavailableStatusIcon( network instanceof WifiNetworkModel.Active)); + collectFlow( + mView, + mCommunalSceneInteractor.isCommunalVisible(), + this::onCommunalVisibleChanged + ); + mNextAlarmController.addCallback(mNextAlarmCallback); updateAlarmStatusIcon(); @@ -230,9 +241,15 @@ public class AmbientStatusBarViewController extends ViewController<AmbientStatus mView.setTranslationY(translationY); } + private void onCommunalVisibleChanged(boolean visible) { + mCommunalVisible = visible; + updateVisibility(); + } + private boolean shouldShowStatusBar() { - return !mDreamOverlayStateController.isLowLightActive() - && !mStatusBarWindowStateController.windowIsShowing(); + return (!mDreamOverlayStateController.isLowLightActive() + && !mStatusBarWindowStateController.windowIsShowing()) + || mCommunalVisible; } @VisibleForTesting diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt index b4d53d02448d..a87ee24da2f7 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt @@ -364,7 +364,7 @@ object BiometricViewSizeBinder { if (midGuideline != null) { val left = if (bounds.left >= 0) { - bounds.left + abs(bounds.left) } else { view.width - abs(bounds.left) } @@ -372,7 +372,7 @@ object BiometricViewSizeBinder { if (bounds.right >= 0) { view.width - abs(bounds.right) } else { - bounds.right + abs(bounds.right) } val mid = (left + right) / 2 mediumConstraintSet.setGuidelineBegin(midGuideline.id, mid) 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 68a3f5d11fb6..7d494a5cf229 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 @@ -30,6 +30,7 @@ import android.hardware.biometrics.BiometricPrompt import android.hardware.biometrics.Flags.customBiometricPrompt import android.hardware.biometrics.PromptContentView import android.os.UserHandle +import android.text.TextPaint import android.util.Log import android.util.RotationUtils import android.view.HapticFeedbackConstants @@ -52,6 +53,7 @@ import com.android.systemui.biometrics.shared.model.PromptKind import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.keyguard.shared.model.AcquiredFingerprintAuthenticationStatus import com.android.systemui.res.R +import com.android.systemui.util.kotlin.combine import javax.inject.Inject import kotlinx.coroutines.Job import kotlinx.coroutines.coroutineScope @@ -260,15 +262,15 @@ constructor( val position: Flow<PromptPosition> = combine( _forceLargeSize, + promptKind, displayStateInteractor.isLargeScreen, displayStateInteractor.currentRotation, modalities - ) { forceLarge, isLargeScreen, rotation, modalities -> + ) { forceLarge, promptKind, isLargeScreen, rotation, modalities -> when { forceLarge || isLargeScreen || - promptKind.value.isOnePaneNoSensorLandscapeBiometric() -> - PromptPosition.Bottom + promptKind.isOnePaneNoSensorLandscapeBiometric() -> PromptPosition.Bottom rotation == DisplayRotation.ROTATION_90 -> PromptPosition.Right rotation == DisplayRotation.ROTATION_270 -> PromptPosition.Left rotation == DisplayRotation.ROTATION_180 && modalities.hasUdfps -> @@ -308,6 +310,10 @@ constructor( context.resources.getDimensionPixelSize( R.dimen.biometric_prompt_two_pane_udfps_horizontal_guideline_padding ) + private val udfpsHorizontalShorterGuidelinePadding = + context.resources.getDimensionPixelSize( + R.dimen.biometric_prompt_two_pane_udfps_shorter_horizontal_guideline_padding + ) private val mediumTopGuidelinePadding = context.resources.getDimensionPixelSize( R.dimen.biometric_prompt_one_pane_medium_top_guideline_padding @@ -449,47 +455,6 @@ constructor( } } - /** - * Rect for positioning prompt guidelines (left, top, right, unused) - * - * Negative values are used to signify that guideline measuring should be flipped, measuring - * from opposite side of the screen - */ - val guidelineBounds: Flow<Rect> = - combine(iconPosition, promptKind, size, position, modalities) { - _, - promptKind, - size, - position, - modalities -> - when (position) { - PromptPosition.Bottom -> - if (promptKind.isOnePaneNoSensorLandscapeBiometric()) { - Rect(0, 0, 0, 0) - } else { - Rect(0, mediumTopGuidelinePadding, 0, 0) - } - PromptPosition.Right -> - if (size.isSmall) { - Rect(-smallHorizontalGuidelinePadding, 0, 0, 0) - } else if (modalities.hasUdfps) { - Rect(udfpsHorizontalGuidelinePadding, 0, 0, 0) - } else { - Rect(-mediumHorizontalGuidelinePadding, 0, 0, 0) - } - PromptPosition.Left -> - if (size.isSmall) { - Rect(0, 0, -smallHorizontalGuidelinePadding, 0) - } else if (modalities.hasUdfps) { - Rect(0, 0, udfpsHorizontalGuidelinePadding, 0) - } else { - Rect(0, 0, -mediumHorizontalGuidelinePadding, 0) - } - PromptPosition.Top -> Rect() - } - } - .distinctUntilChanged() - /** Padding for prompt UI elements */ val promptPadding: Flow<Rect> = combine(size, displayStateInteractor.currentRotation) { size, rotation -> @@ -556,6 +521,81 @@ constructor( if (contentView == null) description else "" } + private val hasOnlyOneLineTitle: Flow<Boolean> = + combine(title, subtitle, contentView, description) { + title, + subtitle, + contentView, + description -> + if (subtitle.isNotEmpty() || contentView != null || description.isNotEmpty()) { + false + } else { + val maxWidth = + context.resources.getDimensionPixelSize( + R.dimen.biometric_prompt_two_pane_udfps_shorter_content_width + ) + val attributes = + context.obtainStyledAttributes( + R.style.TextAppearance_AuthCredential_Title, + intArrayOf(android.R.attr.textSize) + ) + val paint = TextPaint() + paint.textSize = attributes.getDimensionPixelSize(0, 0).toFloat() + val textWidth = paint.measureText(title) + attributes.recycle() + textWidth / maxWidth <= 1 + } + } + + /** + * Rect for positioning prompt guidelines (left, top, right, unused) + * + * Negative values are used to signify that guideline measuring should be flipped, measuring + * from opposite side of the screen + */ + val guidelineBounds: Flow<Rect> = + combine(iconPosition, promptKind, size, position, modalities, hasOnlyOneLineTitle) { + _, + promptKind, + size, + position, + modalities, + hasOnlyOneLineTitle -> + var left = 0 + var top = 0 + var right = 0 + when (position) { + PromptPosition.Bottom -> { + val noSensorLandscape = promptKind.isOnePaneNoSensorLandscapeBiometric() + top = if (noSensorLandscape) 0 else mediumTopGuidelinePadding + } + PromptPosition.Right -> + left = getHorizontalPadding(size, modalities, hasOnlyOneLineTitle) + PromptPosition.Left -> + right = getHorizontalPadding(size, modalities, hasOnlyOneLineTitle) + PromptPosition.Top -> {} + } + Rect(left, top, right, 0) + } + .distinctUntilChanged() + + private fun getHorizontalPadding( + size: PromptSize, + modalities: BiometricModalities, + hasOnlyOneLineTitle: Boolean + ) = + if (size.isSmall) { + -smallHorizontalGuidelinePadding + } else if (modalities.hasUdfps) { + if (hasOnlyOneLineTitle) { + -udfpsHorizontalShorterGuidelinePadding + } else { + udfpsHorizontalGuidelinePadding + } + } else { + -mediumHorizontalGuidelinePadding + } + /** If the indicator (help, error) message should be shown. */ val isIndicatorMessageVisible: Flow<Boolean> = combine( diff --git a/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt b/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt index d522c7e16603..88c3f9f6af2e 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/CommunalSceneStartable.kt @@ -208,7 +208,10 @@ constructor( // doing any translation. CommunalScenes.Communal } - to == KeyguardState.GONE -> CommunalScenes.Blank + // Transitioning to Blank scene when entering the edit mode will be handled separately + // with custom animations. + to == KeyguardState.GONE && !communalInteractor.editModeOpen.value -> + CommunalScenes.Blank !docked && !KeyguardState.deviceIsAwakeInState(to) -> { // If the user taps the screen and wakes the device within this timeout, we don't // want to dismiss the hub diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt index 3e513f829831..00678a81b572 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalInteractor.kt @@ -38,6 +38,7 @@ import com.android.systemui.communal.shared.model.CommunalContentSize.HALF import com.android.systemui.communal.shared.model.CommunalContentSize.THIRD import com.android.systemui.communal.shared.model.CommunalScenes import com.android.systemui.communal.shared.model.CommunalWidgetContentModel +import com.android.systemui.communal.shared.model.EditModeState import com.android.systemui.communal.widgets.CommunalAppWidgetHost import com.android.systemui.communal.widgets.EditWidgetsActivityStarter import com.android.systemui.communal.widgets.WidgetConfigurator @@ -307,6 +308,7 @@ constructor( preselectedKey: String? = null, shouldOpenWidgetPickerOnStart: Boolean = false, ) { + communalSceneInteractor.setEditModeState(EditModeState.STARTING) editWidgetsActivityStarter.startActivity(preselectedKey, shouldOpenWidgetPickerOnStart) } diff --git a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt index 0dab67c98761..20d8a2a191cf 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/domain/interactor/CommunalSceneInteractor.kt @@ -22,14 +22,17 @@ import com.android.compose.animation.scene.TransitionKey import com.android.systemui.communal.data.repository.CommunalSceneRepository import com.android.systemui.communal.domain.model.CommunalTransitionProgressModel import com.android.systemui.communal.shared.model.CommunalScenes +import com.android.systemui.communal.shared.model.EditModeState import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf @@ -57,11 +60,32 @@ constructor( communalSceneRepository.snapToScene(newScene, delayMillis) } + /** Immediately snaps to the new scene when activity is started. */ + fun snapToSceneForActivityStart(newScene: SceneKey, delayMillis: Long = 0) { + // skip if we're starting edit mode activity, as it will be handled later by changeScene + // with transition key [CommunalTransitionKeys.ToEditMode]. + if (_editModeState.value == EditModeState.STARTING) { + return + } + snapToScene(newScene, delayMillis) + } + /** * Target scene as requested by the underlying [SceneTransitionLayout] or through [changeScene]. */ val currentScene: Flow<SceneKey> = communalSceneRepository.currentScene + private val _editModeState = MutableStateFlow<EditModeState?>(null) + /** + * Current state for glanceable hub edit mode, used to chain the animations when transitioning + * between communal scene and the edit mode activity. + */ + val editModeState = _editModeState.asStateFlow() + + fun setEditModeState(value: EditModeState?) { + _editModeState.value = value + } + /** Transition state of the hub mode. */ val transitionState: StateFlow<ObservableTransitionState> = communalSceneRepository.transitionState diff --git a/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalTransitionKeys.kt b/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalTransitionKeys.kt index 73cfb5286e1c..11fb2332dc5f 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalTransitionKeys.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/shared/model/CommunalTransitionKeys.kt @@ -26,6 +26,10 @@ import com.android.compose.animation.scene.TransitionKey object CommunalTransitionKeys { /** Fades the glanceable hub without any translation */ val SimpleFade = TransitionKey("SimpleFade") + /** Transition from the glanceable hub before entering edit mode */ + val ToEditMode = TransitionKey("ToEditMode") + /** Transition to the glanceable hub after exiting edit mode */ + val FromEditMode = TransitionKey("FromEditMode") /** Immediately transitions without any delay */ val Immediately = TransitionKey("Immediately") } diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/TutorialSelectionViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/shared/model/EditModeState.kt index 1a8272d8e7e3..ace9c2e171e0 100644 --- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/TutorialSelectionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/shared/model/EditModeState.kt @@ -14,17 +14,15 @@ * limitations under the License. */ -package com.android.systemui.touchpad.tutorial.ui +package com.android.systemui.communal.shared.model -import androidx.lifecycle.ViewModel -import androidx.lifecycle.ViewModelProvider - -class TutorialSelectionViewModel : ViewModel() - -class TutorialSelectionViewModelFactory : ViewModelProvider.Factory { - - @Suppress("UNCHECKED_CAST") - override fun <T : ViewModel> create(modelClass: Class<T>): T { - return TutorialSelectionViewModel() as T - } +/** + * Models the state of the edit mode activity. Used to chain the animation during the transition + * between the hub on communal scene, and the edit mode activity after unlocking the keyguard. + */ +enum class EditModeState(val value: Int) { + // starting activity after dismissing keyguard + STARTING(0), + // activity content is showing + SHOWING(1), } 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 8cd5603bdc7f..cc9073040c96 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 @@ -25,6 +25,7 @@ import com.android.compose.animation.scene.TransitionKey import com.android.systemui.communal.domain.interactor.CommunalInteractor import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor import com.android.systemui.communal.domain.model.CommunalContentModel +import com.android.systemui.communal.shared.model.EditModeState import com.android.systemui.communal.widgets.WidgetConfigurator import com.android.systemui.media.controls.ui.view.MediaHost import kotlinx.coroutines.flow.Flow @@ -34,12 +35,15 @@ import kotlinx.coroutines.flow.flowOf /** The base view model for the communal hub. */ abstract class BaseCommunalViewModel( - private val communalSceneInteractor: CommunalSceneInteractor, + val communalSceneInteractor: CommunalSceneInteractor, private val communalInteractor: CommunalInteractor, val mediaHost: MediaHost, ) { val currentScene: Flow<SceneKey> = communalSceneInteractor.currentScene + /** Used to animate showing or hiding the communal content. */ + open val isCommunalContentVisible: Flow<Boolean> = MutableStateFlow(false) + /** Whether communal hub should be focused by accessibility tools. */ open val isFocusable: Flow<Boolean> = MutableStateFlow(false) @@ -63,6 +67,8 @@ abstract class BaseCommunalViewModel( communalSceneInteractor.changeScene(scene, transitionKey) } + fun setEditModeState(state: EditModeState?) = communalSceneInteractor.setEditModeState(state) + /** * Updates the transition state of the hub [SceneTransitionLayout]. * 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 5312aec16b1b..c0c586191834 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 @@ -30,21 +30,27 @@ import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor import com.android.systemui.communal.domain.model.CommunalContentModel import com.android.systemui.communal.shared.log.CommunalUiEvent +import com.android.systemui.communal.shared.model.EditModeState import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor +import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.log.LogBuffer import com.android.systemui.log.core.Logger import com.android.systemui.log.dagger.CommunalLog import com.android.systemui.media.controls.ui.view.MediaHost import com.android.systemui.media.dagger.MediaModule import com.android.systemui.res.R +import com.android.systemui.util.kotlin.BooleanFlowOperators.allOf import javax.inject.Inject import javax.inject.Named import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.withContext @@ -56,6 +62,7 @@ constructor( communalSceneInteractor: CommunalSceneInteractor, private val communalInteractor: CommunalInteractor, private val communalSettingsInteractor: CommunalSettingsInteractor, + keyguardTransitionInteractor: KeyguardTransitionInteractor, @Named(MediaModule.COMMUNAL_HUB) mediaHost: MediaHost, private val uiEventLogger: UiEventLogger, @CommunalLog logBuffer: LogBuffer, @@ -66,6 +73,20 @@ constructor( override val isEditMode = true + override val isCommunalContentVisible: Flow<Boolean> = + communalSceneInteractor.editModeState.map { it == EditModeState.SHOWING } + + /** + * Emits when edit mode activity can show, after we've transitioned to [KeyguardState.GONE] + * and edit mode is open. + */ + val canShowEditMode = + allOf( + keyguardTransitionInteractor.isFinishedInState(KeyguardState.GONE), + communalInteractor.editModeOpen + ) + .filter { it } + // Only widgets are editable. override val communalContent: Flow<List<CommunalContentModel>> = communalInteractor.widgetContent.onEach { models -> @@ -172,6 +193,11 @@ constructor( /** Sets whether edit mode is currently open */ fun setEditModeOpen(isOpen: Boolean) = communalInteractor.setEditModeOpen(isOpen) + /** Called when exiting the edit mode, before transitioning back to the communal scene. */ + fun cleanupEditModeState() { + communalSceneInteractor.setEditModeState(null) + } + companion object { private const val TAG = "CommunalEditModeViewModel" diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt index c6fa5a84c195..11247df5e2fd 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt @@ -125,6 +125,8 @@ constructor( logger.d({ "Content updated: $str1" }) { str1 = models.joinToString { it.key } } } + override val isCommunalContentVisible: Flow<Boolean> = MutableStateFlow(true) + /** * Freeze the content flow, when an activity is about to show, like starting a timer via voice: * 1) in handheld mode, use the keyguard occluded state; diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt index 50477b11a7c2..40df6cecaeb8 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/EditWidgetsActivity.kt @@ -37,6 +37,7 @@ import com.android.internal.logging.UiEventLogger import com.android.systemui.communal.shared.log.CommunalUiEvent import com.android.systemui.communal.shared.model.CommunalScenes import com.android.systemui.communal.shared.model.CommunalTransitionKeys +import com.android.systemui.communal.shared.model.EditModeState import com.android.systemui.communal.ui.compose.CommunalHub import com.android.systemui.communal.ui.viewmodel.CommunalEditModeViewModel import com.android.systemui.communal.util.WidgetPickerIntentUtils.getWidgetExtraFromIntent @@ -107,6 +108,7 @@ constructor( override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + listenForTransitionAndChangeScene() communalViewModel.setEditModeOpen(true) @@ -138,6 +140,22 @@ constructor( } } + // Handle scene change to show the activity and animate in its content + private fun listenForTransitionAndChangeScene() { + lifecycleScope.launch { + communalViewModel.canShowEditMode.collect { + communalViewModel.changeScene( + CommunalScenes.Blank, + CommunalTransitionKeys.ToEditMode + ) + // wait till transitioned to Blank scene, then animate in communal content in + // edit mode + communalViewModel.currentScene.first { it == CommunalScenes.Blank } + communalViewModel.setEditModeState(EditModeState.SHOWING) + } + } + } + private fun onOpenWidgetPicker() { lifecycleScope.launch { communalViewModel.onOpenWidgetPicker( @@ -150,9 +168,11 @@ constructor( private fun onEditDone() { lifecycleScope.launch { + communalViewModel.cleanupEditModeState() + communalViewModel.changeScene( CommunalScenes.Communal, - CommunalTransitionKeys.SimpleFade + CommunalTransitionKeys.FromEditMode ) // Wait for the current scene to be idle on communal. @@ -192,6 +212,7 @@ constructor( override fun onDestroy() { super.onDestroy() + communalViewModel.cleanupEditModeState() communalViewModel.setEditModeOpen(false) } diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt index 9d110e665ebe..10d6881e4f93 100644 --- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt @@ -29,6 +29,7 @@ import com.android.systemui.keyguard.domain.interactor.TrustInteractor import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.util.kotlin.Quad +import com.android.systemui.utils.coroutines.flow.mapLatestConflated import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -37,6 +38,7 @@ import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map @@ -96,7 +98,16 @@ constructor( .filter { currentScene -> currentScene == Scenes.Gone || currentScene == Scenes.Lockscreen } - .map { it == Scenes.Gone } + .mapLatestConflated { scene -> + if (scene == Scenes.Gone) { + // Make sure device unlock status is definitely unlocked before we consider the + // device "entered". + deviceUnlockedInteractor.deviceUnlockStatus.first { it.isUnlocked } + true + } else { + false + } + } .stateIn( scope = applicationScope, started = SharingStarted.Eagerly, diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt index 4ac0c5683d06..9d6c2bf4c1d1 100644 --- a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt @@ -16,6 +16,7 @@ package com.android.systemui.display.data.repository +import android.annotation.SuppressLint import android.hardware.display.DisplayManager import android.hardware.display.DisplayManager.DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED import android.hardware.display.DisplayManager.DisplayListener @@ -27,6 +28,7 @@ import android.util.Log import android.view.Display import com.android.app.tracing.FlowTracing.traceEach import com.android.app.tracing.traceSection +import com.android.systemui.Flags import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background @@ -42,11 +44,12 @@ import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged -import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.filterIsInstance import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onStart +import kotlinx.coroutines.flow.scan import kotlinx.coroutines.flow.shareIn import kotlinx.coroutines.flow.stateIn @@ -91,6 +94,7 @@ interface DisplayRepository { } @SysUISingleton +@SuppressLint("SharedFlowCreation") class DisplayRepositoryImpl @Inject constructor( @@ -132,44 +136,77 @@ constructor( allDisplayEvents.filterIsInstance<DisplayEvent.Changed>().map { event -> event.displayId } override val displayAdditionEvent: Flow<Display?> = - allDisplayEvents.filterIsInstance<DisplayEvent.Added>().map { - displayManager.getDisplay(it.displayId) - } + allDisplayEvents.filterIsInstance<DisplayEvent.Added>().map { getDisplay(it.displayId) } + + private val oldEnabledDisplays: Flow<Set<Display>> = + allDisplayEvents + .map { getDisplays() } + .shareIn(bgApplicationScope, started = SharingStarted.WhileSubscribed(), replay = 1) + + /** Propagate to the listeners only enabled displays */ + private val enabledDisplayIds: Flow<Set<Int>> = + if (Flags.enableEfficientDisplayRepository()) { + allDisplayEvents + .scan(initial = emptySet()) { previousIds: Set<Int>, event: DisplayEvent -> + val id = event.displayId + when (event) { + is DisplayEvent.Removed -> previousIds - id + is DisplayEvent.Added, + is DisplayEvent.Changed -> previousIds + id + } + } + .shareIn( + bgApplicationScope, + started = SharingStarted.WhileSubscribed(), + replay = 1 + ) + } else { + oldEnabledDisplays.map { enabledDisplaysSet -> + enabledDisplaysSet.map { it.displayId }.toSet() + } + } + .debugLog("enabledDisplayIds") /** * Represents displays that went though the [DisplayListener.onDisplayAdded] callback. * * Those are commonly the ones provided by [DisplayManager.getDisplays] by default. */ - private val enabledDisplays = - allDisplayEvents - .map { getDisplays() } - .shareIn(bgApplicationScope, started = SharingStarted.WhileSubscribed(), replay = 1) + private val enabledDisplays: Flow<Set<Display>> = + if (Flags.enableEfficientDisplayRepository()) { + enabledDisplayIds + .mapElementsLazily { displayId -> getDisplay(displayId) } + .flowOn(backgroundCoroutineDispatcher) + .debugLog("enabledDisplayIds") + } else { + oldEnabledDisplays + } + /** + * Represents displays that went though the [DisplayListener.onDisplayAdded] callback. + * + * Those are commonly the ones provided by [DisplayManager.getDisplays] by default. + */ override val displays: Flow<Set<Display>> = enabledDisplays private fun getDisplays(): Set<Display> = traceSection("$TAG#getDisplays()") { displayManager.displays?.toSet() ?: emptySet() } - /** Propagate to the listeners only enabled displays */ - private val enabledDisplayIds: Flow<Set<Int>> = - enabledDisplays - .map { enabledDisplaysSet -> enabledDisplaysSet.map { it.displayId }.toSet() } - .debugLog("enabledDisplayIds") - private val _ignoredDisplayIds = MutableStateFlow<Set<Int>>(emptySet()) private val ignoredDisplayIds: Flow<Set<Int>> = _ignoredDisplayIds.debugLog("ignoredDisplayIds") private fun getInitialConnectedDisplays(): Set<Int> = - displayManager - .getDisplays(DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED) - .map { it.displayId } - .toSet() - .also { - if (DEBUG) { - Log.d(TAG, "getInitialConnectedDisplays: $it") + traceSection("$TAG#getInitialConnectedDisplays") { + displayManager + .getDisplays(DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED) + .map { it.displayId } + .toSet() + .also { + if (DEBUG) { + Log.d(TAG, "getInitialConnectedDisplays: $it") + } } - } + } /* keeps connected displays until they are disconnected. */ private val connectedDisplayIds: StateFlow<Set<Int>> = @@ -230,6 +267,9 @@ constructor( private fun getDisplayType(displayId: Int): Int? = traceSection("$TAG#getDisplayType") { displayManager.getDisplay(displayId)?.type } + private fun getDisplay(displayId: Int): Display? = + traceSection("$TAG#getDisplay") { displayManager.getDisplay(displayId) } + /** * Pending displays are the ones connected, but not enabled and not ignored. * @@ -307,6 +347,30 @@ constructor( } } + /** + * Maps a set of T to a set of V, minimizing the number of `createValue` calls taking into + * account the diff between each root flow emission. + * + * This is needed to minimize the number of [getDisplay] in this class. Note that if the + * [createValue] returns a null element, it will not be added in the output set. + */ + private fun <T, V> Flow<Set<T>>.mapElementsLazily(createValue: (T) -> V?): Flow<Set<V>> { + var initialSet = emptySet<T>() + val currentMap = mutableMapOf<T, V>() + var resultSet = emptySet<V>() + return onEach { currentSet -> + val removed = initialSet - currentSet + val added = currentSet - initialSet + if (added.isNotEmpty() || removed.isNotEmpty()) { + added.forEach { key: T -> createValue(key)?.let { currentMap[key] = it } } + removed.forEach { key: T -> currentMap.remove(key) } + resultSet = currentMap.values.toSet() // Creates a **copy** of values + } + initialSet = currentSet + } + .map { resultSet } + } + private companion object { const val TAG = "DisplayRepository" val DEBUG = Log.isLoggable(TAG, Log.DEBUG) || Compile.IS_DEBUG diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt index 14eb97296091..49d00af61a24 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractor.kt @@ -114,13 +114,26 @@ constructor( powerInteractor.isAwake, keyguardInteractor.isAodAvailable, communalInteractor.isIdleOnCommunal, + communalInteractor.editModeOpen, keyguardInteractor.isKeyguardOccluded, ) .filterRelevantKeyguardStateAnd { (isAlternateBouncerShowing, isPrimaryBouncerShowing, _, _, _) -> !isAlternateBouncerShowing && !isPrimaryBouncerShowing } - .collect { (_, _, isAwake, isAodAvailable, isIdleOnCommunal, isOccluded) -> + .collect { + ( + _, + _, + isAwake, + isAodAvailable, + isIdleOnCommunal, + isCommunalEditMode, + isOccluded) -> + // When unlocking over glanceable hub to enter edit mode, transitioning directly + // to GONE prevents the lockscreen flash. Let listenForAlternateBouncerToGone + // handle it. + if (isCommunalEditMode) return@collect val to = if (!isAwake) { if (isAodAvailable) { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt index 48660f25907a..a595eebe6c84 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt @@ -50,6 +50,7 @@ import com.android.systemui.shade.data.repository.ShadeRepository import com.android.systemui.statusbar.CommandQueue import com.android.systemui.statusbar.notification.NotificationUtils.interpolate import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor +import com.android.systemui.util.kotlin.Utils.Companion.sample as sampleCombine import com.android.systemui.util.kotlin.Utils.Companion.sampleFilter import com.android.systemui.util.kotlin.pairwise import com.android.systemui.util.kotlin.sample @@ -77,7 +78,7 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.merge import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.stateIn -import com.android.systemui.util.kotlin.Utils.Companion.sample as sampleCombine +import kotlinx.coroutines.flow.transform /** * Encapsulates business-logic related to the keyguard but not to a more specific part within it. @@ -111,7 +112,7 @@ constructor( keyguardTransitionInteractor.transitionState.map { step -> val startingProgress = lastChangeStep.value val progress = step.value - if (step.to == AOD && progress >= startingProgress) { + if (step.to == AOD && progress >= startingProgress && startingProgress < 1f) { val adjustedProgress = ((progress - startingProgress) / (1F - startingProgress)).coerceIn( 0F, @@ -327,28 +328,35 @@ constructor( * This uses legacyShadeExpansion to process swipe up events. In the future, the touch input * signal should be sent directly to transitions. */ - val dismissAlpha: Flow<Float?> = + val dismissAlpha: Flow<Float> = shadeRepository.legacyShadeExpansion - .filter { it < 1f } .sampleCombine( statusBarState, keyguardTransitionInteractor.currentKeyguardState, + keyguardTransitionInteractor.transitionState, isKeyguardDismissible, ) - .map { - (legacyShadeExpansion, statusBarState, currentKeyguardState, isKeyguardDismissible) - -> + .filter { (_, _, _, step, _) -> !step.transitionState.isTransitioning() } + .transform { + ( + legacyShadeExpansion, + statusBarState, + currentKeyguardState, + step, + isKeyguardDismissible) -> if ( statusBarState == StatusBarState.KEYGUARD && isKeyguardDismissible && - currentKeyguardState == LOCKSCREEN + currentKeyguardState == LOCKSCREEN && + legacyShadeExpansion != 1f ) { - MathUtils.constrainedMap(0f, 1f, 0.95f, 1f, legacyShadeExpansion) - } else { - null + emit(MathUtils.constrainedMap(0f, 1f, 0.95f, 1f, legacyShadeExpansion)) + } else if (legacyShadeExpansion == 0f || legacyShadeExpansion == 1f) { + // Resets alpha state + emit(1f) } } - .onStart { emit(null) } + .onStart { emit(1f) } .distinctUntilChanged() val keyguardTranslationY: Flow<Float> = diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt index 3a43b1c480dc..069f65b4efa6 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt @@ -105,7 +105,17 @@ constructor( is ObservableTransitionState.Transition -> when { transitionState.fromScene == Scenes.Lockscreen && - transitionState.toScene == Scenes.Gone -> flowOf(true) + transitionState.toScene == Scenes.Gone -> + sceneInteractor + .get() + .isTransitionUserInputOngoing + .flatMapLatestConflated { isUserInputOngoing -> + if (isUserInputOngoing) { + isDeviceEntered + } else { + flowOf(true) + } + } transitionState.fromScene == Scenes.Bouncer && transitionState.toScene == Scenes.Gone -> transitionState.progress.map { progress -> 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 940f42377b42..f5c521a3d8c7 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 @@ -32,6 +32,7 @@ import com.android.systemui.res.R import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.shade.shared.model.ShadeMode import com.android.systemui.statusbar.notification.domain.interactor.NotificationsKeyguardInteractor +import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerAlwaysOnDisplayViewModel import com.android.systemui.statusbar.ui.SystemBarUtilsProxy import javax.inject.Inject import kotlinx.coroutines.CoroutineScope @@ -48,6 +49,7 @@ class KeyguardClockViewModel constructor( keyguardClockInteractor: KeyguardClockInteractor, @Application private val applicationScope: CoroutineScope, + aodNotificationIconViewModel: NotificationIconContainerAlwaysOnDisplayViewModel, notifsKeyguardInteractor: NotificationsKeyguardInteractor, @get:VisibleForTesting val shadeInteractor: ShadeInteractor, private val systemBarUtils: SystemBarUtilsProxy, @@ -105,8 +107,13 @@ constructor( initialValue = false ) - val isAodIconsVisible: StateFlow<Boolean> = - notifsKeyguardInteractor.areNotificationsFullyHidden.stateIn( + // To translate elements below smartspace in weather clock to avoid overlapping between date + // element in weather clock and aod icons + val isAodIconsVisible: StateFlow<Boolean> = combine(aodNotificationIconViewModel.icons.map { + it.visibleIcons.isNotEmpty() + }, notifsKeyguardInteractor.areNotificationsFullyHidden) { hasIcons, visible -> + hasIcons && visible + }.stateIn( scope = applicationScope, started = SharingStarted.WhileSubscribed(), initialValue = false diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt index 5027524e7a4b..aefff7d0f79b 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt @@ -66,7 +66,6 @@ import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combineTransform import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filter -import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.merge import kotlinx.coroutines.flow.onStart @@ -236,7 +235,7 @@ constructor( // value emitted by any of them. Do not add flows that cannot make this guarantee. merge( alphaOnShadeExpansion, - keyguardInteractor.dismissAlpha.filterNotNull(), + keyguardInteractor.dismissAlpha, alternateBouncerToGoneTransitionViewModel.lockscreenAlpha(viewState), aodToGoneTransitionViewModel.lockscreenAlpha(viewState), aodToLockscreenTransitionViewModel.lockscreenAlpha(viewState), diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/data/repository/MediaFilterRepository.kt b/packages/SystemUI/src/com/android/systemui/media/controls/data/repository/MediaFilterRepository.kt index 220d32663098..6a91d1b15325 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/data/repository/MediaFilterRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/data/repository/MediaFilterRepository.kt @@ -218,7 +218,7 @@ constructor( mediaFromRecPackageName = null _currentMedia.value = sortedMap.values.toList() } - } else if (sortedMap.size > sortedMedia.size && it.active) { + } else if (sortedMap.size > _currentMedia.value.size && it.active) { _currentMedia.value = sortedMap.values.toList() } else { // When loading an update for an existing media control. diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaItem.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaItem.java index fbb84defc372..4496b258bde4 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaItem.java +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaItem.java @@ -16,7 +16,9 @@ package com.android.systemui.media.dialog; -import androidx.annotation.IntDef; +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; import com.android.settingslib.media.MediaDevice; import com.android.systemui.res.R; @@ -46,40 +48,50 @@ public class MediaItem { int TYPE_PAIR_NEW_DEVICE = 2; } - public MediaItem() { - this.mMediaDeviceOptional = Optional.empty(); - this.mTitle = null; - this.mMediaItemType = MediaItemType.TYPE_PAIR_NEW_DEVICE; + /** + * Returns a new {@link MediaItemType#TYPE_DEVICE} {@link MediaItem} with its {@link + * #getMediaDevice() media device} set to {@code device} and its title set to {@code device}'s + * name. + */ + public static MediaItem createDeviceMediaItem(@NonNull MediaDevice device) { + return new MediaItem(device, device.getName(), MediaItemType.TYPE_DEVICE); } - public MediaItem(String title, int mediaItemType) { - this.mMediaDeviceOptional = Optional.empty(); - this.mTitle = title; - this.mMediaItemType = mediaItemType; + /** + * Returns a new {@link MediaItemType#TYPE_PAIR_NEW_DEVICE} {@link MediaItem} with both {@link + * #getMediaDevice() media device} and title set to {@code null}. + */ + public static MediaItem createPairNewDeviceMediaItem() { + return new MediaItem( + /* device */ null, /* title */ null, MediaItemType.TYPE_PAIR_NEW_DEVICE); + } + + /** + * Returns a new {@link MediaItemType#TYPE_GROUP_DIVIDER} {@link MediaItem} with the specified + * title and a {@code null} {@link #getMediaDevice() media device}. + */ + public static MediaItem createGroupDividerMediaItem(@Nullable String title) { + return new MediaItem(/* device */ null, title, MediaItemType.TYPE_GROUP_DIVIDER); } - public MediaItem(MediaDevice mediaDevice) { - this.mMediaDeviceOptional = Optional.of(mediaDevice); - this.mTitle = mediaDevice.getName(); - this.mMediaItemType = MediaItemType.TYPE_DEVICE; + private MediaItem( + @Nullable MediaDevice device, @Nullable String title, @MediaItemType int type) { + this.mMediaDeviceOptional = Optional.ofNullable(device); + this.mTitle = title; + this.mMediaItemType = type; } public Optional<MediaDevice> getMediaDevice() { return mMediaDeviceOptional; } - /** - * Get layout id based on media item Type. - */ - public static int getMediaLayoutId(int mediaItemType) { - switch (mediaItemType) { - case MediaItemType.TYPE_DEVICE: - case MediaItemType.TYPE_PAIR_NEW_DEVICE: - return R.layout.media_output_list_item_advanced; - case MediaItemType.TYPE_GROUP_DIVIDER: - default: - return R.layout.media_output_list_group_divider; - } + /** Get layout id based on media item Type. */ + public static int getMediaLayoutId(@MediaItemType int mediaItemType) { + return switch (mediaItemType) { + case MediaItemType.TYPE_DEVICE, MediaItemType.TYPE_PAIR_NEW_DEVICE -> + R.layout.media_output_list_item_advanced; + default -> R.layout.media_output_list_group_divider; + }; } public String getTitle() { diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java index c2cfdbe410b8..1e8656311f1d 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java @@ -68,6 +68,7 @@ import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import androidx.core.graphics.drawable.IconCompat; +import com.android.internal.annotations.GuardedBy; import com.android.settingslib.RestrictedLockUtilsInternal; import com.android.settingslib.Utils; import com.android.settingslib.bluetooth.BluetoothUtils; @@ -656,10 +657,16 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback, if (DEBUG) { Log.d(TAG, "No connected media device or muting expected device exist."); } - return categorizeMediaItems(null, devices, needToHandleMutingExpectedDevice); + return categorizeMediaItemsLocked( + /* connectedMediaDevice */ null, + devices, + needToHandleMutingExpectedDevice); } // selected device exist - return categorizeMediaItems(connectedMediaDevice, devices, false); + return categorizeMediaItemsLocked( + connectedMediaDevice, + devices, + /* needToHandleMutingExpectedDevice */ false); } // To keep the same list order final List<MediaDevice> targetMediaDevices = new ArrayList<>(); @@ -682,8 +689,9 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback, devices.removeAll(targetMediaDevices); targetMediaDevices.addAll(devices); } - List<MediaItem> finalMediaItems = targetMediaDevices.stream().map( - MediaItem::new).collect(Collectors.toList()); + List<MediaItem> finalMediaItems = targetMediaDevices.stream() + .map(MediaItem::createDeviceMediaItem) + .collect(Collectors.toList()); dividerItems.forEach(finalMediaItems::add); attachConnectNewDeviceItemIfNeeded(finalMediaItems); return finalMediaItems; @@ -694,51 +702,50 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback, * Initial categorization of current devices, will not be called for updates to the devices * list. */ - private List<MediaItem> categorizeMediaItems(MediaDevice connectedMediaDevice, + @GuardedBy("mMediaDevicesLock") + private List<MediaItem> categorizeMediaItemsLocked(MediaDevice connectedMediaDevice, List<MediaDevice> devices, boolean needToHandleMutingExpectedDevice) { - synchronized (mMediaDevicesLock) { - List<MediaItem> finalMediaItems = new ArrayList<>(); - Set<String> selectedDevicesIds = getSelectedMediaDevice().stream().map( - MediaDevice::getId).collect(Collectors.toSet()); - if (connectedMediaDevice != null) { - selectedDevicesIds.add(connectedMediaDevice.getId()); - } - boolean suggestedDeviceAdded = false; - boolean displayGroupAdded = false; - for (MediaDevice device : devices) { - if (needToHandleMutingExpectedDevice && device.isMutingExpectedDevice()) { - finalMediaItems.add(0, new MediaItem(device)); - } else if (!needToHandleMutingExpectedDevice && selectedDevicesIds.contains( - device.getId())) { - finalMediaItems.add(0, new MediaItem(device)); - } else { - if (device.isSuggestedDevice() && !suggestedDeviceAdded) { - attachGroupDivider(finalMediaItems, mContext.getString( - R.string.media_output_group_title_suggested_device)); - suggestedDeviceAdded = true; - } else if (!device.isSuggestedDevice() && !displayGroupAdded) { - attachGroupDivider(finalMediaItems, mContext.getString( - R.string.media_output_group_title_speakers_and_displays)); - displayGroupAdded = true; - } - finalMediaItems.add(new MediaItem(device)); + List<MediaItem> finalMediaItems = new ArrayList<>(); + Set<String> selectedDevicesIds = getSelectedMediaDevice().stream() + .map(MediaDevice::getId) + .collect(Collectors.toSet()); + if (connectedMediaDevice != null) { + selectedDevicesIds.add(connectedMediaDevice.getId()); + } + boolean suggestedDeviceAdded = false; + boolean displayGroupAdded = false; + for (MediaDevice device : devices) { + if (needToHandleMutingExpectedDevice && device.isMutingExpectedDevice()) { + finalMediaItems.add(0, MediaItem.createDeviceMediaItem(device)); + } else if (!needToHandleMutingExpectedDevice && selectedDevicesIds.contains( + device.getId())) { + finalMediaItems.add(0, MediaItem.createDeviceMediaItem(device)); + } else { + if (device.isSuggestedDevice() && !suggestedDeviceAdded) { + attachGroupDivider(finalMediaItems, mContext.getString( + R.string.media_output_group_title_suggested_device)); + suggestedDeviceAdded = true; + } else if (!device.isSuggestedDevice() && !displayGroupAdded) { + attachGroupDivider(finalMediaItems, mContext.getString( + R.string.media_output_group_title_speakers_and_displays)); + displayGroupAdded = true; } + finalMediaItems.add(MediaItem.createDeviceMediaItem(device)); } - attachConnectNewDeviceItemIfNeeded(finalMediaItems); - return finalMediaItems; } + attachConnectNewDeviceItemIfNeeded(finalMediaItems); + return finalMediaItems; } private void attachGroupDivider(List<MediaItem> mediaItems, String title) { - mediaItems.add( - new MediaItem(title, MediaItem.MediaItemType.TYPE_GROUP_DIVIDER)); + mediaItems.add(MediaItem.createGroupDividerMediaItem(title)); } private void attachConnectNewDeviceItemIfNeeded(List<MediaItem> mediaItems) { // Attach "Connect a device" item only when current output is not remote and not a group if (!isCurrentConnectedDeviceRemote() && getSelectedMediaDevice().size() == 1) { - mediaItems.add(new MediaItem()); + mediaItems.add(MediaItem.createPairNewDeviceMediaItem()); } } diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/data/model/MediaProjectionState.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/data/model/MediaProjectionState.kt index 1d5f6f52000c..de300b2ff900 100644 --- a/packages/SystemUI/src/com/android/systemui/mediaprojection/data/model/MediaProjectionState.kt +++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/data/model/MediaProjectionState.kt @@ -20,7 +20,21 @@ import android.app.ActivityManager.RunningTaskInfo /** Represents the state of media projection. */ sealed interface MediaProjectionState { - object NotProjecting : MediaProjectionState - object EntireScreen : MediaProjectionState - data class SingleTask(val task: RunningTaskInfo) : MediaProjectionState + /** There is no media being projected. */ + data object NotProjecting : MediaProjectionState + + /** + * Media is currently being projected. + * + * @property hostPackage the package name of the app that is receiving the content of the media + * projection (aka which app the phone screen contents are being sent to). + */ + sealed class Projecting(open val hostPackage: String) : MediaProjectionState { + /** The entire screen is being projected. */ + data class EntireScreen(override val hostPackage: String) : Projecting(hostPackage) + + /** Only a single task is being projected. */ + data class SingleTask(override val hostPackage: String, val task: RunningTaskInfo) : + Projecting(hostPackage) + } } diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepository.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepository.kt index 3ce0a1e00910..8a9adc7a5c88 100644 --- a/packages/SystemUI/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepository.kt @@ -64,6 +64,10 @@ constructor( } } + override suspend fun stopProjecting() { + withContext(backgroundDispatcher) { mediaProjectionManager.stopActiveProjection() } + } + override val mediaProjectionState: Flow<MediaProjectionState> = conflatedCallbackFlow { val callback = @@ -83,7 +87,9 @@ constructor( session: ContentRecordingSession? ) { Log.d(TAG, "MediaProjectionManager.Callback#onSessionStarted: $session") - launch { trySendWithFailureLogging(stateForSession(session), TAG) } + launch { + trySendWithFailureLogging(stateForSession(info, session), TAG) + } } } mediaProjectionManager.addCallback(callback, handler) @@ -95,19 +101,23 @@ constructor( initialValue = MediaProjectionState.NotProjecting, ) - private suspend fun stateForSession(session: ContentRecordingSession?): MediaProjectionState { + private suspend fun stateForSession( + info: MediaProjectionInfo, + session: ContentRecordingSession? + ): MediaProjectionState { if (session == null) { return MediaProjectionState.NotProjecting } + + val hostPackage = info.packageName if (session.contentToRecord == RECORD_CONTENT_DISPLAY || session.tokenToRecord == null) { - return MediaProjectionState.EntireScreen + return MediaProjectionState.Projecting.EntireScreen(hostPackage) } val matchingTask = tasksRepository.findRunningTaskFromWindowContainerToken( checkNotNull(session.tokenToRecord) - ) - ?: return MediaProjectionState.EntireScreen - return MediaProjectionState.SingleTask(matchingTask) + ) ?: return MediaProjectionState.Projecting.EntireScreen(hostPackage) + return MediaProjectionState.Projecting.SingleTask(hostPackage, matchingTask) } companion object { diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionRepository.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionRepository.kt index 21300dbff929..50182d7a3b33 100644 --- a/packages/SystemUI/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionRepository.kt @@ -26,6 +26,9 @@ interface MediaProjectionRepository { /** Switches the task that should be projected. */ suspend fun switchProjectedTask(task: RunningTaskInfo) + /** Stops the currently active projection. */ + suspend fun stopProjecting() + /** Represents the current [MediaProjectionState]. */ val mediaProjectionState: Flow<MediaProjectionState> } diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/domain/interactor/TaskSwitchInteractor.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/domain/interactor/TaskSwitchInteractor.kt index c232d4d16294..118639c521f6 100644 --- a/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/domain/interactor/TaskSwitchInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/domain/interactor/TaskSwitchInteractor.kt @@ -57,7 +57,7 @@ constructor( mediaProjectionRepository.mediaProjectionState.flatMapLatest { projectionState -> Log.d(TAG, "MediaProjectionState -> $projectionState") when (projectionState) { - is MediaProjectionState.SingleTask -> { + is MediaProjectionState.Projecting.SingleTask -> { val projectedTask = projectionState.task tasksRepository.foregroundTask.map { foregroundTask -> if (hasForegroundTaskSwitched(projectedTask, foregroundTask)) { @@ -67,7 +67,7 @@ constructor( } } } - is MediaProjectionState.EntireScreen, + is MediaProjectionState.Projecting.EntireScreen, is MediaProjectionState.NotProjecting -> { flowOf(TaskSwitchState.NotProjectingTask) } diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java index 0e819c2b0a4f..07289cb7b809 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java @@ -2028,12 +2028,15 @@ public class NavigationBar extends ViewController<NavigationBarView> implements getBarTransitions().setBackgroundOverrideAlpha(1f); } } - updateScreenPinningGestures(); + + // Update the window layout params when the nav mode changes as that will affect the + // system gesture insets + setNavBarMode(mode); + repositionNavigationBar(mCurrentRotation); if (!canShowSecondaryHandle()) { resetSecondaryHandle(); } - setNavBarMode(mode); mView.setShouldShowSwipeUpUi(mOverviewProxyService.shouldShowSwipeUpUI()); } }; 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 41cd2c46b998..99c95b54a4ca 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java @@ -24,6 +24,7 @@ import static com.android.systemui.Flags.edgebackGestureHandlerGetRunningTasksBa import static com.android.systemui.classifier.Classifier.BACK_GESTURE; import static com.android.systemui.navigationbar.gestural.Utilities.isTrackpadScroll; import static com.android.systemui.navigationbar.gestural.Utilities.isTrackpadThreeFingerSwipe; +import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_TOUCHPAD_GESTURES_DISABLED; import static java.util.stream.Collectors.joining; @@ -1021,8 +1022,10 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack if (mIsTrackpadThreeFingerSwipe) { // Trackpad back gestures don't have zones, so we don't need to check if the down // event is within insets. - mAllowGesture = isBackAllowedCommon && isValidTrackpadBackGesture( - true /* isTrackpadEvent */); + boolean trackpadGesturesEnabled = + (mSysUiFlags & SYSUI_STATE_TOUCHPAD_GESTURES_DISABLED) == 0; + mAllowGesture = isBackAllowedCommon && trackpadGesturesEnabled + && isValidTrackpadBackGesture(true /* isTrackpadEvent */); } else { mAllowGesture = isBackAllowedCommon && !mUsingThreeButtonNav && isWithinInsets && isWithinTouchRegion((int) ev.getX(), (int) ev.getY()) diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractor.kt index 79720c1d8df6..5637115cd6da 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractor.kt @@ -35,6 +35,7 @@ import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction import com.android.systemui.screenrecord.RecordingController import com.android.systemui.screenrecord.data.model.ScreenRecordModel +import com.android.systemui.screenrecord.data.repository.ScreenRecordRepository import com.android.systemui.statusbar.phone.KeyguardDismissUtil import javax.inject.Inject import kotlin.coroutines.CoroutineContext @@ -47,6 +48,7 @@ constructor( @Application private val context: Context, @Main private val mainContext: CoroutineContext, @Background private val backgroundContext: CoroutineContext, + private val screenRecordRepository: ScreenRecordRepository, private val recordingController: RecordingController, private val keyguardInteractor: KeyguardInteractor, private val keyguardDismissUtil: KeyguardDismissUtil, @@ -65,8 +67,7 @@ constructor( Log.d(TAG, "Cancelling countdown") withContext(backgroundContext) { recordingController.cancelCountdown() } } - is ScreenRecordModel.Recording -> - withContext(backgroundContext) { recordingController.stopRecording() } + is ScreenRecordModel.Recording -> screenRecordRepository.stopRecording() is ScreenRecordModel.DoingNothing -> withContext(mainContext) { showPrompt(action.expandable, user.identifier) @@ -122,8 +123,7 @@ constructor( controller, animateBackgroundBoundsChange = true, ) - } - ?: dialog.show() + } ?: dialog.show() } else { dialog.show() } 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 0304e730cb68..1e689bd11fa3 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 @@ -39,6 +39,7 @@ import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor import com.android.systemui.deviceentry.domain.interactor.DeviceUnlockedInteractor import com.android.systemui.deviceentry.shared.model.DeviceUnlockSource import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor +import com.android.systemui.keyguard.domain.interactor.WindowManagerLockscreenVisibilityInteractor import com.android.systemui.model.SceneContainerPlugin import com.android.systemui.model.SysUiState import com.android.systemui.model.updateFlags @@ -81,6 +82,7 @@ import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.filterIsInstance import kotlinx.coroutines.flow.filterNot +import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map @@ -120,6 +122,7 @@ constructor( private val uiEventLogger: UiEventLogger, private val sceneBackInteractor: SceneBackInteractor, private val shadeSessionStorage: SessionStorage, + private val windowMgrLockscreenVisInteractor: WindowManagerLockscreenVisibilityInteractor, ) : CoreStartable { private val centralSurfaces: CentralSurfaces? get() = centralSurfacesOptLazy.get().getOrNull() @@ -227,6 +230,25 @@ constructor( handleDeviceUnlockStatus() handlePowerState() handleShadeTouchability() + handleSurfaceBehindKeyguardVisibility() + } + + private fun handleSurfaceBehindKeyguardVisibility() { + applicationScope.launch { + sceneInteractor.currentScene.collectLatest { currentScene -> + if (currentScene == Scenes.Lockscreen) { + // Wait for surface to become visible + windowMgrLockscreenVisInteractor.surfaceBehindVisibility.first { it } + // Make sure the device is actually unlocked before force-changing the scene + deviceUnlockedInteractor.deviceUnlockStatus.first { it.isUnlocked } + // Override the current transition, if any, by forcing the scene to Gone + sceneInteractor.changeScene( + toScene = Scenes.Gone, + loggingReason = "surface behind keyguard is visible", + ) + } + } + } } private fun handleBouncerImeVisibility() { @@ -329,8 +351,7 @@ constructor( Scenes.Gone to "device was unlocked in Bouncer scene" } else { val prevScene = previousScene.value - (prevScene - ?: Scenes.Gone) to + (prevScene ?: Scenes.Gone) to "device was unlocked in Bouncer scene, from sceneKey=$prevScene" } isOnLockscreen -> diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/data/repository/ScreenRecordRepository.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/data/repository/ScreenRecordRepository.kt index d59d220abe3f..9eeb3b9576d8 100644 --- a/packages/SystemUI/src/com/android/systemui/screenrecord/data/repository/ScreenRecordRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/screenrecord/data/repository/ScreenRecordRepository.kt @@ -28,6 +28,7 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.onStart +import kotlinx.coroutines.withContext /** * Repository storing information about the state of screen recording. @@ -38,6 +39,9 @@ import kotlinx.coroutines.flow.onStart interface ScreenRecordRepository { /** The current screen recording state. Note that this is a cold flow. */ val screenRecordState: Flow<ScreenRecordModel> + + /** Stops the recording. */ + suspend fun stopRecording() } @SysUISingleton @@ -90,4 +94,8 @@ constructor( ScreenRecordModel.DoingNothing } } + + override suspend fun stopRecording() { + withContext(bgCoroutineContext) { recordingController.stopRecording() } + } } diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelUnfoldAnimationController.kt b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelUnfoldAnimationController.kt index 14659e7909ab..f463cb5bcbd6 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelUnfoldAnimationController.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelUnfoldAnimationController.kt @@ -47,6 +47,7 @@ constructor( viewsIdToTranslate = setOf( ViewIdToTranslate(R.id.quick_settings_panel, START, filterShade), + ViewIdToTranslate(R.id.qs_footer_actions, START, filterShade), ViewIdToTranslate(R.id.notification_stack_scroller, END, filterShade)), progressProvider = progressProvider) } @@ -55,9 +56,8 @@ constructor( UnfoldConstantTranslateAnimator( viewsIdToTranslate = setOf( - ViewIdToTranslate(R.id.statusIcons, END, filterShade), + ViewIdToTranslate(R.id.shade_header_system_icons, END, filterShade), ViewIdToTranslate(R.id.privacy_container, END, filterShade), - ViewIdToTranslate(R.id.batteryRemainingIcon, END, filterShade), ViewIdToTranslate(R.id.carrier_group, END, filterShade), ViewIdToTranslate(R.id.clock, START, filterShade), ViewIdToTranslate(R.id.date, START, filterShade) diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java index 3826b50ab024..262befc1e9ad 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java @@ -537,8 +537,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump private final KeyguardMediaController mKeyguardMediaController; private final Optional<KeyguardUnfoldTransition> mKeyguardUnfoldTransition; - private final Optional<NotificationPanelUnfoldAnimationController> - mNotificationPanelUnfoldAnimationController; /** The drag distance required to fully expand the split shade. */ private int mSplitShadeFullTransitionDistance; @@ -964,8 +962,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump mKeyguardUnfoldTransition = unfoldComponent.map( SysUIUnfoldComponent::getKeyguardUnfoldTransition); - mNotificationPanelUnfoldAnimationController = unfoldComponent.map( - SysUIUnfoldComponent::getNotificationPanelUnfoldAnimationController); updateUserSwitcherFlags(); mKeyguardBottomAreaViewModel = keyguardBottomAreaViewModel; @@ -1131,9 +1127,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump mShadeHeaderController.init(); mShadeHeaderController.setShadeCollapseAction( () -> collapse(/* delayed= */ false , /* speedUpFactor= */ 1.0f)); - mKeyguardUnfoldTransition.ifPresent(u -> u.setup(mView)); - mNotificationPanelUnfoldAnimationController.ifPresent(controller -> - controller.setup(mNotificationContainerParent)); // Dreaming->Lockscreen collectFlow( @@ -1511,9 +1504,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump if (!KeyguardBottomAreaRefactor.isEnabled()) { setKeyguardBottomAreaVisibility(mBarState, false); } - - mKeyguardUnfoldTransition.ifPresent(u -> u.setup(mView)); - mNotificationPanelUnfoldAnimationController.ifPresent(u -> u.setup(mView)); } private void attachSplitShadeMediaPlayerContainer(FrameLayout container) { @@ -1797,6 +1787,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump private void updateKeyguardStatusViewAlignment(boolean animate) { boolean shouldBeCentered = shouldKeyguardStatusViewBeCentered(); + mKeyguardUnfoldTransition.ifPresent(t -> t.setStatusViewCentered(shouldBeCentered)); if (MigrateClocksToBlueprint.isEnabled()) { mKeyguardInteractor.setClockShouldBeCentered(shouldBeCentered); return; @@ -1804,7 +1795,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump ConstraintLayout layout = mNotificationContainerParent; mKeyguardStatusViewController.updateAlignment( layout, mSplitShadeEnabled, shouldBeCentered, animate); - mKeyguardUnfoldTransition.ifPresent(t -> t.setStatusViewCentered(shouldBeCentered)); } private boolean shouldKeyguardStatusViewBeCentered() { diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java index 1df085b24cdc..e41f99b99e35 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java @@ -32,6 +32,7 @@ import android.view.ViewGroup; import com.android.internal.annotations.VisibleForTesting; import com.android.keyguard.AuthKeyguardMessageArea; +import com.android.keyguard.KeyguardUnfoldTransition; import com.android.keyguard.LockIconViewController; import com.android.systemui.Dumpable; import com.android.systemui.animation.ActivityTransitionAnimator; @@ -72,6 +73,7 @@ import com.android.systemui.statusbar.phone.DozeServiceHost; import com.android.systemui.statusbar.phone.PhoneStatusBarViewController; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; import com.android.systemui.statusbar.window.StatusBarWindowStateController; +import com.android.systemui.unfold.SysUIUnfoldComponent; import com.android.systemui.unfold.UnfoldTransitionProgressProvider; import com.android.systemui.util.time.SystemClock; @@ -174,6 +176,7 @@ public class NotificationShadeWindowViewController implements Dumpable { DozeScrimController dozeScrimController, NotificationShadeWindowController controller, Optional<UnfoldTransitionProgressProvider> unfoldTransitionProgressProvider, + Optional<SysUIUnfoldComponent> unfoldComponent, KeyguardUnlockAnimationController keyguardUnlockAnimationController, NotificationInsetsController notificationInsetsController, AmbientState ambientState, @@ -234,6 +237,14 @@ public class NotificationShadeWindowViewController implements Dumpable { notificationLaunchAnimationInteractor.isLaunchAnimationRunning(), this::setExpandAnimationRunning); + var keyguardUnfoldTransition = unfoldComponent.map( + SysUIUnfoldComponent::getKeyguardUnfoldTransition); + var notificationPanelUnfoldAnimationController = unfoldComponent.map( + SysUIUnfoldComponent::getNotificationPanelUnfoldAnimationController); + + keyguardUnfoldTransition.ifPresent(KeyguardUnfoldTransition::setup); + notificationPanelUnfoldAnimationController.ifPresent(u -> u.setup(mView)); + mClock = clock; if (featureFlagsClassic.isEnabled(Flags.SPLIT_SHADE_SUBPIXEL_OPTIMIZATION)) { unfoldTransitionProgressProvider.ifPresent( diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt index 6c7606170d9f..b2e0cd04687c 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt @@ -30,6 +30,9 @@ import com.android.systemui.plugins.ActivityStarter import com.android.systemui.privacy.OngoingPrivacyChip import com.android.systemui.privacy.PrivacyItem import com.android.systemui.res.R +import com.android.systemui.scene.domain.interactor.SceneInteractor +import com.android.systemui.scene.shared.model.SceneFamilies +import com.android.systemui.scene.shared.model.TransitionKeys import com.android.systemui.shade.domain.interactor.PrivacyChipInteractor import com.android.systemui.shade.domain.interactor.ShadeHeaderClockInteractor import com.android.systemui.shade.domain.interactor.ShadeInteractor @@ -57,6 +60,7 @@ constructor( @Application private val applicationScope: CoroutineScope, context: Context, private val activityStarter: ActivityStarter, + private val sceneInteractor: SceneInteractor, shadeInteractor: ShadeInteractor, mobileIconsInteractor: MobileIconsInteractor, val mobileIconsViewModel: MobileIconsViewModel, @@ -139,6 +143,15 @@ constructor( clockInteractor.launchClockActivity() } + /** Notifies that the system icons container was clicked. */ + fun onSystemIconContainerClicked() { + sceneInteractor.changeScene( + SceneFamilies.Home, + "ShadeHeaderViewModel.onSystemIconContainerClicked", + TransitionKeys.SlightlyFasterShadeCollapse, + ) + } + /** Notifies that the shadeCarrierGroup was clicked. */ fun onShadeCarrierGroupClicked() { activityStarter.postStartActivityDismissingKeyguard( diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/domain/interactor/OngoingActivityChipInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/domain/interactor/OngoingActivityChipInteractor.kt index c3d37fb3d952..086a32dfe49c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/domain/interactor/OngoingActivityChipInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/domain/interactor/OngoingActivityChipInteractor.kt @@ -16,11 +16,37 @@ package com.android.systemui.statusbar.chips.domain.interactor +import android.view.View +import com.android.systemui.animation.DialogTransitionAnimator +import com.android.systemui.res.R import com.android.systemui.statusbar.chips.domain.model.OngoingActivityChipModel +import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer +import com.android.systemui.statusbar.phone.SystemUIDialog import kotlinx.coroutines.flow.StateFlow /** Interface for an interactor that knows the state of a single type of ongoing activity chip. */ interface OngoingActivityChipInteractor { /** A flow modeling the chip that should be shown. */ val chip: StateFlow<OngoingActivityChipModel> + + companion object { + /** Creates a chip click listener that launches a dialog created by [dialogDelegate]. */ + fun createDialogLaunchOnClickListener( + dialogDelegate: SystemUIDialog.Delegate, + dialogTransitionAnimator: DialogTransitionAnimator, + ): View.OnClickListener { + return View.OnClickListener { view -> + val dialog = dialogDelegate.createDialog() + val launchableView = + view.requireViewById<ChipBackgroundContainer>( + R.id.ongoing_activity_chip_background + ) + // TODO(b/343699052): This makes a beautiful animate-in, but the + // animate-out looks odd because the dialog animates back into the chip + // but then the chip disappears. If we aren't able to address + // b/343699052 in time for launch, we should just use `dialog.show`. + dialogTransitionAnimator.showFromView(dialog, launchableView) + } + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractor.kt index ac16d26e415c..6611434b661e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractor.kt @@ -16,6 +16,9 @@ package com.android.systemui.statusbar.chips.mediaprojection.domain.interactor +import android.content.pm.PackageManager +import androidx.annotation.DrawableRes +import com.android.systemui.animation.DialogTransitionAnimator import com.android.systemui.common.shared.model.ContentDescription import com.android.systemui.common.shared.model.Icon import com.android.systemui.dagger.SysUISingleton @@ -24,7 +27,12 @@ import com.android.systemui.mediaprojection.data.model.MediaProjectionState import com.android.systemui.mediaprojection.data.repository.MediaProjectionRepository import com.android.systemui.res.R import com.android.systemui.statusbar.chips.domain.interactor.OngoingActivityChipInteractor +import com.android.systemui.statusbar.chips.domain.interactor.OngoingActivityChipInteractor.Companion.createDialogLaunchOnClickListener import com.android.systemui.statusbar.chips.domain.model.OngoingActivityChipModel +import com.android.systemui.statusbar.chips.mediaprojection.ui.view.EndCastToOtherDeviceDialogDelegate +import com.android.systemui.statusbar.chips.mediaprojection.ui.view.EndShareToAppDialogDelegate +import com.android.systemui.statusbar.phone.SystemUIDialog +import com.android.systemui.util.Utils import com.android.systemui.util.time.SystemClock import javax.inject.Inject import kotlinx.coroutines.CoroutineScope @@ -32,6 +40,7 @@ import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.launch /** * Interactor for media-projection-related chips in the status bar. @@ -47,33 +56,88 @@ import kotlinx.coroutines.flow.stateIn class MediaProjectionChipInteractor @Inject constructor( - @Application scope: CoroutineScope, - mediaProjectionRepository: MediaProjectionRepository, - val systemClock: SystemClock, + @Application private val scope: CoroutineScope, + private val mediaProjectionRepository: MediaProjectionRepository, + private val packageManager: PackageManager, + private val systemClock: SystemClock, + private val dialogFactory: SystemUIDialog.Factory, + private val dialogTransitionAnimator: DialogTransitionAnimator, ) : OngoingActivityChipInteractor { override val chip: StateFlow<OngoingActivityChipModel> = mediaProjectionRepository.mediaProjectionState .map { state -> when (state) { is MediaProjectionState.NotProjecting -> OngoingActivityChipModel.Hidden - is MediaProjectionState.EntireScreen, - is MediaProjectionState.SingleTask -> { - // TODO(b/332662551): Distinguish between cast-to-other-device and - // share-to-app. - OngoingActivityChipModel.Shown( - icon = - Icon.Resource( - R.drawable.ic_cast_connected, - ContentDescription.Resource(R.string.accessibility_casting) - ), - // TODO(b/332662551): See if we can use a MediaProjection API to fetch - // this time. - startTimeMs = systemClock.elapsedRealtime() - ) { - // TODO(b/332662551): Implement the pause dialog. + is MediaProjectionState.Projecting -> { + if (isProjectionToOtherDevice(state.hostPackage)) { + createCastToOtherDeviceChip() + } else { + createShareToAppChip() } } } } .stateIn(scope, SharingStarted.WhileSubscribed(), OngoingActivityChipModel.Hidden) + + /** Stops the currently active projection. */ + fun stopProjecting() { + scope.launch { mediaProjectionRepository.stopProjecting() } + } + + /** + * Returns true iff projecting to the given [packageName] means that we're projecting to a + * *different* device (as opposed to projecting to some application on *this* device). + */ + private fun isProjectionToOtherDevice(packageName: String?): Boolean { + // The [isHeadlessRemoteDisplayProvider] check approximates whether a projection is to a + // different device or the same device, because headless remote display packages are the + // only kinds of packages that do cast-to-other-device. This isn't exactly perfect, + // because it means that any projection by those headless remote display packages will be + // marked as going to a different device, even if that isn't always true. See b/321078669. + return Utils.isHeadlessRemoteDisplayProvider(packageManager, packageName) + } + + private fun createCastToOtherDeviceChip(): OngoingActivityChipModel.Shown { + return OngoingActivityChipModel.Shown( + icon = + Icon.Resource( + CAST_TO_OTHER_DEVICE_ICON, + ContentDescription.Resource(R.string.accessibility_casting) + ), + // TODO(b/332662551): Maybe use a MediaProjection API to fetch this time. + startTimeMs = systemClock.elapsedRealtime(), + createDialogLaunchOnClickListener( + castToOtherDeviceDialogDelegate, + dialogTransitionAnimator, + ), + ) + } + + private val castToOtherDeviceDialogDelegate = + EndCastToOtherDeviceDialogDelegate( + dialogFactory, + this@MediaProjectionChipInteractor, + ) + + private fun createShareToAppChip(): OngoingActivityChipModel.Shown { + return OngoingActivityChipModel.Shown( + // TODO(b/332662551): Use the right content description. + icon = Icon.Resource(SHARE_TO_APP_ICON, contentDescription = null), + // TODO(b/332662551): Maybe use a MediaProjection API to fetch this time. + startTimeMs = systemClock.elapsedRealtime(), + createDialogLaunchOnClickListener(shareToAppDialogDelegate, dialogTransitionAnimator), + ) + } + + private val shareToAppDialogDelegate = + EndShareToAppDialogDelegate( + dialogFactory, + this@MediaProjectionChipInteractor, + ) + + companion object { + // TODO(b/332662551): Use the right icon. + @DrawableRes val SHARE_TO_APP_ICON = R.drawable.ic_screenshot_share + @DrawableRes val CAST_TO_OTHER_DEVICE_ICON = R.drawable.ic_cast_connected + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndCastToOtherDeviceDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndCastToOtherDeviceDialogDelegate.kt new file mode 100644 index 000000000000..33cec9755b1f --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndCastToOtherDeviceDialogDelegate.kt @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.chips.mediaprojection.ui.view + +import android.os.Bundle +import com.android.systemui.res.R +import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractor +import com.android.systemui.statusbar.phone.SystemUIDialog + +/** A dialog that lets the user stop an ongoing cast-screen-to-other-device event. */ +class EndCastToOtherDeviceDialogDelegate( + private val systemUIDialogFactory: SystemUIDialog.Factory, + private val interactor: MediaProjectionChipInteractor, +) : SystemUIDialog.Delegate { + override fun createDialog(): SystemUIDialog { + return systemUIDialogFactory.create(this) + } + + override fun beforeCreate(dialog: SystemUIDialog, savedInstanceState: Bundle?) { + with(dialog) { + setIcon(MediaProjectionChipInteractor.CAST_TO_OTHER_DEVICE_ICON) + setTitle(R.string.cast_to_other_device_stop_dialog_title) + // TODO(b/332662551): Use a different message if they're sharing just a single app. + setMessage(R.string.cast_to_other_device_stop_dialog_message) + // No custom on-click, because the dialog will automatically be dismissed when the + // button is clicked anyway. + setNegativeButton(R.string.close_dialog_button, /* onClick= */ null) + setPositiveButton(R.string.cast_to_other_device_stop_dialog_button) { _, _ -> + interactor.stopProjecting() + } + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndShareToAppDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndShareToAppDialogDelegate.kt new file mode 100644 index 000000000000..3a863b10ef82 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndShareToAppDialogDelegate.kt @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.chips.mediaprojection.ui.view + +import android.os.Bundle +import com.android.systemui.res.R +import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractor +import com.android.systemui.statusbar.phone.SystemUIDialog + +/** A dialog that lets the user stop an ongoing share-screen-to-app event. */ +class EndShareToAppDialogDelegate( + private val systemUIDialogFactory: SystemUIDialog.Factory, + private val interactor: MediaProjectionChipInteractor, +) : SystemUIDialog.Delegate { + override fun createDialog(): SystemUIDialog { + return systemUIDialogFactory.create(this) + } + + override fun beforeCreate(dialog: SystemUIDialog, savedInstanceState: Bundle?) { + with(dialog) { + setIcon(MediaProjectionChipInteractor.SHARE_TO_APP_ICON) + setTitle(R.string.share_to_app_stop_dialog_title) + // TODO(b/332662551): Use a different message if they're sharing just a single app. + setMessage(R.string.share_to_app_stop_dialog_message) + // No custom on-click, because the dialog will automatically be dismissed when the + // button is clicked anyway. + setNegativeButton(R.string.close_dialog_button, /* onClick= */ null) + setPositiveButton(R.string.share_to_app_stop_dialog_button) { _, _ -> + interactor.stopProjecting() + } + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractor.kt index 585ff5f78ff8..4959b0950fb5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractor.kt @@ -16,6 +16,8 @@ package com.android.systemui.statusbar.chips.screenrecord.domain.interactor +import androidx.annotation.DrawableRes +import com.android.systemui.animation.DialogTransitionAnimator import com.android.systemui.common.shared.model.Icon import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application @@ -23,7 +25,10 @@ import com.android.systemui.res.R import com.android.systemui.screenrecord.data.model.ScreenRecordModel import com.android.systemui.screenrecord.data.repository.ScreenRecordRepository import com.android.systemui.statusbar.chips.domain.interactor.OngoingActivityChipInteractor +import com.android.systemui.statusbar.chips.domain.interactor.OngoingActivityChipInteractor.Companion.createDialogLaunchOnClickListener import com.android.systemui.statusbar.chips.domain.model.OngoingActivityChipModel +import com.android.systemui.statusbar.chips.screenrecord.ui.view.EndScreenRecordingDialogDelegate +import com.android.systemui.statusbar.phone.SystemUIDialog import com.android.systemui.util.time.SystemClock import javax.inject.Inject import kotlinx.coroutines.CoroutineScope @@ -31,15 +36,18 @@ import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.launch /** Interactor for the screen recording chip shown in the status bar. */ @SysUISingleton class ScreenRecordChipInteractor @Inject constructor( - @Application scope: CoroutineScope, - screenRecordRepository: ScreenRecordRepository, - val systemClock: SystemClock, + @Application private val scope: CoroutineScope, + private val screenRecordRepository: ScreenRecordRepository, + private val systemClock: SystemClock, + private val dialogFactory: SystemUIDialog.Factory, + private val dialogTransitionAnimator: DialogTransitionAnimator, ) : OngoingActivityChipInteractor { override val chip: StateFlow<OngoingActivityChipModel> = screenRecordRepository.screenRecordState @@ -51,16 +59,29 @@ constructor( is ScreenRecordModel.Recording -> OngoingActivityChipModel.Shown( // TODO(b/332662551): Also provide a content description. - icon = - Icon.Resource( - R.drawable.stat_sys_screen_record, - contentDescription = null - ), - startTimeMs = systemClock.elapsedRealtime() - ) { - // TODO(b/332662551): Implement the pause dialog. - } + icon = Icon.Resource(ICON, contentDescription = null), + startTimeMs = systemClock.elapsedRealtime(), + createDialogLaunchOnClickListener( + dialogDelegate, + dialogTransitionAnimator + ), + ) } } .stateIn(scope, SharingStarted.WhileSubscribed(), OngoingActivityChipModel.Hidden) + + /** Stops the recording. */ + fun stopRecording() { + scope.launch { screenRecordRepository.stopRecording() } + } + + private val dialogDelegate = + EndScreenRecordingDialogDelegate( + dialogFactory, + this@ScreenRecordChipInteractor, + ) + + companion object { + @DrawableRes val ICON = R.drawable.ic_screenrecord + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/ui/view/EndScreenRecordingDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/ui/view/EndScreenRecordingDialogDelegate.kt new file mode 100644 index 000000000000..b8e8cfa11afc --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/ui/view/EndScreenRecordingDialogDelegate.kt @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.chips.screenrecord.ui.view + +import android.os.Bundle +import com.android.systemui.res.R +import com.android.systemui.statusbar.chips.screenrecord.domain.interactor.ScreenRecordChipInteractor +import com.android.systemui.statusbar.phone.SystemUIDialog + +/** A dialog that lets the user stop an ongoing screen recording. */ +class EndScreenRecordingDialogDelegate( + private val systemUIDialogFactory: SystemUIDialog.Factory, + private val interactor: ScreenRecordChipInteractor, +) : SystemUIDialog.Delegate { + + override fun createDialog(): SystemUIDialog { + return systemUIDialogFactory.create(this) + } + + override fun beforeCreate(dialog: SystemUIDialog, savedInstanceState: Bundle?) { + with(dialog) { + setIcon(ScreenRecordChipInteractor.ICON) + setTitle(R.string.screenrecord_stop_dialog_title) + // TODO(b/332662551): Use a different message if they're sharing just a single app. + setMessage(R.string.screenrecord_stop_dialog_message) + // No custom on-click, because the dialog will automatically be dismissed when the + // button is clicked anyway. + setNegativeButton(R.string.close_dialog_button, /* onClick= */ null) + setPositiveButton(R.string.screenrecord_stop_dialog_button) { _, _ -> + interactor.stopRecording() + } + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java index d1fabb168d90..939424945038 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java @@ -358,11 +358,7 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper imple * @param whenMillis */ public void setNotificationWhen(long whenMillis) { - if (mNotificationHeader == null) { - return; - } - - final View timeView = mNotificationHeader.findViewById(com.android.internal.R.id.time); + final View timeView = mView.findViewById(com.android.internal.R.id.time); if (timeView instanceof DateTimeView) { ((DateTimeView) timeView).setTime(whenMillis); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java index b77321bbe46a..71a0b941c350 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java @@ -3721,7 +3721,7 @@ public class NotificationStackScrollLayout protected boolean isInsideQsHeader(MotionEvent ev) { if (SceneContainerFlag.isEnabled()) { - return ev.getY() < mScrollViewFields.getScrimClippingShape().getBounds().getTop(); + return ev.getY() < mScrollViewFields.getStackTop(); } mQsHeader.getBoundsOnScreen(mQsHeaderBound); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt index cf5a5628cfa8..85835d22f183 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationScrollViewModel.kt @@ -40,6 +40,7 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.flow.map /** ViewModel which represents the state of the NSSL/Controller in the world of flexiglass */ @SysUISingleton @@ -146,6 +147,7 @@ constructor( /** Receives the amount (px) that the stack should scroll due to internal expansion. */ val syntheticScrollConsumer: (Float) -> Unit = stackAppearanceInteractor::setSyntheticScroll + /** * Receives whether the current touch gesture is overscroll as it has already been consumed by * the stack. @@ -154,10 +156,9 @@ constructor( stackAppearanceInteractor::setCurrentGestureOverscroll /** Whether the notification stack is scrollable or not. */ - val isScrollable: Flow<Boolean> = - sceneInteractor - .isCurrentSceneInFamily(SceneFamilies.NotifShade) - .dumpWhileCollecting("isScrollable") + val isScrollable: Flow<Boolean> = sceneInteractor.currentScene.map { + sceneInteractor.isSceneInFamily(it, SceneFamilies.NotifShade) || it == Scenes.Lockscreen + }.dumpWhileCollecting("isScrollable") /** Whether the notification stack is displayed in doze mode. */ val isDozing: Flow<Boolean> by lazy { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt index 6a8c43a077a6..b13630fa302a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt @@ -53,6 +53,7 @@ import com.android.systemui.keyguard.ui.viewmodel.GlanceableHubToLockscreenTrans import com.android.systemui.keyguard.ui.viewmodel.GoneToAodTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.GoneToDozingTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.GoneToDreamingTransitionViewModel +import com.android.systemui.keyguard.ui.viewmodel.GoneToLockscreenTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.LockscreenToDreamingTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.LockscreenToGlanceableHubTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.LockscreenToGoneTransitionViewModel @@ -120,6 +121,7 @@ constructor( private val goneToAodTransitionViewModel: GoneToAodTransitionViewModel, private val goneToDozingTransitionViewModel: GoneToDozingTransitionViewModel, private val goneToDreamingTransitionViewModel: GoneToDreamingTransitionViewModel, + private val goneToLockscreenTransitionViewModel: GoneToLockscreenTransitionViewModel, private val lockscreenToDreamingTransitionViewModel: LockscreenToDreamingTransitionViewModel, private val lockscreenToGlanceableHubTransitionViewModel: LockscreenToGlanceableHubTransitionViewModel, @@ -472,6 +474,9 @@ constructor( // All transition view models are mututally exclusive, and safe to merge val alphaTransitions = merge( + keyguardInteractor.dismissAlpha.dumpWhileCollecting( + "keyguardInteractor.dismissAlpha" + ), alternateBouncerToGoneTransitionViewModel.notificationAlpha(viewState), aodToGoneTransitionViewModel.notificationAlpha(viewState), aodToLockscreenTransitionViewModel.notificationAlpha, @@ -482,6 +487,7 @@ constructor( goneToAodTransitionViewModel.notificationAlpha, goneToDreamingTransitionViewModel.lockscreenAlpha, goneToDozingTransitionViewModel.lockscreenAlpha, + goneToLockscreenTransitionViewModel.lockscreenAlpha, lockscreenToDreamingTransitionViewModel.lockscreenAlpha, lockscreenToGoneTransitionViewModel.notificationAlpha(viewState), lockscreenToOccludedTransitionViewModel.lockscreenAlpha, @@ -498,24 +504,12 @@ constructor( // These remaining cases handle alpha changes within an existing state, such as // shade expansion or swipe to dismiss combineTransform( - isOnLockscreenWithoutShade, isTransitioningToHiddenKeyguard, - shadeCollapseFadeIn, alphaForShadeAndQsExpansion, - keyguardInteractor.dismissAlpha.dumpWhileCollecting( - "keyguardInteractor.keyguardAlpha" - ), ) { - isOnLockscreenWithoutShade, isTransitioningToHiddenKeyguard, - shadeCollapseFadeIn, - alphaForShadeAndQsExpansion, - dismissAlpha -> - if (isOnLockscreenWithoutShade) { - if (!shadeCollapseFadeIn && dismissAlpha != null) { - emit(dismissAlpha) - } - } else if (!isTransitioningToHiddenKeyguard) { + alphaForShadeAndQsExpansion -> + if (!isTransitioningToHiddenKeyguard) { emit(alphaForShadeAndQsExpansion) } }, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java index e44edcbd4ebb..cd59d4ebfd73 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java @@ -21,6 +21,7 @@ import static android.app.StatusBarManager.DISABLE_SYSTEM_INFO; import static com.android.systemui.Flags.updateUserSwitcherBackground; import static com.android.systemui.statusbar.StatusBarState.KEYGUARD; +import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow; import android.content.res.Configuration; import android.content.res.Resources; @@ -46,6 +47,7 @@ import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.keyguard.logging.KeyguardLogger; import com.android.systemui.battery.BatteryMeterViewController; +import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.log.core.LogLevel; @@ -83,6 +85,7 @@ import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Executor; +import java.util.function.Consumer; import javax.inject.Inject; @@ -128,6 +131,7 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat private final Executor mBackgroundExecutor; private final Object mLock = new Object(); private final KeyguardLogger mLogger; + private final CommunalSceneInteractor mCommunalSceneInteractor; private View mSystemIconsContainer; private final StatusOverlayHoverListenerFactory mStatusOverlayHoverListenerFactory; @@ -241,6 +245,12 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat } }; + private boolean mCommunalShowing; + + private final Consumer<Boolean> mCommunalConsumer = (communalShowing) -> { + mCommunalShowing = communalShowing; + updateViewState(); + }; private final DisableStateTracker mDisableStateTracker; @@ -298,7 +308,8 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat @Main Executor mainExecutor, @Background Executor backgroundExecutor, KeyguardLogger logger, - StatusOverlayHoverListenerFactory statusOverlayHoverListenerFactory + StatusOverlayHoverListenerFactory statusOverlayHoverListenerFactory, + CommunalSceneInteractor communalSceneInteractor ) { super(view); mCarrierTextController = carrierTextController; @@ -324,6 +335,7 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat mMainExecutor = mainExecutor; mBackgroundExecutor = backgroundExecutor; mLogger = logger; + mCommunalSceneInteractor = communalSceneInteractor; mFirstBypassAttempt = mKeyguardBypassController.getBypassEnabled(); mKeyguardStateController.addCallback( @@ -405,6 +417,7 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat UserHandle.USER_ALL); updateUserSwitcher(); onThemeChanged(); + collectFlow(mView, mCommunalSceneInteractor.isCommunalVisible(), mCommunalConsumer); } @Override @@ -559,6 +572,7 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat && !mDozing && !hideForBypass && !mDisableStateTracker.isDisabled() + && !mCommunalShowing ? View.VISIBLE : View.INVISIBLE; updateViewState(newAlpha, newVisibility); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt index e5fc4e2968cf..800d7fda5b98 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt @@ -469,7 +469,7 @@ constructor( shadeControllerLazy.get().collapseShadeForActivityStart() } if (communalHub()) { - communalSceneInteractor.snapToScene(CommunalScenes.Blank) + communalSceneInteractor.snapToSceneForActivityStart(CommunalScenes.Blank) } return deferred } @@ -556,7 +556,7 @@ constructor( override fun onTransitionAnimationStart(isExpandingFullyAbove: Boolean) { super.onTransitionAnimationStart(isExpandingFullyAbove) if (communalHub()) { - communalSceneInteractor.snapToScene( + communalSceneInteractor.snapToSceneForActivityStart( CommunalScenes.Blank, ActivityTransitionAnimator.TIMINGS.totalDuration ) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt index a2ec1f21a35c..44b5bafc0859 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt @@ -94,7 +94,7 @@ class CollapsedStatusBarViewBinderImpl @Inject constructor() : CollapsedStatusBa is OngoingActivityChipModel.Shown -> { IconViewBinder.bind(chipModel.icon, chipIconView) ChipChronometerBinder.bind(chipModel.startTimeMs, chipTimeView) - // TODO(b/332662551): Attach click listener to chip + chipView.setOnClickListener(chipModel.onClickListener) listener.onOngoingActivityStatusChanged( hasOngoingActivity = true diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/TouchpadTutorialViewModel.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/TouchpadTutorialViewModel.kt index 7669524ee316..11740a8a4ecc 100644 --- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/TouchpadTutorialViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/TouchpadTutorialViewModel.kt @@ -27,6 +27,10 @@ class TouchpadTutorialViewModel : ViewModel() { private val _screen = MutableStateFlow(Screen.TUTORIAL_SELECTION) val screen: StateFlow<Screen> = _screen + fun goTo(screen: Screen) { + _screen.value = screen + } + class Factory @Inject constructor() : ViewModelProvider.Factory { @Suppress("UNCHECKED_CAST") diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/view/TouchpadTutorialActivity.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/view/TouchpadTutorialActivity.kt index 09dd909cd9a3..b7629c7160ee 100644 --- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/view/TouchpadTutorialActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/view/TouchpadTutorialActivity.kt @@ -19,6 +19,7 @@ package com.android.systemui.touchpad.tutorial.ui.view import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent +import androidx.activity.enableEdgeToEdge import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.lifecycle.Lifecycle.State.STARTED @@ -33,8 +34,6 @@ import com.android.systemui.touchpad.tutorial.ui.Screen.BACK_GESTURE import com.android.systemui.touchpad.tutorial.ui.Screen.HOME_GESTURE import com.android.systemui.touchpad.tutorial.ui.Screen.TUTORIAL_SELECTION import com.android.systemui.touchpad.tutorial.ui.TouchpadTutorialViewModel -import com.android.systemui.touchpad.tutorial.ui.TutorialSelectionViewModel -import com.android.systemui.touchpad.tutorial.ui.TutorialSelectionViewModelFactory import javax.inject.Inject class TouchpadTutorialActivity @@ -45,27 +44,31 @@ constructor( override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setContent { PlatformTheme { TouchpadTutorialScreen(viewModelFactory) } } + enableEdgeToEdge() + setContent { + PlatformTheme { TouchpadTutorialScreen(viewModelFactory, closeTutorial = { finish() }) } + } } } @Composable -fun TouchpadTutorialScreen(viewModelFactory: ViewModelProvider.Factory) { +fun TouchpadTutorialScreen(viewModelFactory: ViewModelProvider.Factory, closeTutorial: () -> Unit) { val vm = viewModel<TouchpadTutorialViewModel>(factory = viewModelFactory) val activeScreen by vm.screen.collectAsStateWithLifecycle(STARTED) when (activeScreen) { - TUTORIAL_SELECTION -> TutorialSelectionScreen() + TUTORIAL_SELECTION -> + TutorialSelectionScreen( + onBackTutorialClicked = { vm.goTo(BACK_GESTURE) }, + onHomeTutorialClicked = { vm.goTo(HOME_GESTURE) }, + onActionKeyTutorialClicked = {}, + onDoneButtonClicked = closeTutorial + ) BACK_GESTURE -> BackGestureTutorialScreen() HOME_GESTURE -> HomeGestureTutorialScreen() } } @Composable -fun TutorialSelectionScreen() { - val vm = viewModel<TutorialSelectionViewModel>(factory = TutorialSelectionViewModelFactory()) -} - -@Composable fun BackGestureTutorialScreen() { val vm = viewModel<BackGestureTutorialViewModel>(factory = GestureViewModelFactory()) } diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/view/TutorialSelectionScreen.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/view/TutorialSelectionScreen.kt new file mode 100644 index 000000000000..532eb1b82197 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/view/TutorialSelectionScreen.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.systemui.touchpad.tutorial.ui.view + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.aspectRatio +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import com.android.systemui.res.R + +@Composable +fun TutorialSelectionScreen( + onBackTutorialClicked: () -> Unit, + onHomeTutorialClicked: () -> Unit, + onActionKeyTutorialClicked: () -> Unit, + onDoneButtonClicked: () -> Unit, +) { + Column( + verticalArrangement = Arrangement.Center, + modifier = + Modifier.background( + color = MaterialTheme.colorScheme.surfaceContainer, + ) + .fillMaxSize() + ) { + TutorialSelectionButtons( + onBackTutorialClicked = onBackTutorialClicked, + onHomeTutorialClicked = onHomeTutorialClicked, + onActionKeyTutorialClicked = onActionKeyTutorialClicked, + modifier = Modifier.padding(60.dp) + ) + DoneButton( + onDoneButtonClicked = onDoneButtonClicked, + modifier = Modifier.padding(horizontal = 60.dp) + ) + } +} + +@Composable +private fun TutorialSelectionButtons( + onBackTutorialClicked: () -> Unit, + onHomeTutorialClicked: () -> Unit, + onActionKeyTutorialClicked: () -> Unit, + modifier: Modifier = Modifier +) { + Row( + horizontalArrangement = Arrangement.spacedBy(20.dp), + verticalAlignment = Alignment.CenterVertically, + modifier = modifier + ) { + TutorialButton( + text = stringResource(R.string.touchpad_tutorial_back_gesture_button), + onClick = onBackTutorialClicked, + color = MaterialTheme.colorScheme.primary, + modifier = Modifier.weight(1f) + ) + TutorialButton( + text = stringResource(R.string.touchpad_tutorial_home_gesture_button), + onClick = onHomeTutorialClicked, + color = MaterialTheme.colorScheme.secondary, + modifier = Modifier.weight(1f) + ) + TutorialButton( + text = stringResource(R.string.touchpad_tutorial_action_key_button), + onClick = onActionKeyTutorialClicked, + color = MaterialTheme.colorScheme.tertiary, + modifier = Modifier.weight(1f) + ) + } +} + +@Composable +private fun TutorialButton( + text: String, + onClick: () -> Unit, + color: Color, + modifier: Modifier = Modifier +) { + Button( + onClick = onClick, + shape = RoundedCornerShape(16.dp), + colors = ButtonDefaults.buttonColors(containerColor = color), + modifier = modifier.aspectRatio(0.66f) + ) { + Text(text = text, style = MaterialTheme.typography.headlineLarge) + } +} + +@Composable +private fun DoneButton(onDoneButtonClicked: () -> Unit, modifier: Modifier = Modifier) { + Row( + horizontalArrangement = Arrangement.End, + verticalAlignment = Alignment.CenterVertically, + modifier = modifier.fillMaxWidth() + ) { + Button(onClick = onDoneButtonClicked) { + Text(stringResource(R.string.touchpad_tutorial_done_button)) + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt b/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt index a27989d772c4..291903d7254f 100644 --- a/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt +++ b/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt @@ -21,6 +21,7 @@ import com.android.systemui.CoreStartable import com.android.systemui.dagger.SysUISingleton import com.android.systemui.shade.NotificationPanelUnfoldAnimationController import com.android.systemui.statusbar.phone.StatusBarMoveFromCenterAnimationController +import com.android.systemui.unfold.dagger.NaturalRotation import com.android.systemui.unfold.dagger.UnfoldBg import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider @@ -108,6 +109,13 @@ abstract class SysUIUnfoldInternalModule { abstract fun bindsFoldLightRevealOverlayAnimation( anim: FoldLightRevealOverlayAnimation ): FullscreenLightRevealAnimation + + @Binds + @NaturalRotation + @SysUIUnfoldScope + abstract fun bindNaturalRotationUnfoldProgressProvider( + provider: NaturalRotationUnfoldProgressProvider + ): UnfoldTransitionProgressProvider } @SysUIUnfoldScope diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUnfoldTransitionTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUnfoldTransitionTest.kt index 3afca5933e5d..336183d71ff1 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUnfoldTransitionTest.kt +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUnfoldTransitionTest.kt @@ -18,26 +18,25 @@ package com.android.keyguard import android.testing.AndroidTestingRunner import android.view.View -import android.view.ViewGroup import androidx.test.filters.SmallTest -import com.android.systemui.res.R import com.android.systemui.SysuiTestCase +import com.android.systemui.keyguard.ui.view.KeyguardRootView +import com.android.systemui.kosmos.Kosmos import com.android.systemui.plugins.statusbar.StatusBarStateController +import com.android.systemui.res.R +import com.android.systemui.shade.NotificationShadeWindowView import com.android.systemui.statusbar.StatusBarState.KEYGUARD import com.android.systemui.statusbar.StatusBarState.SHADE +import com.android.systemui.unfold.FakeUnfoldTransitionProvider import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener -import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider -import com.android.systemui.util.mockito.capture -import com.android.systemui.util.mockito.whenever +import com.android.systemui.unfold.fakeUnfoldTransitionProgressProvider import com.google.common.truth.Truth.assertThat 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.Mockito.verify import org.mockito.MockitoAnnotations +import org.mockito.kotlin.whenever /** * Translates items away/towards the hinge when the device is opened/closed. This is controlled by @@ -47,11 +46,16 @@ import org.mockito.MockitoAnnotations @RunWith(AndroidTestingRunner::class) class KeyguardUnfoldTransitionTest : SysuiTestCase() { - @Mock private lateinit var progressProvider: NaturalRotationUnfoldProgressProvider + private val kosmos = Kosmos() + + private val progressProvider: FakeUnfoldTransitionProvider = + kosmos.fakeUnfoldTransitionProgressProvider - @Captor private lateinit var progressListenerCaptor: ArgumentCaptor<TransitionProgressListener> + @Mock + private lateinit var keyguardRootView: KeyguardRootView - @Mock private lateinit var parent: ViewGroup + @Mock + private lateinit var notificationShadeWindowView: NotificationShadeWindowView @Mock private lateinit var statusBarStateController: StatusBarStateController @@ -66,13 +70,15 @@ class KeyguardUnfoldTransitionTest : SysuiTestCase() { xTranslationMax = context.resources.getDimensionPixelSize(R.dimen.keyguard_unfold_translation_x).toFloat() - underTest = KeyguardUnfoldTransition(context, statusBarStateController, progressProvider) + underTest = KeyguardUnfoldTransition( + context, keyguardRootView, notificationShadeWindowView, + statusBarStateController, progressProvider + ) - underTest.setup(parent) + underTest.setup() underTest.statusViewCentered = false - verify(progressProvider).addCallback(capture(progressListenerCaptor)) - progressListener = progressListenerCaptor.value + progressListener = progressProvider } @Test @@ -81,7 +87,9 @@ class KeyguardUnfoldTransitionTest : SysuiTestCase() { underTest.statusViewCentered = true val view = View(context) - whenever(parent.findViewById<View>(R.id.lockscreen_clock_view_large)).thenReturn(view) + whenever(keyguardRootView.findViewById<View>(R.id.lockscreen_clock_view_large)).thenReturn( + view + ) progressListener.onTransitionStarted() assertThat(view.translationX).isZero() @@ -101,7 +109,9 @@ class KeyguardUnfoldTransitionTest : SysuiTestCase() { whenever(statusBarStateController.getState()).thenReturn(SHADE) val view = View(context) - whenever(parent.findViewById<View>(R.id.lockscreen_clock_view_large)).thenReturn(view) + whenever(keyguardRootView.findViewById<View>(R.id.lockscreen_clock_view_large)).thenReturn( + view + ) progressListener.onTransitionStarted() assertThat(view.translationX).isZero() @@ -121,7 +131,10 @@ class KeyguardUnfoldTransitionTest : SysuiTestCase() { whenever(statusBarStateController.getState()).thenReturn(KEYGUARD) val view = View(context) - whenever(parent.findViewById<View>(R.id.lockscreen_clock_view_large)).thenReturn(view) + whenever( + notificationShadeWindowView + .findViewById<View>(R.id.lockscreen_clock_view_large) + ).thenReturn(view) progressListener.onTransitionStarted() assertThat(view.translationX).isZero() diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt index 707695441fb5..cc7dec56be40 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt @@ -26,6 +26,7 @@ import android.content.pm.PackageManager.NameNotFoundException import android.content.res.Configuration import android.graphics.Bitmap import android.graphics.Point +import android.graphics.Rect import android.graphics.drawable.BitmapDrawable import android.hardware.biometrics.BiometricFingerprintConstants import android.hardware.biometrics.Flags.FLAG_CUSTOM_BIOMETRIC_PROMPT @@ -87,9 +88,6 @@ import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Before -import platform.test.runner.parameterized.ParameterizedAndroidJunit4 -import platform.test.runner.parameterized.Parameter -import platform.test.runner.parameterized.Parameters import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith @@ -97,6 +95,8 @@ import org.mockito.ArgumentMatchers.anyInt import org.mockito.ArgumentMatchers.eq import org.mockito.Mock import org.mockito.junit.MockitoJUnit +import platform.test.runner.parameterized.ParameterizedAndroidJunit4 +import platform.test.runner.parameterized.Parameters private const val USER_ID = 4 private const val REQUEST_ID = 4L @@ -135,6 +135,27 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa private val defaultLogoDescription = "Test Android App" private val logoDescriptionFromApp = "Test Cake App" private val packageNameForLogoWithOverrides = "should.use.overridden.logo" + /** Prompt panel size padding */ + private val smallHorizontalGuidelinePadding = + context.resources.getDimensionPixelSize( + R.dimen.biometric_prompt_land_small_horizontal_guideline_padding + ) + private val udfpsHorizontalGuidelinePadding = + context.resources.getDimensionPixelSize( + R.dimen.biometric_prompt_two_pane_udfps_horizontal_guideline_padding + ) + private val udfpsHorizontalShorterGuidelinePadding = + context.resources.getDimensionPixelSize( + R.dimen.biometric_prompt_two_pane_udfps_shorter_horizontal_guideline_padding + ) + private val mediumTopGuidelinePadding = + context.resources.getDimensionPixelSize( + R.dimen.biometric_prompt_one_pane_medium_top_guideline_padding + ) + private val mediumHorizontalGuidelinePadding = + context.resources.getDimensionPixelSize( + R.dimen.biometric_prompt_two_pane_medium_horizontal_guideline_padding + ) private lateinit var fingerprintRepository: FakeFingerprintPropertyRepository private lateinit var promptRepository: FakePromptRepository @@ -1370,6 +1391,142 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa } @Test + @EnableFlags(FLAG_CONSTRAINT_BP) + fun position_bottom_rotation0() = runGenericTest { + displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_0) + val position by collectLastValue(viewModel.position) + assertThat(position).isEqualTo(PromptPosition.Bottom) + } // TODO(b/335278136): Add test for no sensor landscape + + @Test + @EnableFlags(FLAG_CONSTRAINT_BP) + fun position_bottom_forceLarge() = runGenericTest { + displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_270) + viewModel.onSwitchToCredential() + val position by collectLastValue(viewModel.position) + assertThat(position).isEqualTo(PromptPosition.Bottom) + } + + @Test + @EnableFlags(FLAG_CONSTRAINT_BP) + fun position_bottom_largeScreen() = runGenericTest { + displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_270) + displayStateRepository.setIsLargeScreen(true) + val position by collectLastValue(viewModel.position) + assertThat(position).isEqualTo(PromptPosition.Bottom) + } + + @Test + @EnableFlags(FLAG_CONSTRAINT_BP) + fun position_right_rotation90() = runGenericTest { + displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_90) + val position by collectLastValue(viewModel.position) + assertThat(position).isEqualTo(PromptPosition.Right) + } + + @Test + @EnableFlags(FLAG_CONSTRAINT_BP) + fun position_left_rotation270() = runGenericTest { + displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_270) + val position by collectLastValue(viewModel.position) + assertThat(position).isEqualTo(PromptPosition.Left) + } + + @Test + @EnableFlags(FLAG_CONSTRAINT_BP) + fun position_top_rotation180() = runGenericTest { + displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_180) + val position by collectLastValue(viewModel.position) + if (testCase.modalities.hasUdfps) { + assertThat(position).isEqualTo(PromptPosition.Top) + } else { + assertThat(position).isEqualTo(PromptPosition.Bottom) + } + } + + @Test + @EnableFlags(FLAG_CONSTRAINT_BP) + fun guideline_bottom() = runGenericTest { + displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_0) + val guidelineBounds by collectLastValue(viewModel.guidelineBounds) + assertThat(guidelineBounds).isEqualTo(Rect(0, mediumTopGuidelinePadding, 0, 0)) + } // TODO(b/335278136): Add test for no sensor landscape + + @Test + @EnableFlags(FLAG_CONSTRAINT_BP) + fun guideline_right() = runGenericTest { + displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_90) + + val isSmall = testCase.shouldStartAsImplicitFlow + val guidelineBounds by collectLastValue(viewModel.guidelineBounds) + + if (isSmall) { + assertThat(guidelineBounds).isEqualTo(Rect(-smallHorizontalGuidelinePadding, 0, 0, 0)) + } else if (testCase.modalities.hasUdfps) { + assertThat(guidelineBounds).isEqualTo(Rect(udfpsHorizontalGuidelinePadding, 0, 0, 0)) + } else { + assertThat(guidelineBounds).isEqualTo(Rect(-mediumHorizontalGuidelinePadding, 0, 0, 0)) + } + } + + @Test + @EnableFlags(FLAG_CONSTRAINT_BP) + fun guideline_right_onlyShortTitle() = + runGenericTest(subtitle = "") { + displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_90) + + val isSmall = testCase.shouldStartAsImplicitFlow + val guidelineBounds by collectLastValue(viewModel.guidelineBounds) + + if (!isSmall && testCase.modalities.hasUdfps) { + assertThat(guidelineBounds) + .isEqualTo(Rect(-udfpsHorizontalShorterGuidelinePadding, 0, 0, 0)) + } + } + + @Test + @EnableFlags(FLAG_CONSTRAINT_BP) + fun guideline_left() = runGenericTest { + displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_270) + + val isSmall = testCase.shouldStartAsImplicitFlow + val guidelineBounds by collectLastValue(viewModel.guidelineBounds) + + if (isSmall) { + assertThat(guidelineBounds).isEqualTo(Rect(0, 0, -smallHorizontalGuidelinePadding, 0)) + } else if (testCase.modalities.hasUdfps) { + assertThat(guidelineBounds).isEqualTo(Rect(0, 0, udfpsHorizontalGuidelinePadding, 0)) + } else { + assertThat(guidelineBounds).isEqualTo(Rect(0, 0, -mediumHorizontalGuidelinePadding, 0)) + } + } + + @Test + @EnableFlags(FLAG_CONSTRAINT_BP) + fun guideline_left_onlyShortTitle() = + runGenericTest(subtitle = "") { + displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_270) + + val isSmall = testCase.shouldStartAsImplicitFlow + val guidelineBounds by collectLastValue(viewModel.guidelineBounds) + + if (!isSmall && testCase.modalities.hasUdfps) { + assertThat(guidelineBounds) + .isEqualTo(Rect(0, 0, -udfpsHorizontalShorterGuidelinePadding, 0)) + } + } + + @Test + @EnableFlags(FLAG_CONSTRAINT_BP) + fun guideline_top() = runGenericTest { + displayStateRepository.setCurrentRotation(DisplayRotation.ROTATION_180) + val guidelineBounds by collectLastValue(viewModel.guidelineBounds) + if (testCase.modalities.hasUdfps) { + assertThat(guidelineBounds).isEqualTo(Rect(0, 0, 0, 0)) + } + } + + @Test fun iconViewLoaded() = runGenericTest { val isIconViewLoaded by collectLastValue(viewModel.isIconViewLoaded) // TODO(b/328677869): Add test for noIcon logic. @@ -1399,9 +1556,10 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa private fun runGenericTest( doNotStart: Boolean = false, allowCredentialFallback: Boolean = false, + subtitle: String? = "s", description: String? = null, contentView: PromptContentView? = null, - logoRes: Int = -1, + logoRes: Int = 0, logoBitmap: Bitmap? = null, logoDescription: String? = null, packageName: String = OP_PACKAGE_NAME, @@ -1437,11 +1595,11 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa allowCredentialFallback = allowCredentialFallback, fingerprint = testCase.fingerprint, face = testCase.face, + subtitleFromApp = subtitle, descriptionFromApp = description, contentViewFromApp = contentView, logoResFromApp = logoRes, - logoBitmapFromApp = - if (logoRes != -1) logoDrawableFromAppRes.toBitmap() else logoBitmap, + logoBitmapFromApp = if (logoRes != 0) logoDrawableFromAppRes.toBitmap() else logoBitmap, logoDescriptionFromApp = logoDescription, packageName = packageName, ) @@ -1625,9 +1783,10 @@ private fun PromptSelectorInteractor.initializePrompt( face: FaceSensorPropertiesInternal? = null, requireConfirmation: Boolean = false, allowCredentialFallback: Boolean = false, + subtitleFromApp: String? = "s", descriptionFromApp: String? = null, contentViewFromApp: PromptContentView? = null, - logoResFromApp: Int = -1, + logoResFromApp: Int = 0, logoBitmapFromApp: Bitmap? = null, logoDescriptionFromApp: String? = null, packageName: String = OP_PACKAGE_NAME, @@ -1636,7 +1795,7 @@ private fun PromptSelectorInteractor.initializePrompt( PromptInfo().apply { logoDescription = logoDescriptionFromApp title = "t" - subtitle = "s" + subtitle = subtitleFromApp description = descriptionFromApp contentView = contentViewFromApp authenticators = listOf(face, fingerprint).extractAuthenticatorTypes() diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt index e02fb29d1070..89e09710eb94 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt @@ -30,7 +30,6 @@ import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepositor import com.android.systemui.communal.domain.interactor.communalInteractor import com.android.systemui.communal.domain.interactor.setCommunalAvailable import com.android.systemui.communal.shared.model.CommunalScenes -import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository import com.android.systemui.dock.fakeDockManager import com.android.systemui.flags.BrokenWithSceneContainer import com.android.systemui.flags.DisableSceneContainer @@ -608,31 +607,6 @@ class KeyguardTransitionScenariosTest(flags: FlagsParameterization?) : SysuiTest /** This handles security method NONE and screen off with lock timeout */ @Test - fun dozingToGoneWithKeyguardNotShowing() = - testScope.runTest { - // GIVEN a prior transition has run to DOZING - runTransitionAndSetWakefulness(KeyguardState.LOCKSCREEN, KeyguardState.DOZING) - runCurrent() - - // WHEN the device wakes up without a keyguard - keyguardRepository.setKeyguardShowing(false) - keyguardRepository.setKeyguardDismissible(true) - kosmos.fakeDeviceEntryRepository.setLockscreenEnabled(false) - powerInteractor.setAwakeForTest() - advanceTimeBy(60L) - - assertThat(transitionRepository) - .startedTransition( - to = KeyguardState.GONE, - from = KeyguardState.DOZING, - animatorAssertion = { it.isNotNull() } - ) - - coroutineContext.cancelChildren() - } - - /** This handles security method NONE and screen off with lock timeout */ - @Test @DisableSceneContainer fun dreamingToGoneWithKeyguardNotShowing() = testScope.runTest { diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java index ec02c6445b57..411ff91ebc2f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java @@ -113,8 +113,8 @@ public class MediaOutputAdapterTest extends SysuiTestCase { LocalMediaManager.MediaDeviceState.STATE_DISCONNECTED); mMediaDevices.add(mMediaDevice1); mMediaDevices.add(mMediaDevice2); - mMediaItems.add(new MediaItem(mMediaDevice1)); - mMediaItems.add(new MediaItem(mMediaDevice2)); + mMediaItems.add(MediaItem.createDeviceMediaItem(mMediaDevice1)); + mMediaItems.add(MediaItem.createDeviceMediaItem(mMediaDevice2)); mMediaOutputAdapter = new MediaOutputAdapter(mMediaOutputController); mMediaOutputAdapter.updateItems(); @@ -146,7 +146,8 @@ public class MediaOutputAdapterTest extends SysuiTestCase { mMediaOutputAdapter.updateItems(); mViewHolder = (MediaOutputAdapter.MediaDeviceViewHolder) mMediaOutputAdapter .onCreateViewHolder(new LinearLayout(mContext), 0); - mMediaItems.add(new MediaItem()); + mMediaItems.add(MediaItem.createPairNewDeviceMediaItem()); + mMediaItems.add(MediaItem.createPairNewDeviceMediaItem()); mMediaOutputAdapter.updateItems(); mMediaOutputAdapter.onBindViewHolder(mViewHolder, 2); @@ -589,7 +590,7 @@ public class MediaOutputAdapterTest extends SysuiTestCase { mMediaOutputAdapter.updateItems(); mViewHolder = (MediaOutputAdapter.MediaDeviceViewHolder) mMediaOutputAdapter .onCreateViewHolder(new LinearLayout(mContext), 0); - mMediaItems.add(new MediaItem()); + mMediaItems.add(MediaItem.createPairNewDeviceMediaItem()); mMediaOutputAdapter.updateItems(); mMediaOutputAdapter.onBindViewHolder(mViewHolder, 2); mViewHolder.mContainerLayout.performClick(); @@ -725,7 +726,7 @@ public class MediaOutputAdapterTest extends SysuiTestCase { public void updateItems_controllerItemsUpdated_notUpdatesInAdapterUntilUpdateItems() { mMediaOutputAdapter.updateItems(); List<MediaItem> updatedList = new ArrayList<>(); - updatedList.add(new MediaItem()); + updatedList.add(MediaItem.createPairNewDeviceMediaItem()); when(mMediaOutputController.getMediaItemList()).thenReturn(updatedList); assertThat(mMediaOutputAdapter.getItemCount()).isEqualTo(mMediaItems.size()); diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepositoryTest.kt index b7fefc0a202f..c0d411b12496 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepositoryTest.kt @@ -16,7 +16,9 @@ package com.android.systemui.mediaprojection.data.repository +import android.media.projection.MediaProjectionInfo import android.os.Binder +import android.os.UserHandle import android.view.ContentRecordingSession import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest @@ -26,6 +28,7 @@ import com.android.systemui.kosmos.testScope import com.android.systemui.mediaprojection.data.model.MediaProjectionState import com.android.systemui.mediaprojection.taskswitcher.FakeActivityTaskManager.Companion.createTask import com.android.systemui.mediaprojection.taskswitcher.FakeActivityTaskManager.Companion.createToken +import com.android.systemui.mediaprojection.taskswitcher.FakeMediaProjectionManager.Companion.createDisplaySession import com.android.systemui.mediaprojection.taskswitcher.fakeActivityTaskManager import com.android.systemui.mediaprojection.taskswitcher.fakeMediaProjectionManager import com.android.systemui.mediaprojection.taskswitcher.taskSwitcherKosmos @@ -33,6 +36,7 @@ import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith +import org.mockito.kotlin.verify @RunWith(AndroidJUnit4::class) @SmallTest @@ -55,7 +59,8 @@ class MediaProjectionManagerRepositoryTest : SysuiTestCase() { fakeActivityTaskManager.addRunningTasks(task) repo.switchProjectedTask(task) - assertThat(state).isEqualTo(MediaProjectionState.SingleTask(task)) + assertThat(state).isInstanceOf(MediaProjectionState.Projecting.SingleTask::class.java) + assertThat((state as MediaProjectionState.Projecting.SingleTask).task).isEqualTo(task) } @Test @@ -97,7 +102,7 @@ class MediaProjectionManagerRepositoryTest : SysuiTestCase() { session = ContentRecordingSession.createDisplaySession(/* displayToMirror= */ 123) ) - assertThat(state).isEqualTo(MediaProjectionState.EntireScreen) + assertThat(state).isInstanceOf(MediaProjectionState.Projecting.EntireScreen::class.java) } @Test @@ -110,7 +115,27 @@ class MediaProjectionManagerRepositoryTest : SysuiTestCase() { session = ContentRecordingSession.createTaskSession(taskWindowContainerToken) ) - assertThat(state).isEqualTo(MediaProjectionState.EntireScreen) + assertThat(state).isInstanceOf(MediaProjectionState.Projecting.EntireScreen::class.java) + } + + @Test + fun mediaProjectionState_entireScreen_hasHostPackage() = + testScope.runTest { + val state by collectLastValue(repo.mediaProjectionState) + + val info = + MediaProjectionInfo( + /* packageName= */ "com.media.projection.repository.test", + /* handle= */ UserHandle.getUserHandleForUid(UserHandle.myUserId()), + /* launchCookie = */ null, + ) + fakeMediaProjectionManager.dispatchOnSessionSet( + info = info, + session = createDisplaySession(), + ) + + assertThat((state as MediaProjectionState.Projecting.EntireScreen).hostPackage) + .isEqualTo("com.media.projection.repository.test") } @Test @@ -125,6 +150,39 @@ class MediaProjectionManagerRepositoryTest : SysuiTestCase() { session = ContentRecordingSession.createTaskSession(token.asBinder()) ) - assertThat(state).isEqualTo(MediaProjectionState.SingleTask(task)) + assertThat(state).isInstanceOf(MediaProjectionState.Projecting.SingleTask::class.java) + assertThat((state as MediaProjectionState.Projecting.SingleTask).task).isEqualTo(task) + } + + @Test + fun mediaProjectionState_singleTask_hasHostPackage() = + testScope.runTest { + val state by collectLastValue(repo.mediaProjectionState) + + val token = createToken() + val task = createTask(taskId = 1, token = token) + fakeActivityTaskManager.addRunningTasks(task) + + val info = + MediaProjectionInfo( + /* packageName= */ "com.media.projection.repository.test", + /* handle= */ UserHandle.getUserHandleForUid(UserHandle.myUserId()), + /* launchCookie = */ null, + ) + fakeMediaProjectionManager.dispatchOnSessionSet( + info = info, + session = ContentRecordingSession.createTaskSession(token.asBinder()) + ) + + assertThat((state as MediaProjectionState.Projecting.SingleTask).hostPackage) + .isEqualTo("com.media.projection.repository.test") + } + + @Test + fun stopProjecting_invokesManager() = + testScope.runTest { + repo.stopProjecting() + + verify(fakeMediaProjectionManager.mediaProjectionManager).stopActiveProjection() } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/data/repository/ScreenRecordRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/data/repository/ScreenRecordRepositoryTest.kt index b77a15b43bea..61ea437f10dd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/data/repository/ScreenRecordRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/data/repository/ScreenRecordRepositoryTest.kt @@ -119,4 +119,12 @@ class ScreenRecordRepositoryTest : SysuiTestCase() { assertThat(lastModel).isEqualTo(isRecording) } + + @Test + fun stopRecording_invokesController() = + testScope.runTest { + underTest.stopRecording() + + verify(recordingController).stopRecording() + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt index 586adbd65ce8..74a299910b18 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt @@ -70,6 +70,7 @@ import com.android.systemui.statusbar.phone.DozeServiceHost import com.android.systemui.statusbar.phone.PhoneStatusBarViewController import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager import com.android.systemui.statusbar.window.StatusBarWindowStateController +import com.android.systemui.unfold.SysUIUnfoldComponent import com.android.systemui.unfold.UnfoldTransitionProgressProvider import com.android.systemui.user.domain.interactor.SelectedUserInteractor import com.android.systemui.util.mockito.any @@ -85,6 +86,7 @@ import org.junit.Assert.assertEquals import org.junit.Before import org.junit.Test import org.junit.runner.RunWith +import org.mockito.Answers import org.mockito.ArgumentCaptor import org.mockito.Mock import org.mockito.Mockito.anyFloat @@ -132,6 +134,8 @@ class NotificationShadeWindowViewControllerTest(flags: FlagsParameterization) : private lateinit var mLockscreenHostedDreamGestureListener: LockscreenHostedDreamGestureListener @Mock private lateinit var notificationInsetsController: NotificationInsetsController @Mock private lateinit var mGlanceableHubContainerController: GlanceableHubContainerController + @Mock(answer = Answers.RETURNS_DEEP_STUBS) + private lateinit var sysUiUnfoldComponent: SysUIUnfoldComponent @Mock lateinit var keyguardBouncerComponentFactory: KeyguardBouncerComponent.Factory @Mock lateinit var keyguardBouncerComponent: KeyguardBouncerComponent @Mock lateinit var keyguardSecurityContainerController: KeyguardSecurityContainerController @@ -209,6 +213,7 @@ class NotificationShadeWindowViewControllerTest(flags: FlagsParameterization) : dozeScrimController, notificationShadeWindowController, unfoldTransitionProgressProvider, + Optional.of(sysUiUnfoldComponent), keyguardUnlockAnimationController, notificationInsetsController, ambientState, diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt index e83a46bb56a0..31bd12f9a168 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt @@ -58,6 +58,7 @@ import com.android.systemui.statusbar.phone.DozeScrimController import com.android.systemui.statusbar.phone.DozeServiceHost import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager import com.android.systemui.statusbar.window.StatusBarWindowStateController +import com.android.systemui.unfold.SysUIUnfoldComponent import com.android.systemui.unfold.UnfoldTransitionProgressProvider import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.mock @@ -112,6 +113,7 @@ class NotificationShadeWindowViewTest : SysuiTestCase() { @Mock private lateinit var shadeLogger: ShadeLogger @Mock private lateinit var dumpManager: DumpManager @Mock private lateinit var pulsingGestureListener: PulsingGestureListener + @Mock private lateinit var sysUiUnfoldComponent: SysUIUnfoldComponent @Mock private lateinit var mLockscreenHostedDreamGestureListener: LockscreenHostedDreamGestureListener @Mock private lateinit var keyguardBouncerComponentFactory: KeyguardBouncerComponent.Factory @@ -181,6 +183,7 @@ class NotificationShadeWindowViewTest : SysuiTestCase() { dozeScrimController, notificationShadeWindowController, unfoldTransitionProgressProvider, + Optional.of(sysUiUnfoldComponent), keyguardUnlockAnimationController, notificationInsetsController, ambientState, diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimatorTest.kt index a05a23bb8bb1..293dc048af1d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimatorTest.kt @@ -27,6 +27,9 @@ import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock +import org.mockito.Mockito.anyFloat +import org.mockito.Mockito.spy +import org.mockito.Mockito.verify import org.mockito.Mockito.`when` as whenever import org.mockito.MockitoAnnotations @@ -78,6 +81,22 @@ class UnfoldConstantTranslateAnimatorTest : SysuiTestCase() { } @Test + fun initMultipleTimes_onTransition_translationIsSetOnlyOnce() { + animator.init(parent, MAX_TRANSLATION) + animator.init(parent, MAX_TRANSLATION) + animator.init(parent, MAX_TRANSLATION) + + // GIVEN one view with a matching id + val view = spy(View(context)) + whenever(parent.findViewById<View>(START_VIEW_ID)).thenReturn(view) + progressProvider.onTransitionStarted() + + // WHEN the transition progresses, translation is updated once + progressProvider.onTransitionProgress(.5f) + verify(view).translationX = anyFloat() + } + + @Test fun onTransition_oneMovesStartWithRTL() { // GIVEN one view with a matching id val view = View(context) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/domain/interactor/OngoingActivityChipInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/domain/interactor/OngoingActivityChipInteractorTest.kt new file mode 100644 index 000000000000..abb6e2b02103 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/domain/interactor/OngoingActivityChipInteractorTest.kt @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.chips.domain.interactor + +import android.view.View +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.animation.DialogTransitionAnimator +import com.android.systemui.res.R +import com.android.systemui.statusbar.chips.domain.interactor.OngoingActivityChipInteractor.Companion.createDialogLaunchOnClickListener +import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer +import com.android.systemui.statusbar.phone.SystemUIDialog +import kotlin.test.Test +import org.mockito.ArgumentMatchers.anyBoolean +import org.mockito.kotlin.eq +import org.mockito.kotlin.mock +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever + +@SmallTest +class OngoingActivityChipInteractorTest : SysuiTestCase() { + private val mockSystemUIDialog = mock<SystemUIDialog>() + private val dialogDelegate = SystemUIDialog.Delegate { mockSystemUIDialog } + private val dialogTransitionAnimator = mock<DialogTransitionAnimator>() + + private val chipBackgroundView = mock<ChipBackgroundContainer>() + private val chipView = + mock<View>().apply { + whenever( + this.requireViewById<ChipBackgroundContainer>( + R.id.ongoing_activity_chip_background + ) + ) + .thenReturn(chipBackgroundView) + } + + @Test + fun createDialogLaunchOnClickListener_showsDialogOnClick() { + val clickListener = + createDialogLaunchOnClickListener(dialogDelegate, dialogTransitionAnimator) + + // Dialogs must be created on the main thread + context.mainExecutor.execute { + clickListener.onClick(chipView) + verify(dialogTransitionAnimator) + .showFromView( + eq(mockSystemUIDialog), + eq(chipBackgroundView), + eq(null), + anyBoolean(), + ) + } + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractorTest.kt index 0f33b9dfc077..a4505a99cb77 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractorTest.kt @@ -16,8 +16,15 @@ package com.android.systemui.statusbar.chips.mediaprojection.domain.interactor +import android.Manifest +import android.content.Intent +import android.content.packageManager +import android.content.pm.PackageManager +import android.content.pm.ResolveInfo +import android.view.View import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase +import com.android.systemui.animation.mockDialogTransitionAnimator import com.android.systemui.common.shared.model.Icon import com.android.systemui.coroutines.collectLastValue import com.android.systemui.kosmos.Kosmos @@ -27,11 +34,24 @@ import com.android.systemui.mediaprojection.data.repository.fakeMediaProjectionR import com.android.systemui.mediaprojection.taskswitcher.FakeActivityTaskManager.Companion.createTask import com.android.systemui.res.R import com.android.systemui.statusbar.chips.domain.model.OngoingActivityChipModel -import com.android.systemui.statusbar.chips.ui.viewmodel.mediaProjectionChipInteractor +import com.android.systemui.statusbar.chips.mediaprojection.ui.view.EndCastToOtherDeviceDialogDelegate +import com.android.systemui.statusbar.chips.mediaprojection.ui.view.EndShareToAppDialogDelegate +import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer +import com.android.systemui.statusbar.phone.SystemUIDialog +import com.android.systemui.statusbar.phone.mockSystemUIDialogFactory import com.android.systemui.util.time.fakeSystemClock import com.google.common.truth.Truth.assertThat import kotlin.test.Test import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.mockito.ArgumentMatchers.anyBoolean +import org.mockito.ArgumentMatchers.anyInt +import org.mockito.Mockito.doAnswer +import org.mockito.kotlin.any +import org.mockito.kotlin.eq +import org.mockito.kotlin.mock +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever @SmallTest class MediaProjectionChipInteractorTest : SysuiTestCase() { @@ -40,6 +60,30 @@ class MediaProjectionChipInteractorTest : SysuiTestCase() { private val mediaProjectionRepo = kosmos.fakeMediaProjectionRepository private val systemClock = kosmos.fakeSystemClock + private val mockCastDialog = mock<SystemUIDialog>() + private val mockShareDialog = mock<SystemUIDialog>() + + private val chipBackgroundView = mock<ChipBackgroundContainer>() + private val chipView = + mock<View>().apply { + whenever( + this.requireViewById<ChipBackgroundContainer>( + R.id.ongoing_activity_chip_background + ) + ) + .thenReturn(chipBackgroundView) + } + + @Before + fun setUp() { + setUpPackageManagerForMediaProjection(kosmos) + + whenever(kosmos.mockSystemUIDialogFactory.create(any<EndCastToOtherDeviceDialogDelegate>())) + .thenReturn(mockCastDialog) + whenever(kosmos.mockSystemUIDialogFactory.create(any<EndShareToAppDialogDelegate>())) + .thenReturn(mockShareDialog) + } + private val underTest = kosmos.mediaProjectionChipInteractor @Test @@ -53,12 +97,15 @@ class MediaProjectionChipInteractorTest : SysuiTestCase() { } @Test - fun chip_singleTaskState_isShownWithIcon() = + fun chip_singleTaskState_otherDevicesPackage_castToOtherDeviceChipShown() = testScope.runTest { val latest by collectLastValue(underTest.chip) mediaProjectionRepo.mediaProjectionState.value = - MediaProjectionState.SingleTask(createTask(taskId = 1)) + MediaProjectionState.Projecting.SingleTask( + CAST_TO_OTHER_DEVICES_PACKAGE, + createTask(taskId = 1) + ) assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java) val icon = (latest as OngoingActivityChipModel.Shown).icon @@ -66,11 +113,12 @@ class MediaProjectionChipInteractorTest : SysuiTestCase() { } @Test - fun chip_entireScreenState_isShownWithIcon() = + fun chip_entireScreenState_otherDevicesPackage_castToOtherDeviceChipShown() = testScope.runTest { val latest by collectLastValue(underTest.chip) - mediaProjectionRepo.mediaProjectionState.value = MediaProjectionState.EntireScreen + mediaProjectionRepo.mediaProjectionState.value = + MediaProjectionState.Projecting.EntireScreen(CAST_TO_OTHER_DEVICES_PACKAGE) assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java) val icon = (latest as OngoingActivityChipModel.Shown).icon @@ -78,12 +126,39 @@ class MediaProjectionChipInteractorTest : SysuiTestCase() { } @Test + fun chip_singleTaskState_normalPackage_shareToAppChipShown() = + testScope.runTest { + val latest by collectLastValue(underTest.chip) + + mediaProjectionRepo.mediaProjectionState.value = + MediaProjectionState.Projecting.SingleTask(NORMAL_PACKAGE, createTask(taskId = 1)) + + assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java) + val icon = (latest as OngoingActivityChipModel.Shown).icon + assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.ic_screenshot_share) + } + + @Test + fun chip_entireScreenState_normalPackage_shareToAppChipShown() = + testScope.runTest { + val latest by collectLastValue(underTest.chip) + + mediaProjectionRepo.mediaProjectionState.value = + MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE) + + assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java) + val icon = (latest as OngoingActivityChipModel.Shown).icon + assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.ic_screenshot_share) + } + + @Test fun chip_timeResetsOnEachNewShare() = testScope.runTest { val latest by collectLastValue(underTest.chip) systemClock.setElapsedRealtime(1234) - mediaProjectionRepo.mediaProjectionState.value = MediaProjectionState.EntireScreen + mediaProjectionRepo.mediaProjectionState.value = + MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE) assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java) assertThat((latest as OngoingActivityChipModel.Shown).startTimeMs).isEqualTo(1234) @@ -92,9 +167,99 @@ class MediaProjectionChipInteractorTest : SysuiTestCase() { assertThat(latest).isInstanceOf(OngoingActivityChipModel.Hidden::class.java) systemClock.setElapsedRealtime(5678) - mediaProjectionRepo.mediaProjectionState.value = MediaProjectionState.EntireScreen + mediaProjectionRepo.mediaProjectionState.value = + MediaProjectionState.Projecting.SingleTask( + CAST_TO_OTHER_DEVICES_PACKAGE, + createTask(taskId = 1) + ) assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java) assertThat((latest as OngoingActivityChipModel.Shown).startTimeMs).isEqualTo(5678) } + + @Test + fun chip_castToOtherDevice_clickListenerShowsCastDialog() = + testScope.runTest { + val latest by collectLastValue(underTest.chip) + mediaProjectionRepo.mediaProjectionState.value = + MediaProjectionState.Projecting.EntireScreen(CAST_TO_OTHER_DEVICES_PACKAGE) + + val clickListener = ((latest as OngoingActivityChipModel.Shown).onClickListener) + + // Dialogs must be created on the main thread + context.mainExecutor.execute { + clickListener.onClick(chipView) + verify(kosmos.mockDialogTransitionAnimator) + .showFromView( + eq(mockCastDialog), + eq(chipBackgroundView), + eq(null), + anyBoolean(), + ) + } + } + + @Test + fun chip_shareToApp_clickListenerShowsShareDialog() = + testScope.runTest { + val latest by collectLastValue(underTest.chip) + mediaProjectionRepo.mediaProjectionState.value = + MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE) + + val clickListener = ((latest as OngoingActivityChipModel.Shown).onClickListener) + + // Dialogs must be created on the main thread + context.mainExecutor.execute { + clickListener.onClick(chipView) + verify(kosmos.mockDialogTransitionAnimator) + .showFromView( + eq(mockShareDialog), + eq(chipBackgroundView), + eq(null), + anyBoolean(), + ) + } + } + + companion object { + const val CAST_TO_OTHER_DEVICES_PACKAGE = "other.devices.package" + const val NORMAL_PACKAGE = "some.normal.package" + + /** + * Sets up [kosmos.packageManager] so that [CAST_TO_OTHER_DEVICES_PACKAGE] is marked as a + * package that casts to other devices, and [NORMAL_PACKAGE] is *not* marked as casting to + * other devices. + */ + fun setUpPackageManagerForMediaProjection(kosmos: Kosmos) { + kosmos.packageManager.apply { + whenever( + this.checkPermission( + Manifest.permission.REMOTE_DISPLAY_PROVIDER, + CAST_TO_OTHER_DEVICES_PACKAGE + ) + ) + .thenReturn(PackageManager.PERMISSION_GRANTED) + whenever( + this.checkPermission( + Manifest.permission.REMOTE_DISPLAY_PROVIDER, + NORMAL_PACKAGE + ) + ) + .thenReturn(PackageManager.PERMISSION_DENIED) + + doAnswer { + // See Utils.isHeadlessRemoteDisplayProvider + if ( + (it.arguments[0] as Intent).`package` == CAST_TO_OTHER_DEVICES_PACKAGE + ) { + emptyList() + } else { + listOf(mock<ResolveInfo>()) + } + } + .whenever(this) + .queryIntentActivities(any(), anyInt()) + } + } + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndCastToOtherDeviceDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndCastToOtherDeviceDialogDelegateTest.kt new file mode 100644 index 000000000000..9a2f545fa67e --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndCastToOtherDeviceDialogDelegateTest.kt @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.chips.mediaprojection.ui.view + +import android.content.DialogInterface +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.testScope +import com.android.systemui.mediaprojection.data.repository.fakeMediaProjectionRepository +import com.android.systemui.res.R +import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.mediaProjectionChipInteractor +import com.android.systemui.statusbar.phone.SystemUIDialog +import com.android.systemui.statusbar.phone.mockSystemUIDialogFactory +import com.google.common.truth.Truth.assertThat +import kotlin.test.Test +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.mockito.kotlin.argumentCaptor +import org.mockito.kotlin.eq +import org.mockito.kotlin.mock +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever + +@SmallTest +@OptIn(ExperimentalCoroutinesApi::class) +class EndCastToOtherDeviceDialogDelegateTest : SysuiTestCase() { + private val kosmos = Kosmos() + private val sysuiDialog = mock<SystemUIDialog>() + private val sysuiDialogFactory = kosmos.mockSystemUIDialogFactory + private val underTest = + EndCastToOtherDeviceDialogDelegate( + sysuiDialogFactory, + kosmos.mediaProjectionChipInteractor, + ) + + @Before + fun setUp() { + whenever(sysuiDialogFactory.create(eq(underTest), eq(context))).thenReturn(sysuiDialog) + } + + @Test + fun icon() { + underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null) + + verify(sysuiDialog).setIcon(R.drawable.ic_cast_connected) + } + + @Test + fun title() { + underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null) + + verify(sysuiDialog).setTitle(R.string.cast_to_other_device_stop_dialog_title) + } + + @Test + fun message() { + underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null) + + verify(sysuiDialog).setMessage(R.string.cast_to_other_device_stop_dialog_message) + } + + @Test + fun negativeButton() { + underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null) + + verify(sysuiDialog).setNegativeButton(R.string.close_dialog_button, null) + } + + @Test + fun positiveButton() = + kosmos.testScope.runTest { + underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null) + + val clickListener = argumentCaptor<DialogInterface.OnClickListener>() + + // Verify the button has the right text + verify(sysuiDialog) + .setPositiveButton( + eq(R.string.cast_to_other_device_stop_dialog_button), + clickListener.capture() + ) + + // Verify that clicking the button stops the recording + assertThat(kosmos.fakeMediaProjectionRepository.stopProjectingInvoked).isFalse() + + clickListener.firstValue.onClick(mock<DialogInterface>(), 0) + runCurrent() + + assertThat(kosmos.fakeMediaProjectionRepository.stopProjectingInvoked).isTrue() + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndShareToAppDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndShareToAppDialogDelegateTest.kt new file mode 100644 index 000000000000..1d6e8669274d --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndShareToAppDialogDelegateTest.kt @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.chips.mediaprojection.ui.view + +import android.content.DialogInterface +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.testScope +import com.android.systemui.mediaprojection.data.repository.fakeMediaProjectionRepository +import com.android.systemui.res.R +import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.mediaProjectionChipInteractor +import com.android.systemui.statusbar.phone.SystemUIDialog +import com.android.systemui.statusbar.phone.mockSystemUIDialogFactory +import com.google.common.truth.Truth.assertThat +import kotlin.test.Test +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.mockito.kotlin.argumentCaptor +import org.mockito.kotlin.eq +import org.mockito.kotlin.mock +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever + +@SmallTest +@OptIn(ExperimentalCoroutinesApi::class) +class EndShareToAppDialogDelegateTest : SysuiTestCase() { + private val kosmos = Kosmos() + private val sysuiDialog = mock<SystemUIDialog>() + private val sysuiDialogFactory = kosmos.mockSystemUIDialogFactory + private val underTest = + EndShareToAppDialogDelegate( + sysuiDialogFactory, + kosmos.mediaProjectionChipInteractor, + ) + + @Before + fun setUp() { + whenever(sysuiDialogFactory.create(eq(underTest), eq(context))).thenReturn(sysuiDialog) + } + + @Test + fun icon() { + underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null) + + verify(sysuiDialog).setIcon(R.drawable.ic_screenshot_share) + } + + @Test + fun title() { + underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null) + + verify(sysuiDialog).setTitle(R.string.share_to_app_stop_dialog_title) + } + + @Test + fun message() { + underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null) + + verify(sysuiDialog).setMessage(R.string.share_to_app_stop_dialog_message) + } + + @Test + fun negativeButton() { + underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null) + + verify(sysuiDialog).setNegativeButton(R.string.close_dialog_button, null) + } + + @Test + fun positiveButton() = + kosmos.testScope.runTest { + underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null) + + val clickListener = argumentCaptor<DialogInterface.OnClickListener>() + + // Verify the button has the right text + verify(sysuiDialog) + .setPositiveButton( + eq(R.string.share_to_app_stop_dialog_button), + clickListener.capture() + ) + + // Verify that clicking the button stops the recording + assertThat(kosmos.fakeMediaProjectionRepository.stopProjectingInvoked).isFalse() + + clickListener.firstValue.onClick(mock<DialogInterface>(), 0) + runCurrent() + + assertThat(kosmos.fakeMediaProjectionRepository.stopProjectingInvoked).isTrue() + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractorTest.kt index 25efaf10fce7..f6c3adb364ce 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractorTest.kt @@ -16,8 +16,10 @@ package com.android.systemui.statusbar.chips.screenrecord.domain.interactor +import android.view.View import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase +import com.android.systemui.animation.mockDialogTransitionAnimator import com.android.systemui.common.shared.model.Icon import com.android.systemui.coroutines.collectLastValue import com.android.systemui.kosmos.Kosmos @@ -26,11 +28,21 @@ import com.android.systemui.res.R import com.android.systemui.screenrecord.data.model.ScreenRecordModel import com.android.systemui.screenrecord.data.repository.screenRecordRepository import com.android.systemui.statusbar.chips.domain.model.OngoingActivityChipModel +import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer import com.android.systemui.statusbar.chips.ui.viewmodel.screenRecordChipInteractor +import com.android.systemui.statusbar.phone.SystemUIDialog +import com.android.systemui.statusbar.phone.mockSystemUIDialogFactory import com.android.systemui.util.time.fakeSystemClock import com.google.common.truth.Truth.assertThat import kotlin.test.Test import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.mockito.ArgumentMatchers.anyBoolean +import org.mockito.kotlin.any +import org.mockito.kotlin.eq +import org.mockito.kotlin.mock +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever @SmallTest class ScreenRecordChipInteractorTest : SysuiTestCase() { @@ -38,9 +50,27 @@ class ScreenRecordChipInteractorTest : SysuiTestCase() { private val testScope = kosmos.testScope private val screenRecordRepo = kosmos.screenRecordRepository private val systemClock = kosmos.fakeSystemClock + private val mockSystemUIDialog = mock<SystemUIDialog>() + + private val chipBackgroundView = mock<ChipBackgroundContainer>() + private val chipView = + mock<View>().apply { + whenever( + this.requireViewById<ChipBackgroundContainer>( + R.id.ongoing_activity_chip_background + ) + ) + .thenReturn(chipBackgroundView) + } private val underTest = kosmos.screenRecordChipInteractor + @Before + fun setUp() { + whenever(kosmos.mockSystemUIDialogFactory.create(any<SystemUIDialog.Delegate>())) + .thenReturn(mockSystemUIDialog) + } + @Test fun chip_doingNothingState_isHidden() = testScope.runTest { @@ -70,7 +100,7 @@ class ScreenRecordChipInteractorTest : SysuiTestCase() { assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java) val icon = (latest as OngoingActivityChipModel.Shown).icon - assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.stat_sys_screen_record) + assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.ic_screenrecord) } @Test @@ -93,4 +123,25 @@ class ScreenRecordChipInteractorTest : SysuiTestCase() { assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java) assertThat((latest as OngoingActivityChipModel.Shown).startTimeMs).isEqualTo(5678) } + + @Test + fun chip_clickListenerShowsDialog() = + testScope.runTest { + val latest by collectLastValue(underTest.chip) + screenRecordRepo.screenRecordState.value = ScreenRecordModel.Recording + + val clickListener = ((latest as OngoingActivityChipModel.Shown).onClickListener) + + // Dialogs must be created on the main thread + context.mainExecutor.execute { + clickListener.onClick(chipView) + verify(kosmos.mockDialogTransitionAnimator) + .showFromView( + eq(mockSystemUIDialog), + eq(chipBackgroundView), + eq(null), + anyBoolean(), + ) + } + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/screenrecord/ui/view/EndScreenRecordingDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/screenrecord/ui/view/EndScreenRecordingDialogDelegateTest.kt new file mode 100644 index 000000000000..bca6763a4989 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/screenrecord/ui/view/EndScreenRecordingDialogDelegateTest.kt @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.chips.screenrecord.ui.view + +import android.content.DialogInterface +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.testScope +import com.android.systemui.res.R +import com.android.systemui.screenrecord.data.repository.screenRecordRepository +import com.android.systemui.statusbar.chips.ui.viewmodel.screenRecordChipInteractor +import com.android.systemui.statusbar.phone.SystemUIDialog +import com.android.systemui.statusbar.phone.mockSystemUIDialogFactory +import com.google.common.truth.Truth.assertThat +import kotlin.test.Test +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.mockito.kotlin.argumentCaptor +import org.mockito.kotlin.eq +import org.mockito.kotlin.mock +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever + +@SmallTest +@OptIn(ExperimentalCoroutinesApi::class) +class EndScreenRecordingDialogDelegateTest : SysuiTestCase() { + private val kosmos = Kosmos() + + private val sysuiDialog = mock<SystemUIDialog>() + private val sysuiDialogFactory = kosmos.mockSystemUIDialogFactory + + private val underTest = + EndScreenRecordingDialogDelegate( + sysuiDialogFactory, + kosmos.screenRecordChipInteractor, + ) + + @Before + fun setUp() { + whenever(sysuiDialogFactory.create(eq(underTest), eq(context))).thenReturn(sysuiDialog) + } + + @Test + fun icon() { + underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null) + + verify(sysuiDialog).setIcon(R.drawable.ic_screenrecord) + } + + @Test + fun title() { + underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null) + + verify(sysuiDialog).setTitle(R.string.screenrecord_stop_dialog_title) + } + + @Test + fun message() { + underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null) + + verify(sysuiDialog).setMessage(R.string.screenrecord_stop_dialog_message) + } + + @Test + fun negativeButton() { + underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null) + + verify(sysuiDialog).setNegativeButton(R.string.close_dialog_button, null) + } + + @Test + fun positiveButton() = + kosmos.testScope.runTest { + underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null) + + val clickListener = argumentCaptor<DialogInterface.OnClickListener>() + + // Verify the button has the right text + verify(sysuiDialog) + .setPositiveButton( + eq(R.string.screenrecord_stop_dialog_button), + clickListener.capture() + ) + + // Verify that clicking the button stops the recording + assertThat(kosmos.screenRecordRepository.stopRecordingInvoked).isFalse() + + clickListener.firstValue.onClick(mock<DialogInterface>(), 0) + runCurrent() + + assertThat(kosmos.screenRecordRepository.stopRecordingInvoked).isTrue() + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt index 121229c321b6..67129639efaf 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt @@ -30,8 +30,11 @@ import com.android.systemui.res.R import com.android.systemui.screenrecord.data.model.ScreenRecordModel import com.android.systemui.screenrecord.data.repository.screenRecordRepository import com.android.systemui.statusbar.chips.domain.model.OngoingActivityChipModel +import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.NORMAL_PACKAGE +import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.setUpPackageManagerForMediaProjection import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.test.runTest +import org.junit.Before import org.junit.Test @SmallTest @@ -46,6 +49,11 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() { private val underTest = kosmos.ongoingActivityChipsViewModel + @Before + fun setUp() { + setUpPackageManagerForMediaProjection(kosmos) + } + @Test fun chip_allHidden_hidden() = testScope.runTest { @@ -91,7 +99,8 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() { fun chip_screenRecordShowAndMediaProjectionShow_screenRecordShown() = testScope.runTest { screenRecordState.value = ScreenRecordModel.Recording - mediaProjectionState.value = MediaProjectionState.EntireScreen + mediaProjectionState.value = + MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE) callState.value = OngoingActivityChipModel.Hidden val latest by collectLastValue(underTest.chip) @@ -103,7 +112,8 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() { fun chip_mediaProjectionShowAndCallShow_mediaProjectionShown() = testScope.runTest { screenRecordState.value = ScreenRecordModel.DoingNothing - mediaProjectionState.value = MediaProjectionState.EntireScreen + mediaProjectionState.value = + MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE) val callChip = OngoingActivityChipModel.Shown( Icon.Resource(R.drawable.ic_call, ContentDescription.Loaded("icon")), @@ -113,7 +123,7 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() { val latest by collectLastValue(underTest.chip) - assertIsMediaProjectionChip(latest) + assertIsShareToAppChip(latest) } @Test @@ -152,10 +162,14 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() { assertThat(latest).isEqualTo(callChip) // WHEN the higher priority media projection chip is added - mediaProjectionState.value = MediaProjectionState.SingleTask(createTask(taskId = 1)) + mediaProjectionState.value = + MediaProjectionState.Projecting.SingleTask( + NORMAL_PACKAGE, + createTask(taskId = 1), + ) // THEN the higher priority media projection chip is used - assertIsMediaProjectionChip(latest) + assertIsShareToAppChip(latest) // WHEN the higher priority screen record chip is added screenRecordState.value = ScreenRecordModel.Recording @@ -169,7 +183,8 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() { testScope.runTest { // WHEN all chips are active screenRecordState.value = ScreenRecordModel.Recording - mediaProjectionState.value = MediaProjectionState.EntireScreen + mediaProjectionState.value = + MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE) val callChip = OngoingActivityChipModel.Shown( @@ -187,7 +202,7 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() { screenRecordState.value = ScreenRecordModel.DoingNothing // THEN the lower priority media projection is used - assertIsMediaProjectionChip(latest) + assertIsShareToAppChip(latest) // WHEN the higher priority media projection is removed mediaProjectionState.value = MediaProjectionState.NotProjecting @@ -200,13 +215,13 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() { fun assertIsScreenRecordChip(latest: OngoingActivityChipModel?) { assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java) val icon = (latest as OngoingActivityChipModel.Shown).icon - assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.stat_sys_screen_record) + assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.ic_screenrecord) } - fun assertIsMediaProjectionChip(latest: OngoingActivityChipModel?) { + fun assertIsShareToAppChip(latest: OngoingActivityChipModel?) { assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java) val icon = (latest as OngoingActivityChipModel.Shown).icon - assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.ic_cast_connected) + assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.ic_screenshot_share) } } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java index f3d640758cf3..53e643e00be4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java @@ -223,7 +223,8 @@ public class KeyguardStatusBarViewControllerTest extends SysuiTestCase { mFakeExecutor, mBackgroundExecutor, mLogger, - mStatusOverlayHoverListenerFactory + mStatusOverlayHoverListenerFactory, + mKosmos.getCommunalSceneInteractor() ); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt index c9fe44918757..cdb2b883078a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt @@ -37,8 +37,10 @@ import com.android.systemui.mediaprojection.data.repository.fakeMediaProjectionR import com.android.systemui.screenrecord.data.model.ScreenRecordModel import com.android.systemui.screenrecord.data.repository.screenRecordRepository import com.android.systemui.statusbar.chips.domain.model.OngoingActivityChipModel -import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipsViewModelTest.Companion.assertIsMediaProjectionChip +import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.NORMAL_PACKAGE +import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.setUpPackageManagerForMediaProjection import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipsViewModelTest.Companion.assertIsScreenRecordChip +import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipsViewModelTest.Companion.assertIsShareToAppChip import com.android.systemui.statusbar.chips.ui.viewmodel.ongoingActivityChipsViewModel import com.android.systemui.statusbar.data.model.StatusBarMode import com.android.systemui.statusbar.data.repository.FakeStatusBarModeRepository.Companion.DISPLAY_ID @@ -55,6 +57,7 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.test.UnconfinedTestDispatcher import kotlinx.coroutines.test.runTest +import org.junit.Before import org.junit.Test @SmallTest @@ -77,6 +80,11 @@ class CollapsedStatusBarViewModelImplTest : SysuiTestCase() { kosmos.applicationCoroutineScope, ) + @Before + fun setUp() { + setUpPackageManagerForMediaProjection(kosmos) + } + @Test fun isTransitioningFromLockscreenToOccluded_started_isTrue() = testScope.runTest { @@ -405,9 +413,9 @@ class CollapsedStatusBarViewModelImplTest : SysuiTestCase() { assertThat(latest).isEqualTo(OngoingActivityChipModel.Hidden) kosmos.fakeMediaProjectionRepository.mediaProjectionState.value = - MediaProjectionState.EntireScreen + MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE) - assertIsMediaProjectionChip(latest) + assertIsShareToAppChip(latest) } private fun activeNotificationsStore(notifications: List<ActiveNotificationModel>) = diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/animation/DialogTransitionAnimatorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/animation/DialogTransitionAnimatorKosmos.kt index 62e56beccc93..976a19ce6265 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/animation/DialogTransitionAnimatorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/animation/DialogTransitionAnimatorKosmos.kt @@ -20,6 +20,7 @@ import android.content.applicationContext import com.android.systemui.jank.interactionJankMonitor import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture +import com.android.systemui.util.mockito.mock val Kosmos.dialogTransitionAnimator by Fixture { fakeDialogTransitionAnimator( @@ -29,3 +30,5 @@ val Kosmos.dialogTransitionAnimator by Fixture { interactionJankMonitor = interactionJankMonitor, ) } + +val Kosmos.mockDialogTransitionAnimator by Fixture { mock<DialogTransitionAnimator>() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorFactory.kt index 02842cc3f56b..b5ca964d6968 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorFactory.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorFactory.kt @@ -24,6 +24,7 @@ import com.android.systemui.flags.FakeFeatureFlags import com.android.systemui.keyguard.data.repository.FakeCommandQueue import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.keyguard.shared.model.TransitionStep import com.android.systemui.power.domain.interactor.PowerInteractor import com.android.systemui.power.domain.interactor.PowerInteractorFactory import com.android.systemui.scene.domain.interactor.SceneInteractor @@ -34,6 +35,7 @@ import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.test.TestScope /** @@ -60,9 +62,11 @@ object KeyguardInteractorFactory { ): WithDependencies { // Mock these until they are replaced by kosmos val currentKeyguardStateFlow = MutableSharedFlow<KeyguardState>() + val transitionStateFlow = MutableStateFlow(TransitionStep()) val keyguardTransitionInteractor = mock<KeyguardTransitionInteractor>().also { whenever(it.currentKeyguardState).thenReturn(currentKeyguardStateFlow) + whenever(it.transitionState).thenReturn(transitionStateFlow) } val configurationDimensionFlow = MutableSharedFlow<ConfigurationBasedDimensions>() configurationDimensionFlow.tryEmit( diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelKosmos.kt index 6d46694db650..3c62b44ed2c4 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelKosmos.kt @@ -22,6 +22,7 @@ import com.android.systemui.keyguard.domain.interactor.keyguardClockInteractor import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.shade.domain.interactor.shadeInteractor +import com.android.systemui.statusbar.notification.icon.ui.viewmodel.notificationIconContainerAlwaysOnDisplayViewModel import com.android.systemui.statusbar.notification.stack.domain.interactor.notificationsKeyguardInteractor import com.android.systemui.statusbar.ui.systemBarUtilsProxy @@ -30,6 +31,7 @@ val Kosmos.keyguardClockViewModel by KeyguardClockViewModel( keyguardClockInteractor = keyguardClockInteractor, applicationScope = applicationCoroutineScope, + aodNotificationIconViewModel = notificationIconContainerAlwaysOnDisplayViewModel, notifsKeyguardInteractor = notificationsKeyguardInteractor, shadeInteractor = shadeInteractor, systemBarUtils = systemBarUtilsProxy, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt index 45a14ad69372..67f844340096 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt @@ -29,6 +29,7 @@ import com.android.systemui.common.ui.data.repository.fakeConfigurationRepositor import com.android.systemui.common.ui.domain.interactor.configurationInteractor import com.android.systemui.communal.data.repository.fakeCommunalSceneRepository import com.android.systemui.communal.domain.interactor.communalInteractor +import com.android.systemui.communal.domain.interactor.communalSceneInteractor import com.android.systemui.communal.ui.viewmodel.communalTransitionViewModel import com.android.systemui.concurrency.fakeExecutor import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor @@ -113,6 +114,7 @@ class KosmosJavaAdapter() { val deviceEntryUdfpsInteractor by lazy { kosmos.deviceEntryUdfpsInteractor } val deviceUnlockedInteractor by lazy { kosmos.deviceUnlockedInteractor } val communalInteractor by lazy { kosmos.communalInteractor } + val communalSceneInteractor by lazy { kosmos.communalSceneInteractor } val sceneContainerPlugin by lazy { kosmos.sceneContainerPlugin } val deviceProvisioningInteractor by lazy { kosmos.deviceProvisioningInteractor } val fakeDeviceProvisioningRepository by lazy { kosmos.fakeDeviceProvisioningRepository } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/mediaprojection/data/repository/FakeMediaProjectionRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/mediaprojection/data/repository/FakeMediaProjectionRepository.kt index c4365c9093d2..d631f926176d 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/mediaprojection/data/repository/FakeMediaProjectionRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/mediaprojection/data/repository/FakeMediaProjectionRepository.kt @@ -25,4 +25,10 @@ class FakeMediaProjectionRepository : MediaProjectionRepository { override val mediaProjectionState: MutableStateFlow<MediaProjectionState> = MutableStateFlow(MediaProjectionState.NotProjecting) + + var stopProjectingInvoked = false + + override suspend fun stopProjecting() { + stopProjectingInvoked = true + } } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneContainerStartableKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneContainerStartableKosmos.kt index d82286fd3569..cf18c0e295ea 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneContainerStartableKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneContainerStartableKosmos.kt @@ -26,6 +26,7 @@ import com.android.systemui.deviceentry.domain.interactor.deviceEntryFaceAuthInt import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor import com.android.systemui.keyguard.domain.interactor.keyguardInteractor +import com.android.systemui.keyguard.domain.interactor.windowManagerLockscreenVisibilityInteractor import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture import com.android.systemui.kosmos.testScope @@ -67,5 +68,6 @@ val Kosmos.sceneContainerStartable by Fixture { uiEventLogger = uiEventLogger, sceneBackInteractor = sceneBackInteractor, shadeSessionStorage = shadeSessionStorage, + windowMgrLockscreenVisInteractor = windowManagerLockscreenVisibilityInteractor, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/screenrecord/data/repository/FakeScreenRecordRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/screenrecord/data/repository/FakeScreenRecordRepository.kt index fb0e3687bb1a..30b4763118a7 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/screenrecord/data/repository/FakeScreenRecordRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/screenrecord/data/repository/FakeScreenRecordRepository.kt @@ -22,4 +22,10 @@ import kotlinx.coroutines.flow.MutableStateFlow class FakeScreenRecordRepository : ScreenRecordRepository { override val screenRecordState: MutableStateFlow<ScreenRecordModel> = MutableStateFlow(ScreenRecordModel.DoingNothing) + + var stopRecordingInvoked = false + + override suspend fun stopRecording() { + stopRecordingInvoked = true + } } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelKosmos.kt index 8d653f7212aa..0e21698ef271 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelKosmos.kt @@ -21,6 +21,7 @@ import com.android.systemui.broadcast.broadcastDispatcher import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.plugins.activityStarter +import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.shade.domain.interactor.privacyChipInteractor import com.android.systemui.shade.domain.interactor.shadeHeaderClockInteractor import com.android.systemui.shade.domain.interactor.shadeInteractor @@ -33,6 +34,7 @@ val Kosmos.shadeHeaderViewModel: ShadeHeaderViewModel by applicationScope = applicationCoroutineScope, context = applicationContext, activityStarter = activityStarter, + sceneInteractor = sceneInteractor, shadeInteractor = shadeInteractor, mobileIconsInteractor = mobileIconsInteractor, mobileIconsViewModel = mobileIconsViewModel, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractorKosmos.kt new file mode 100644 index 000000000000..062b4484044c --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractorKosmos.kt @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.chips.mediaprojection.domain.interactor + +import android.content.packageManager +import com.android.systemui.animation.mockDialogTransitionAnimator +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.applicationCoroutineScope +import com.android.systemui.mediaprojection.data.repository.fakeMediaProjectionRepository +import com.android.systemui.statusbar.phone.mockSystemUIDialogFactory +import com.android.systemui.util.time.fakeSystemClock + +val Kosmos.mediaProjectionChipInteractor: MediaProjectionChipInteractor by + Kosmos.Fixture { + MediaProjectionChipInteractor( + scope = applicationCoroutineScope, + mediaProjectionRepository = fakeMediaProjectionRepository, + packageManager = packageManager, + systemClock = fakeSystemClock, + dialogFactory = mockSystemUIDialogFactory, + dialogTransitionAnimator = mockDialogTransitionAnimator, + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelKosmos.kt index 88bde2ed5d8f..51ec54000261 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelKosmos.kt @@ -16,13 +16,14 @@ package com.android.systemui.statusbar.chips.ui.viewmodel +import com.android.systemui.animation.mockDialogTransitionAnimator import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.kosmos.testScope -import com.android.systemui.mediaprojection.data.repository.fakeMediaProjectionRepository import com.android.systemui.screenrecord.data.repository.screenRecordRepository -import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractor +import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.mediaProjectionChipInteractor import com.android.systemui.statusbar.chips.screenrecord.domain.interactor.ScreenRecordChipInteractor +import com.android.systemui.statusbar.phone.mockSystemUIDialogFactory import com.android.systemui.util.time.fakeSystemClock val Kosmos.screenRecordChipInteractor: ScreenRecordChipInteractor by @@ -30,15 +31,8 @@ val Kosmos.screenRecordChipInteractor: ScreenRecordChipInteractor by ScreenRecordChipInteractor( scope = applicationCoroutineScope, screenRecordRepository = screenRecordRepository, - systemClock = fakeSystemClock, - ) - } - -val Kosmos.mediaProjectionChipInteractor: MediaProjectionChipInteractor by - Kosmos.Fixture { - MediaProjectionChipInteractor( - scope = applicationCoroutineScope, - mediaProjectionRepository = fakeMediaProjectionRepository, + dialogFactory = mockSystemUIDialogFactory, + dialogTransitionAnimator = mockDialogTransitionAnimator, systemClock = fakeSystemClock, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt index d00eedf22efc..299486fc8ef4 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt @@ -31,6 +31,7 @@ import com.android.systemui.keyguard.ui.viewmodel.glanceableHubToLockscreenTrans import com.android.systemui.keyguard.ui.viewmodel.goneToAodTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.goneToDozingTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.goneToDreamingTransitionViewModel +import com.android.systemui.keyguard.ui.viewmodel.goneToLockscreenTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.lockscreenToDreamingTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.lockscreenToGlanceableHubTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.lockscreenToGoneTransitionViewModel @@ -70,6 +71,7 @@ val Kosmos.sharedNotificationContainerViewModel by Fixture { goneToAodTransitionViewModel = goneToAodTransitionViewModel, goneToDozingTransitionViewModel = goneToDozingTransitionViewModel, goneToDreamingTransitionViewModel = goneToDreamingTransitionViewModel, + goneToLockscreenTransitionViewModel = goneToLockscreenTransitionViewModel, glanceableHubToLockscreenTransitionViewModel = glanceableHubToLockscreenTransitionViewModel, lockscreenToDreamingTransitionViewModel = lockscreenToDreamingTransitionViewModel, lockscreenToGlanceableHubTransitionViewModel = lockscreenToGlanceableHubTransitionViewModel, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/SystemUIDialogFactoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/SystemUIDialogFactoryKosmos.kt index 3bb9580e69fa..1851c89ecd94 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/SystemUIDialogFactoryKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/SystemUIDialogFactoryKosmos.kt @@ -21,8 +21,9 @@ import com.android.systemui.animation.dialogTransitionAnimator import com.android.systemui.broadcast.broadcastDispatcher import com.android.systemui.kosmos.Kosmos import com.android.systemui.model.sysUiState +import com.android.systemui.util.mockito.mock -val Kosmos.systemUIDialogFactory by +val Kosmos.systemUIDialogFactory: SystemUIDialogFactory by Kosmos.Fixture { SystemUIDialogFactory( applicationContext, @@ -32,3 +33,6 @@ val Kosmos.systemUIDialogFactory by dialogTransitionAnimator, ) } + +val Kosmos.mockSystemUIDialogFactory: SystemUIDialog.Factory by + Kosmos.Fixture { mock<SystemUIDialog.Factory>() } diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/dagger/NaturalRotation.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/dagger/NaturalRotation.kt new file mode 100644 index 000000000000..be02487c75e6 --- /dev/null +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/dagger/NaturalRotation.kt @@ -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 com.android.systemui.unfold.dagger + +import javax.inject.Qualifier + +/** Qualifier annotation for a progress provider that emits animation events only when + * in natural rotation */ +@Qualifier @Retention(AnnotationRetention.RUNTIME) annotation class NaturalRotation diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java index 6fc05b72da9b..eae516e10d6e 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java @@ -2003,7 +2003,7 @@ public final class AutofillManagerService final AutofillManagerServiceImpl service = peekServiceForUserWithLocalBinderIdentityLocked(userId); if (service != null) { - service.setViewAutofilled(sessionId, getCallingUid(), id); + service.setViewAutofilledLocked(sessionId, getCallingUid(), id); } else if (sVerbose) { Slog.v(TAG, "setAutofillFailure(): no service for " + userId); } diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java index 588266fba47a..9ad15b2d431f 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java @@ -478,7 +478,7 @@ final class AutofillManagerServiceImpl } @GuardedBy("mLock") - void setViewAutofilled(int sessionId, int uid, @NonNull AutofillId id) { + void setViewAutofilledLocked(int sessionId, int uid, @NonNull AutofillId id) { if (!isEnabledLocked()) { Slog.wtf(TAG, "Service not enabled"); return; @@ -488,7 +488,7 @@ final class AutofillManagerServiceImpl Slog.v(TAG, "setViewAutofilled(): no session for " + sessionId + "(" + uid + ")"); return; } - session.setViewAutofilled(id); + session.setViewAutofilledLocked(id); } @GuardedBy("mLock") @@ -792,10 +792,9 @@ final class AutofillManagerServiceImpl * Initializes the last fill selection after an autofill service returned a new * {@link FillResponse}. */ - void setLastResponse(int sessionId, @NonNull FillResponse response) { - synchronized (mLock) { + @GuardedBy("mLock") + void setLastResponseLocked(int sessionId, @NonNull FillResponse response) { mEventHistory = new FillEventHistory(sessionId, response.getClientState()); - } } void setLastAugmentedAutofillResponse(int sessionId) { diff --git a/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java b/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java index aa76200c5270..435a671bcbbb 100644 --- a/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java +++ b/services/autofill/java/com/android/server/autofill/PresentationStatsEventLogger.java @@ -675,7 +675,14 @@ public final class PresentationStatsEventLogger { } else if (autofillIds.contains(autofillId)) { if (sVerbose) { Slog.v(TAG, "Logging autofill for id:" + autofillId); - event.mViewFillSuccessCount++; + } + event.mViewFillSuccessCount++; + autofillIds.remove(autofillId); + event.mAlreadyFilledAutofillIds.add(autofillId); + } else if (event.mAlreadyFilledAutofillIds.contains(autofillId)) { + if (sVerbose) { + Slog.v(TAG, "Successfully filled autofillId:" + autofillId + + " already processed "); } } else { Slog.w(TAG, "Successfully filled autofillId:" + autofillId @@ -850,6 +857,7 @@ public final class PresentationStatsEventLogger { int mViewFilledButUnexpectedCount = 0; ArraySet<AutofillId> mAutofillIdsAttemptedAutofill; + ArraySet<AutofillId> mAlreadyFilledAutofillIds = new ArraySet<>(); PresentationStatsEventInternal() {} } diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index aa67ffed0b2d..ecb0ad8b7d08 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -1677,22 +1677,23 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState final LogMaker requestLog; - // Start a new FillResponse logger for the success case. - mFillResponseEventLogger.startLogForNewResponse(); - mFillResponseEventLogger.maybeSetRequestId(requestId); - mFillResponseEventLogger.maybeSetAppPackageUid(uid); - mFillResponseEventLogger.maybeSetResponseStatus(RESPONSE_STATUS_SUCCESS); - mFillResponseEventLogger.startResponseProcessingTime(); - // Time passed since session was created - final long fillRequestReceivedRelativeTimestamp = - SystemClock.elapsedRealtime() - mLatencyBaseTime; - mPresentationStatsEventLogger.maybeSetFillResponseReceivedTimestampMs( - (int) (fillRequestReceivedRelativeTimestamp)); - mFillResponseEventLogger.maybeSetLatencyFillResponseReceivedMillis( - (int) (fillRequestReceivedRelativeTimestamp)); - mFillResponseEventLogger.maybeSetDetectionPreference(getDetectionPreferenceForLogging()); - synchronized (mLock) { + // Start a new FillResponse logger for the success case. + mFillResponseEventLogger.startLogForNewResponse(); + mFillResponseEventLogger.maybeSetRequestId(requestId); + mFillResponseEventLogger.maybeSetAppPackageUid(uid); + mFillResponseEventLogger.maybeSetResponseStatus(RESPONSE_STATUS_SUCCESS); + mFillResponseEventLogger.startResponseProcessingTime(); + // Time passed since session was created + final long fillRequestReceivedRelativeTimestamp = + SystemClock.elapsedRealtime() - mLatencyBaseTime; + mPresentationStatsEventLogger.maybeSetFillResponseReceivedTimestampMs( + (int) (fillRequestReceivedRelativeTimestamp)); + mFillResponseEventLogger.maybeSetLatencyFillResponseReceivedMillis( + (int) (fillRequestReceivedRelativeTimestamp)); + mFillResponseEventLogger.maybeSetDetectionPreference( + getDetectionPreferenceForLogging()); + if (mDestroyed) { Slog.w(TAG, "Call to Session#onFillRequestSuccess() rejected - session: " + id + " destroyed"); @@ -1744,11 +1745,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState Slog.v(TAG, "Service requested to wait for delayed fill response."); registerDelayedFillBroadcastLocked(); } - } - mService.setLastResponse(id, response); + mService.setLastResponseLocked(id, response); - synchronized (mLock) { if (mLogViewEntered) { mLogViewEntered = false; mService.logViewEntered(id, null); @@ -1821,16 +1820,18 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } int datasetCount = (datasetList == null) ? 0 : datasetList.size(); - mFillResponseEventLogger.maybeSetTotalDatasetsProvided(datasetCount); - // It's possible that this maybe overwritten later on after PCC filtering. - mFillResponseEventLogger.maybeSetAvailableCount(datasetCount); + synchronized (mLock) { + mFillResponseEventLogger.maybeSetTotalDatasetsProvided(datasetCount); + // It's possible that this maybe overwritten later on after PCC filtering. + mFillResponseEventLogger.maybeSetAvailableCount(datasetCount); - // TODO(b/266379948): Ideally wait for PCC request to finish for a while more - // (say 100ms) before proceeding further on. + // TODO(b/266379948): Ideally wait for PCC request to finish for a while more + // (say 100ms) before proceeding further on. - processResponseLockedForPcc(response, response.getClientState(), requestFlags); - mFillResponseEventLogger.maybeSetLatencyResponseProcessingMillis(); - mFillResponseEventLogger.logAndEndEvent(); + processResponseLockedForPcc(response, response.getClientState(), requestFlags); + mFillResponseEventLogger.maybeSetLatencyResponseProcessingMillis(); + mFillResponseEventLogger.logAndEndEvent(); + } } @@ -2381,21 +2382,22 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState @Nullable CharSequence message) { boolean showMessage = !TextUtils.isEmpty(message); - // Start a new FillResponse logger for the failure or timeout case. - mFillResponseEventLogger.startLogForNewResponse(); - mFillResponseEventLogger.maybeSetRequestId(requestId); - mFillResponseEventLogger.maybeSetAppPackageUid(uid); - mFillResponseEventLogger.maybeSetAvailableCount( - AVAILABLE_COUNT_WHEN_FILL_REQUEST_FAILED_OR_TIMEOUT); - mFillResponseEventLogger.maybeSetTotalDatasetsProvided( - AVAILABLE_COUNT_WHEN_FILL_REQUEST_FAILED_OR_TIMEOUT); - mFillResponseEventLogger.maybeSetDetectionPreference(getDetectionPreferenceForLogging()); - final long fillRequestReceivedRelativeTimestamp = - SystemClock.elapsedRealtime() - mLatencyBaseTime; - mFillResponseEventLogger.maybeSetLatencyFillResponseReceivedMillis( - (int)(fillRequestReceivedRelativeTimestamp)); - synchronized (mLock) { + // Start a new FillResponse logger for the failure or timeout case. + mFillResponseEventLogger.startLogForNewResponse(); + mFillResponseEventLogger.maybeSetRequestId(requestId); + mFillResponseEventLogger.maybeSetAppPackageUid(uid); + mFillResponseEventLogger.maybeSetAvailableCount( + AVAILABLE_COUNT_WHEN_FILL_REQUEST_FAILED_OR_TIMEOUT); + mFillResponseEventLogger.maybeSetTotalDatasetsProvided( + AVAILABLE_COUNT_WHEN_FILL_REQUEST_FAILED_OR_TIMEOUT); + mFillResponseEventLogger.maybeSetDetectionPreference( + getDetectionPreferenceForLogging()); + final long fillRequestReceivedRelativeTimestamp = + SystemClock.elapsedRealtime() - mLatencyBaseTime; + mFillResponseEventLogger.maybeSetLatencyFillResponseReceivedMillis( + (int) (fillRequestReceivedRelativeTimestamp)); + unregisterDelayedFillBroadcastLocked(); if (mDestroyed) { Slog.w(TAG, "Call to Session#onFillRequestFailureOrTimeout(req=" + requestId @@ -2635,8 +2637,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState + id + " destroyed"); return; } + mSaveEventLogger.maybeSetLatencySaveRequestMillis(); } - mSaveEventLogger.maybeSetLatencySaveRequestMillis(); mHandler.sendMessage(obtainMessage( AutofillManagerServiceImpl::handleSessionSave, mService, this)); @@ -3215,6 +3217,58 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState return saveInfo == null ? 0 : saveInfo.getFlags(); } + static class SaveInfoStats { + public int saveInfoCount; + public int saveDataTypeCount; + } + + /** + * Get statistic information of save info in current session. Specifically + * 1. how many save info the current session has. + * 2. How many distinct save data types current session has. + * + * @return SaveInfoStats returns the above two number in a SaveInfoStats object + */ + @GuardedBy("mLock") + private SaveInfoStats getSaveInfoStatsLocked() { + SaveInfoStats retSaveInfoStats = new SaveInfoStats(); + retSaveInfoStats.saveInfoCount = -1; + retSaveInfoStats.saveDataTypeCount = -1; + + if (mContexts == null) { + if (sVerbose) { + Slog.v(TAG, "getSaveInfoStatsLocked(): mContexts is null"); + } + } else if (mResponses == null) { + // Happens when the activity / session was finished before the service replied, or + // when the service cannot autofill it (and returned a null response). + if (sVerbose) { + Slog.v(TAG, "getSaveInfoStatsLocked(): mResponses is null"); + } + return retSaveInfoStats; + } else { + int numSaveInfos = 0; + int numSaveDataTypes = 0; + ArraySet<Integer> saveDataTypeSeen = new ArraySet<>(); + final int numResponses = mResponses.size(); + for (int responseNum = 0; responseNum < numResponses; responseNum++) { + final FillResponse response = mResponses.valueAt(responseNum); + if (response != null && response.getSaveInfo() != null) { + numSaveInfos += 1; + int saveDataType = response.getSaveInfo().getType(); + if (!saveDataTypeSeen.contains(saveDataType)) { + saveDataTypeSeen.add(saveDataType); + numSaveDataTypes += 1; + } + } + } + retSaveInfoStats.saveInfoCount = numSaveInfos; + retSaveInfoStats.saveDataTypeCount = numSaveDataTypes; + } + + return retSaveInfoStats; + } + /** * Generates a {@link android.service.autofill.FillEventHistory.Event#TYPE_CONTEXT_COMMITTED} * when necessary. @@ -3227,7 +3281,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mHandler.sendMessage(obtainMessage(Session::handleLogContextCommitted, this, Event.NO_SAVE_UI_REASON_NONE, COMMIT_REASON_UNKNOWN)); - logAllEvents(COMMIT_REASON_UNKNOWN); + synchronized (mLock) { + logAllEventsLocked(COMMIT_REASON_UNKNOWN); + } } /** @@ -3251,6 +3307,11 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState mSessionCommittedEventLogger.maybeSetCommitReason(commitReason); mSessionCommittedEventLogger.maybeSetRequestCount(mRequestCount); + SaveInfoStats saveInfoStats = getSaveInfoStatsLocked(); + mSessionCommittedEventLogger.maybeSetSaveInfoCount(saveInfoStats.saveInfoCount); + mSessionCommittedEventLogger.maybeSetSaveDataTypeCount(saveInfoStats.saveDataTypeCount); + mSessionCommittedEventLogger.maybeSetLastFillResponseHasSaveInfo( + getSaveInfoLocked() != null); mSaveEventLogger.maybeSetSaveUiNotShownReason(NO_SAVE_REASON_NONE); } @@ -4571,7 +4632,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState FillResponse response = viewState.getSecondaryResponse(); if (response != null) { - logPresentationStatsOnViewEntered(response, isCredmanRequested); + logPresentationStatsOnViewEnteredLocked(response, isCredmanRequested); } // If the ViewState is ready to be displayed, onReady() will be called. @@ -4662,7 +4723,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState FillResponse response = viewState.getResponse(); if (response != null) { - logPresentationStatsOnViewEntered(response, isCredmanRequested); + logPresentationStatsOnViewEnteredLocked(response, isCredmanRequested); } if (isSameViewEntered) { @@ -4703,7 +4764,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } @GuardedBy("mLock") - private void logPresentationStatsOnViewEntered(FillResponse response, + private void logPresentationStatsOnViewEnteredLocked(FillResponse response, boolean isCredmanRequested) { mPresentationStatsEventLogger.maybeSetRequestId(response.getRequestId()); mPresentationStatsEventLogger.maybeSetIsCredentialRequest(isCredmanRequested); @@ -5061,7 +5122,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState getUiForShowing().showFillDialog(filledId, response, filterText, mService.getServicePackageName(), mComponentName, serviceIcon, this, - id, mCompatMode, mPresentationStatsEventLogger); + id, mCompatMode, mPresentationStatsEventLogger, mLock); return true; } @@ -5420,7 +5481,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState * Sets the state of views that failed to autofill. */ @GuardedBy("mLock") - void setViewAutofilled(@NonNull AutofillId id) { + void setViewAutofilledLocked(@NonNull AutofillId id) { if (sVerbose) { Slog.v(TAG, "View autofilled: " + id); } @@ -6663,7 +6724,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState } @GuardedBy("mLock") - private void logAllEvents(@AutofillCommitReason int val) { + private void logAllEventsLocked(@AutofillCommitReason int val) { if (sVerbose) { Slog.v(TAG, "logAllEvents(" + id + "): commitReason: " + val); } @@ -6695,7 +6756,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState if (sVerbose) { Slog.v(TAG, "destroyLocked for session: " + id); } - logAllEvents(COMMIT_REASON_SESSION_DESTROYED); + logAllEventsLocked(COMMIT_REASON_SESSION_DESTROYED); if (mDestroyed) { return null; diff --git a/services/autofill/java/com/android/server/autofill/SessionCommittedEventLogger.java b/services/autofill/java/com/android/server/autofill/SessionCommittedEventLogger.java index 1be8548c788f..8f3c8803154d 100644 --- a/services/autofill/java/com/android/server/autofill/SessionCommittedEventLogger.java +++ b/services/autofill/java/com/android/server/autofill/SessionCommittedEventLogger.java @@ -94,6 +94,33 @@ public final class SessionCommittedEventLogger { } /** + * Set how many save infos there are in current session as long as mEventInternal presents. + */ + public void maybeSetSaveInfoCount(int saveInfoCount) { + mEventInternal.ifPresent(event -> { + event.mSaveInfoCount = saveInfoCount; + }); + } + + /** + * Set how many save data types there are in current session as long as mEventInternal presents. + */ + public void maybeSetSaveDataTypeCount(int saveDataTypeCount) { + mEventInternal.ifPresent(event -> { + event.mSaveDataTypeCount = saveDataTypeCount; + }); + } + + /** + * Set whether last fill response in session has save info as long as mEventInternal presents. + */ + public void maybeSetLastFillResponseHasSaveInfo(boolean lastFillResponseHasSaveInfo) { + mEventInternal.ifPresent(event -> { + event.mLastFillResponseHasSaveInfo = lastFillResponseHasSaveInfo; + }); + } + + /** * Log an AUTOFILL_SESSION_COMMITTED event. */ public void logAndEndEvent() { @@ -109,7 +136,10 @@ public final class SessionCommittedEventLogger { + " mRequestCount=" + event.mRequestCount + " mCommitReason=" + event.mCommitReason + " mSessionDurationMillis=" + event.mSessionDurationMillis - + " mServiceUid=" + event.mServiceUid); + + " mServiceUid=" + event.mServiceUid + + " mSaveInfoCount=" + event.mSaveInfoCount + + " mSaveDataTypeCount=" + event.mSaveDataTypeCount + + " mLastFillResponseHasSaveInfo=" + event.mLastFillResponseHasSaveInfo); } FrameworkStatsLog.write( AUTOFILL_SESSION_COMMITTED, @@ -118,7 +148,10 @@ public final class SessionCommittedEventLogger { event.mRequestCount, event.mCommitReason, event.mSessionDurationMillis, - event.mServiceUid); + event.mServiceUid, + event.mSaveInfoCount, + event.mSaveDataTypeCount, + event.mLastFillResponseHasSaveInfo); mEventInternal = Optional.empty(); } @@ -127,6 +160,9 @@ public final class SessionCommittedEventLogger { int mRequestCount = 0; int mCommitReason = COMMIT_REASON_UNKNOWN; long mSessionDurationMillis = 0; + int mSaveInfoCount = -1; + int mSaveDataTypeCount = -1; + boolean mLastFillResponseHasSaveInfo = false; int mServiceUid = -1; SessionCommittedEventInternal() { diff --git a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java index 3b9c54f79e61..8cc666b538ec 100644 --- a/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java +++ b/services/autofill/java/com/android/server/autofill/ui/AutoFillUI.java @@ -425,7 +425,8 @@ public final class AutoFillUI { @Nullable String filterText, @Nullable String servicePackageName, @NonNull ComponentName componentName, @Nullable Drawable serviceIcon, @NonNull AutoFillUiCallback callback, int sessionId, boolean compatMode, - @Nullable PresentationStatsEventLogger mPresentationStatsEventLogger) { + @Nullable PresentationStatsEventLogger presentationStatsEventLogger, + @NonNull Object sessionLock) { if (sVerbose) { Slog.v(TAG, "showFillDialog for " + componentName.toShortString() + ": " + response); @@ -467,9 +468,11 @@ public final class AutoFillUI { @Override public void onDatasetPicked(Dataset dataset) { log(MetricsEvent.TYPE_ACTION); - if (mPresentationStatsEventLogger != null) { - mPresentationStatsEventLogger.maybeSetPositiveCtaButtonClicked( - true); + synchronized (sessionLock) { + if (presentationStatsEventLogger != null) { + presentationStatsEventLogger.maybeSetPositiveCtaButtonClicked( + true); + } } hideFillDialogUiThread(callback); if (mCallback != null) { @@ -482,8 +485,10 @@ public final class AutoFillUI { @Override public void onDismissed() { log(MetricsEvent.TYPE_DISMISS); - if (mPresentationStatsEventLogger != null) { - mPresentationStatsEventLogger.maybeSetDialogDismissed(true); + synchronized (sessionLock) { + if (presentationStatsEventLogger != null) { + presentationStatsEventLogger.maybeSetDialogDismissed(true); + } } hideFillDialogUiThread(callback); callback.requestShowSoftInput(focusedId); @@ -493,9 +498,11 @@ public final class AutoFillUI { @Override public void onCanceled() { log(MetricsEvent.TYPE_CLOSE); - if (mPresentationStatsEventLogger != null) { - mPresentationStatsEventLogger.maybeSetNegativeCtaButtonClicked( - true); + synchronized (sessionLock) { + if (presentationStatsEventLogger != null) { + presentationStatsEventLogger.maybeSetNegativeCtaButtonClicked( + true); + } } hideFillDialogUiThread(callback); callback.requestShowSoftInput(focusedId); diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java index c9cce1568335..0ab6bbc3e0d3 100644 --- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java +++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java @@ -532,7 +532,8 @@ public class CompanionDeviceManagerService extends SystemService { String packageName, int userId) { startObservingDevicePresence_enforcePermission(); - mDevicePresenceProcessor.startObservingDevicePresence(request, packageName, userId); + mDevicePresenceProcessor.startObservingDevicePresence( + request, packageName, userId, /* enforcePermissions */ true); } @Override @@ -541,7 +542,8 @@ public class CompanionDeviceManagerService extends SystemService { String packageName, int userId) { stopObservingDevicePresence_enforcePermission(); - mDevicePresenceProcessor.stopObservingDevicePresence(request, packageName, userId); + mDevicePresenceProcessor.stopObservingDevicePresence( + request, packageName, userId, /* enforcePermissions */ true); } @Override diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java index c892b84ab4d5..3d53deb8d2bb 100644 --- a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java +++ b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java @@ -21,6 +21,7 @@ import static android.companion.CompanionDeviceManager.MESSAGE_REQUEST_CONTEXT_S import android.companion.AssociationInfo; import android.companion.ContextSyncMessage; import android.companion.Flags; +import android.companion.ObservingDevicePresenceRequest; import android.companion.Telecom; import android.companion.datatransfer.PermissionSyncRequest; import android.net.MacAddress; @@ -193,6 +194,43 @@ class CompanionDeviceShellCommand extends ShellCommand { break; } + case "start-observing-device-presence-uuid": { + if (Flags.devicePresence()) { + int userId = getNextIntArgRequired(); + String packageName = getNextArgRequired(); + String uuid = getNextArgRequired(); + if ("null".equals(uuid)) { + out.println("UUID can not be null."); + break; + } + ParcelUuid parcelUuid = ParcelUuid.fromString(uuid); + ObservingDevicePresenceRequest request = new ObservingDevicePresenceRequest + .Builder().setUuid(parcelUuid).build(); + mDevicePresenceProcessor.startObservingDevicePresence( + request, packageName, userId, /* enforcePermissions */ false); + + } + break; + } + + case "stop-observing-device-presence-uuid": { + if (Flags.devicePresence()) { + int userId = getNextIntArgRequired(); + String packageName = getNextArgRequired(); + String uuid = getNextArgRequired(); + if ("null".equals(uuid)) { + out.println("UUID can not be null."); + break; + } + ParcelUuid parcelUuid = ParcelUuid.fromString(uuid); + ObservingDevicePresenceRequest request = new ObservingDevicePresenceRequest + .Builder().setUuid(parcelUuid).build(); + mDevicePresenceProcessor.stopObservingDevicePresence( + request, packageName, userId, /* enforcePermissions */ false); + } + break; + } + case "get-backup-payload": { final int userId = getNextIntArgRequired(); byte[] payload = mBackupRestoreProcessor.getBackupPayload(userId); @@ -515,6 +553,14 @@ class CompanionDeviceShellCommand extends ShellCommand { pw.println(" callback after simulate-device-event-device-locked"); pw.println(" command has been called."); pw.println(" USE FOR DEBUGGING AND/OR TESTING PURPOSES ONLY."); + + pw.println(" start-observing-device-presence-uuid USER_ID PACKAGE_NAME UUID"); + pw.println(" Start observing device presence base on the UUID."); + pw.println(" USE FOR DEBUGGING AND/OR TESTING PURPOSES ONLY."); + + pw.println(" stop-observing-device-presence-uuid USER_ID PACKAGE_NAME UUID"); + pw.println(" Stop observing device presence base on the UUID."); + pw.println(" USE FOR DEBUGGING AND/OR TESTING PURPOSES ONLY."); } pw.println(" remove-inactive-associations"); diff --git a/services/companion/java/com/android/server/companion/devicepresence/DevicePresenceProcessor.java b/services/companion/java/com/android/server/companion/devicepresence/DevicePresenceProcessor.java index af49df69a979..a374d279af0b 100644 --- a/services/companion/java/com/android/server/companion/devicepresence/DevicePresenceProcessor.java +++ b/services/companion/java/com/android/server/companion/devicepresence/DevicePresenceProcessor.java @@ -181,14 +181,16 @@ public class DevicePresenceProcessor implements AssociationStore.OnChangeListene * Process device presence start request. */ public void startObservingDevicePresence(ObservingDevicePresenceRequest request, - String packageName, int userId) { + String packageName, int userId, boolean enforcePermissions) { Slog.i(TAG, "Start observing request=[" + request + "] for userId=[" + userId + "], package=[" + packageName + "]..."); final ParcelUuid requestUuid = request.getUuid(); if (requestUuid != null) { - enforceCallerCanObserveDevicePresenceByUuid(mContext); + if (enforcePermissions) { + enforceCallerCanObserveDevicePresenceByUuid(mContext, packageName, userId); + } // If it's already being observed, then no-op. if (mObservableUuidStore.isUuidBeingObserved(requestUuid, userId, packageName)) { @@ -236,7 +238,7 @@ public class DevicePresenceProcessor implements AssociationStore.OnChangeListene * Process device presence stop request. */ public void stopObservingDevicePresence(ObservingDevicePresenceRequest request, - String packageName, int userId) { + String packageName, int userId, boolean enforcePermissions) { Slog.i(TAG, "Stop observing request=[" + request + "] for userId=[" + userId + "], package=[" + packageName + "]..."); @@ -244,7 +246,9 @@ public class DevicePresenceProcessor implements AssociationStore.OnChangeListene final ParcelUuid requestUuid = request.getUuid(); if (requestUuid != null) { - enforceCallerCanObserveDevicePresenceByUuid(mContext); + if (enforcePermissions) { + enforceCallerCanObserveDevicePresenceByUuid(mContext, packageName, userId); + } if (!mObservableUuidStore.isUuidBeingObserved(requestUuid, userId, packageName)) { Slog.i(TAG, "UUID=[" + requestUuid + "], package=[" + packageName + "], userId=[" @@ -283,7 +287,7 @@ public class DevicePresenceProcessor implements AssociationStore.OnChangeListene * For legacy device presence below Android V. * * @deprecated Use {@link #startObservingDevicePresence(ObservingDevicePresenceRequest, String, - * int)} + * int, boolean)} */ @Deprecated public void startObservingDevicePresence(int userId, String packageName, String deviceAddress) @@ -306,14 +310,14 @@ public class DevicePresenceProcessor implements AssociationStore.OnChangeListene startObservingDevicePresence( new ObservingDevicePresenceRequest.Builder().setAssociationId(association.getId()) - .build(), packageName, userId); + .build(), packageName, userId, /* enforcePermissions */ true); } /** * For legacy device presence below Android V. * * @deprecated Use {@link #stopObservingDevicePresence(ObservingDevicePresenceRequest, String, - * int)} + * int, boolean)} */ @Deprecated public void stopObservingDevicePresence(int userId, String packageName, String deviceAddress) @@ -336,7 +340,7 @@ public class DevicePresenceProcessor implements AssociationStore.OnChangeListene stopObservingDevicePresence( new ObservingDevicePresenceRequest.Builder().setAssociationId(association.getId()) - .build(), packageName, userId); + .build(), packageName, userId, /* enforcePermissions */ true); } /** diff --git a/services/companion/java/com/android/server/companion/utils/PermissionsUtils.java b/services/companion/java/com/android/server/companion/utils/PermissionsUtils.java index 796d2851760f..c927cd04cf64 100644 --- a/services/companion/java/com/android/server/companion/utils/PermissionsUtils.java +++ b/services/companion/java/com/android/server/companion/utils/PermissionsUtils.java @@ -35,6 +35,8 @@ import static android.os.Binder.getCallingUid; import static android.os.Process.SYSTEM_UID; import static android.os.UserHandle.getCallingUserId; +import static com.android.server.companion.utils.RolesUtils.isRoleHolder; + import static java.util.Collections.unmodifiableMap; import android.Manifest; @@ -44,6 +46,7 @@ import android.annotation.UserIdInt; import android.companion.AssociationRequest; import android.companion.CompanionDeviceManager; import android.content.Context; +import android.os.Binder; import android.os.RemoteException; import android.os.ServiceManager; import android.util.ArrayMap; @@ -203,11 +206,9 @@ public final class PermissionsUtils { /** * Require the caller to hold necessary permission to observe device presence by UUID. */ - public static void enforceCallerCanObserveDevicePresenceByUuid(@NonNull Context context) { - if (context.checkCallingPermission(REQUEST_OBSERVE_DEVICE_UUID_PRESENCE) - != PERMISSION_GRANTED - || context.checkCallingPermission(BLUETOOTH_SCAN) != PERMISSION_GRANTED - || context.checkCallingPermission(BLUETOOTH_CONNECT) != PERMISSION_GRANTED) { + public static void enforceCallerCanObserveDevicePresenceByUuid(@NonNull Context context, + String packageName, int userId) { + if (!hasRequirePermissions(context, packageName, userId)) { throw new SecurityException("Caller (uid=" + getCallingUid() + ") does not have " + "permissions to request observing device presence base on the UUID"); } @@ -234,6 +235,17 @@ public final class PermissionsUtils { return sAppOpsService; } + private static boolean hasRequirePermissions( + @NonNull Context context, String packageName, int userId) { + return context.checkCallingPermission( + REQUEST_OBSERVE_DEVICE_UUID_PRESENCE) == PERMISSION_GRANTED + && context.checkCallingPermission(BLUETOOTH_SCAN) == PERMISSION_GRANTED + && context.checkCallingPermission(BLUETOOTH_CONNECT) == PERMISSION_GRANTED + && Boolean.TRUE.equals(Binder.withCleanCallingIdentity( + () -> isRoleHolder(context, userId, packageName, + DEVICE_PROFILE_AUTOMOTIVE_PROJECTION))); + } + // DO NOT USE DIRECTLY! Access via getAppOpsService(). private static IAppOpsService sAppOpsService = null; diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java index 2607ed3193eb..89c888c6dfc8 100644 --- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java +++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java @@ -18,6 +18,7 @@ package com.android.server.contentcapture; import static android.Manifest.permission.MANAGE_CONTENT_CAPTURE; import static android.content.Context.CONTENT_CAPTURE_MANAGER_SERVICE; +import static android.service.contentcapture.ContentCaptureService.ASSIST_CONTENT_ACTIVITY_START_KEY; import static android.service.contentcapture.ContentCaptureService.setClientState; import static android.view.contentcapture.ContentCaptureHelper.toList; import static android.view.contentcapture.ContentCaptureManager.DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_ALLOWLIST_DELAY_MS; @@ -28,6 +29,7 @@ import static android.view.contentcapture.ContentCaptureManager.DEVICE_CONFIG_PR import static android.view.contentcapture.ContentCaptureManager.DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_OPTIONAL_GROUPS_THRESHOLD; import static android.view.contentcapture.ContentCaptureManager.DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_REQUIRED_GROUPS_CONFIG; import static android.view.contentcapture.ContentCaptureManager.DEVICE_CONFIG_PROPERTY_ENABLE_CONTENT_PROTECTION_RECEIVER; +import static android.view.contentcapture.ContentCaptureManager.DEVICE_CONFIG_ENABLE_ACTIVITY_START_ASSIST_CONTENT; import static android.view.contentcapture.ContentCaptureManager.RESULT_CODE_FALSE; import static android.view.contentcapture.ContentCaptureManager.RESULT_CODE_OK; import static android.view.contentcapture.ContentCaptureManager.RESULT_CODE_SECURITY_EXCEPTION; @@ -44,6 +46,7 @@ import static com.android.internal.util.FrameworkStatsLog.CONTENT_CAPTURE_SERVIC import static com.android.internal.util.FrameworkStatsLog.CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__DATA_SHARE_WRITE_FINISHED; import static com.android.internal.util.FrameworkStatsLog.CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__REJECT_DATA_SHARE_REQUEST; import static com.android.internal.util.SyncResultReceiver.bundleFor; +import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_CONTENT; import android.annotation.NonNull; import android.annotation.Nullable; @@ -52,10 +55,12 @@ import android.app.ActivityManagerInternal; import android.app.ActivityThread; import android.app.admin.DevicePolicyCache; import android.app.assist.ActivityId; +import android.app.assist.AssistContent; import android.content.ComponentName; import android.content.ContentCaptureOptions; import android.content.ContentResolver; import android.content.Context; +import android.content.Intent; import android.content.pm.ActivityPresentationInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; @@ -184,6 +189,9 @@ public class ContentCaptureManagerService extends @Nullable private boolean mDisabledByDeviceConfig; + @GuardedBy("mLock") + private boolean activityStartAssistDataEnabled; + // Device-config settings that are cached and passed back to apps @GuardedBy("mLock") int mDevCfgLoggingLevel; @@ -451,6 +459,8 @@ public class ContentCaptureManagerService extends case DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_AUTO_DISCONNECT_TIMEOUT: setFineTuneParamsFromDeviceConfig(); return; + case DEVICE_CONFIG_ENABLE_ACTIVITY_START_ASSIST_CONTENT: + setActivityStartAssistDataEnabled(); default: Slog.i(TAG, "Ignoring change on " + key); } @@ -639,6 +649,15 @@ public class ContentCaptureManagerService extends final String enabled = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_CONTENT_CAPTURE, ContentCaptureManager.DEVICE_CONFIG_PROPERTY_SERVICE_EXPLICITLY_ENABLED); setDisabledByDeviceConfig(enabled); + setActivityStartAssistDataEnabled(); + } + + private void setActivityStartAssistDataEnabled() { + synchronized (mLock) { + this.activityStartAssistDataEnabled = DeviceConfig.getBoolean( + DeviceConfig.NAMESPACE_CONTENT_CAPTURE, + DEVICE_CONFIG_ENABLE_ACTIVITY_START_ASSIST_CONTENT, false); + } } private void setDisabledByDeviceConfig(@Nullable String explicitlyEnabled) { @@ -908,6 +927,9 @@ public class ContentCaptureManagerService extends pw.print(prefix2); pw.print("contentProtectionAutoDisconnectTimeoutMs: "); pw.println(mDevCfgContentProtectionAutoDisconnectTimeoutMs); + pw.print(prefix2); + pw.print("activityStartAssistDataEnabled: "); + pw.println(activityStartAssistDataEnabled); pw.print(prefix); pw.println("Global Options:"); mGlobalContentCaptureOptions.dump(prefix2, pw); @@ -1327,6 +1349,33 @@ public class ContentCaptureManagerService extends } @Override + @SuppressWarnings("GuardedBy") + public boolean sendActivityStartAssistData(@UserIdInt int userId, + @NonNull IBinder activityToken, + @NonNull Intent intentData) { + synchronized (mLock) { + if (!activityStartAssistDataEnabled) { + return false; + } + Intent intent = new Intent(intentData); + intent.setFlags(intent.getFlags() & ~(Intent.FLAG_GRANT_WRITE_URI_PERMISSION + | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)); + Bundle assistContentExtras = new Bundle(); + assistContentExtras.putBoolean(ASSIST_CONTENT_ACTIVITY_START_KEY, true); + AssistContent assistContent = new AssistContent(assistContentExtras); + assistContent.setDefaultIntent(intent); + + final Bundle activityAssistData = new Bundle(); + activityAssistData.putParcelable(ASSIST_KEY_CONTENT, assistContent); + final ContentCapturePerUserService service = peekServiceForUserLocked(userId); + if (service != null) { + return service.sendActivityAssistDataLocked(activityToken, activityAssistData); + } + } + return false; + } + + @Override public boolean sendActivityAssistData(@UserIdInt int userId, @NonNull IBinder activityToken, @NonNull Bundle data) { synchronized (mLock) { diff --git a/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java b/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java index bc35fea2ce32..38bbfc4d9108 100644 --- a/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java +++ b/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java @@ -47,6 +47,7 @@ import android.app.contextualsearch.ContextualSearchManager; import android.app.contextualsearch.ContextualSearchState; import android.app.contextualsearch.IContextualSearchCallback; import android.app.contextualsearch.IContextualSearchManager; +import android.app.contextualsearch.flags.Flags; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -91,6 +92,8 @@ public class ContextualSearchManagerService extends SystemService { private static final String TAG = ContextualSearchManagerService.class.getSimpleName(); private static final int MSG_RESET_TEMPORARY_PACKAGE = 0; private static final int MAX_TEMP_PACKAGE_DURATION_MS = 1_000 * 60 * 2; // 2 minutes + private static final int MSG_INVALIDATE_TOKEN = 1; + private static final int MAX_TOKEN_VALID_DURATION_MS = 1_000 * 60 * 10; // 10 minutes private final Context mContext; private final ActivityTaskManagerInternal mAtmInternal; @@ -145,6 +148,8 @@ public class ContextualSearchManagerService extends SystemService { private Handler mTemporaryHandler; @GuardedBy("this") private String mTemporaryPackage = null; + @GuardedBy("this") + private long mTokenValidDurationMs = MAX_TOKEN_VALID_DURATION_MS; @GuardedBy("mLock") private IContextualSearchCallback mStateCallback; @@ -212,6 +217,29 @@ public class ContextualSearchManagerService extends SystemService { } } + void resetTokenValidDurationMs() { + setTokenValidDurationMs(MAX_TOKEN_VALID_DURATION_MS); + } + + void setTokenValidDurationMs(int durationMs) { + synchronized (this) { + enforceOverridingPermission("setTokenValidDurationMs"); + if (durationMs > MAX_TOKEN_VALID_DURATION_MS) { + throw new IllegalArgumentException( + "Token max duration is " + MAX_TOKEN_VALID_DURATION_MS + " (called with " + + durationMs + ")"); + } + mTokenValidDurationMs = durationMs; + if (DEBUG_USER) Log.d(TAG, "mTokenValidDurationMs set to " + durationMs); + } + } + + private long getTokenValidDurationMs() { + synchronized (this) { + return mTokenValidDurationMs; + } + } + private Intent getResolvedLaunchIntent() { synchronized (this) { // If mTemporaryPackage is not null, use it to get the ContextualSearch intent. @@ -356,15 +384,51 @@ public class ContextualSearchManagerService extends SystemService { } private class ContextualSearchManagerStub extends IContextualSearchManager.Stub { + @GuardedBy("this") + private Handler mTokenHandler; private @Nullable CallbackToken mToken; + private void invalidateToken() { + synchronized (this) { + if (mTokenHandler != null) { + mTokenHandler.removeMessages(MSG_INVALIDATE_TOKEN); + mTokenHandler = null; + } + if (DEBUG_USER) Log.d(TAG, "mToken invalidated."); + mToken = null; + } + } + + private void issueToken() { + synchronized (this) { + mToken = new CallbackToken(); + if (mTokenHandler == null) { + mTokenHandler = new Handler(Looper.getMainLooper(), null, true) { + @Override + public void handleMessage(Message msg) { + if (msg.what == MSG_INVALIDATE_TOKEN) { + invalidateToken(); + } else { + Slog.wtf(TAG, "invalid token handler msg: " + msg); + } + } + }; + } else { + mTokenHandler.removeMessages(MSG_INVALIDATE_TOKEN); + } + mTokenHandler.sendEmptyMessageDelayed( + MSG_INVALIDATE_TOKEN, getTokenValidDurationMs()); + } + } + @Override public void startContextualSearch(int entrypoint) { synchronized (this) { if (DEBUG_USER) Log.d(TAG, "startContextualSearch"); enforcePermission("startContextualSearch"); mAssistDataRequester.cancel(); - mToken = new CallbackToken(); + // Creates a new CallbackToken at mToken and an expiration handler. + issueToken(); // We get the launch intent with the system server's identity because the system // server has READ_FRAME_BUFFER permission to get the screenshot and because only // the system server can invoke non-exported activities. @@ -397,7 +461,18 @@ public class ContextualSearchManagerService extends SystemService { } return; } - mToken = null; + invalidateToken(); + if (Flags.enableTokenRefresh()) { + issueToken(); + Bundle bundle = new Bundle(); + bundle.putParcelable(ContextualSearchManager.EXTRA_TOKEN, mToken); + try { + callback.onResult( + new ContextualSearchState(null, null, bundle)); + } catch (RemoteException e) { + Log.e(TAG, "Error invoking ContextualSearchCallback", e); + } + } synchronized (mLock) { mStateCallback = callback; } diff --git a/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerShellCommand.java b/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerShellCommand.java index 5777e1d154de..66a4e7b65aba 100644 --- a/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerShellCommand.java +++ b/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerShellCommand.java @@ -52,6 +52,19 @@ public class ContextualSearchManagerShellCommand extends ShellCommand { + packageName + " for " + duration + "ms"); break; } + case "token-duration": { + String durationStr = getNextArg(); + if (durationStr == null) { + mService.resetTokenValidDurationMs(); + pw.println("ContextualSearchManagerService token duration reset."); + return 0; + } + final int durationMs = Integer.parseInt(durationStr); + mService.setTokenValidDurationMs(durationMs); + pw.println("ContextualSearchManagerService temporarily set token duration" + + " to " + durationMs + "ms"); + break; + } } } break; @@ -72,6 +85,9 @@ public class ContextualSearchManagerShellCommand extends ShellCommand { pw.println(" Temporarily (for DURATION ms) changes the Contextual Search " + "implementation."); pw.println(" To reset, call without any arguments."); + pw.println(" set token-duration [DURATION]"); + pw.println(" Changes the Contextual Search token duration to DURATION ms."); + pw.println(" To reset, call without any arguments."); pw.println(""); } } diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java index 2c048831ccab..f5a297bfd4f5 100644 --- a/services/core/java/com/android/server/am/ActivityManagerConstants.java +++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java @@ -174,6 +174,12 @@ final class ActivityManagerConstants extends ContentObserver { */ static final String KEY_ENABLE_BATCHING_OOM_ADJ = "enable_batching_oom_adj"; + /** + * How long to wait before scheduling another follow-up oomAdjuster update for time based state. + */ + static final String KEY_FOLLOW_UP_OOMADJ_UPDATE_WAIT_DURATION = + "follow_up_oomadj_update_wait_duration"; + private static final int DEFAULT_MAX_CACHED_PROCESSES = 1024; private static final boolean DEFAULT_PRIORITIZE_ALARM_BROADCASTS = true; private static final long DEFAULT_FGSERVICE_MIN_SHOWN_TIME = 2*1000; @@ -236,7 +242,7 @@ final class ActivityManagerConstants extends ContentObserver { static final long DEFAULT_BACKGROUND_SETTLE_TIME = 60 * 1000; static final long DEFAULT_KILL_BG_RESTRICTED_CACHED_IDLE_SETTLE_TIME_MS = 60 * 1000; - static final boolean DEFAULT_KILL_BG_RESTRICTED_CACHED_IDLE = true; + static final boolean DEFAULT_KILL_BG_RESTRICTED_CACHED_IDLE = false; static final int DEFAULT_MAX_SERVICE_CONNECTIONS_PER_PROCESS = 3000; @@ -254,6 +260,11 @@ final class ActivityManagerConstants extends ContentObserver { private static final boolean DEFAULT_ENABLE_BATCHING_OOM_ADJ = Flags.batchingOomAdj(); /** + * The default value to {@link #KEY_FOLLOW_UP_OOMADJ_UPDATE_WAIT_DURATION}. + */ + private static final long DEFAULT_FOLLOW_UP_OOMADJ_UPDATE_WAIT_DURATION = 1000L; + + /** * Same as {@link TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED} */ private static final int @@ -1149,6 +1160,10 @@ final class ActivityManagerConstants extends ContentObserver { /** @see #KEY_ENABLE_BATCHING_OOM_ADJ */ public boolean ENABLE_BATCHING_OOM_ADJ = DEFAULT_ENABLE_BATCHING_OOM_ADJ; + /** @see #KEY_FOLLOW_UP_OOMADJ_UPDATE_WAIT_DURATION */ + public long FOLLOW_UP_OOMADJ_UPDATE_WAIT_DURATION = + DEFAULT_FOLLOW_UP_OOMADJ_UPDATE_WAIT_DURATION; + /** * Indicates whether PSS profiling in AppProfiler is disabled or not. */ @@ -1359,6 +1374,9 @@ final class ActivityManagerConstants extends ContentObserver { case KEY_PROC_STATE_DEBUG_UIDS: updateProcStateDebugUids(); break; + case KEY_FOLLOW_UP_OOMADJ_UPDATE_WAIT_DURATION: + updateFollowUpOomAdjUpdateWaitDuration(); + break; default: updateFGSPermissionEnforcementFlagsIfNecessary(name); break; @@ -2246,6 +2264,13 @@ final class ActivityManagerConstants extends ContentObserver { DEFAULT_ENABLE_NEW_OOM_ADJ); } + private void updateFollowUpOomAdjUpdateWaitDuration() { + FOLLOW_UP_OOMADJ_UPDATE_WAIT_DURATION = DeviceConfig.getLong( + DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + KEY_FOLLOW_UP_OOMADJ_UPDATE_WAIT_DURATION, + DEFAULT_FOLLOW_UP_OOMADJ_UPDATE_WAIT_DURATION); + } + private void updateFGSPermissionEnforcementFlagsIfNecessary(@NonNull String name) { ForegroundServiceTypePolicy.getDefaultPolicy() .updatePermissionEnforcementFlagIfNecessary(name); @@ -2514,6 +2539,9 @@ final class ActivityManagerConstants extends ContentObserver { pw.print(" ENABLE_WAIT_FOR_FINISH_ATTACH_APPLICATION="); pw.println(mEnableWaitForFinishAttachApplication); + pw.print(" "); pw.print(KEY_FOLLOW_UP_OOMADJ_UPDATE_WAIT_DURATION); + pw.print("="); pw.println(FOLLOW_UP_OOMADJ_UPDATE_WAIT_DURATION); + synchronized (mProcStateDebugUids) { pw.print(" "); pw.print(KEY_PROC_STATE_DEBUG_UIDS); pw.print("="); pw.println(mProcStateDebugUids); diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 44e522f4fa2f..d750065ee96f 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -1663,6 +1663,7 @@ public class ActivityManagerService extends IActivityManager.Stub static final int BIND_APPLICATION_TIMEOUT_HARD_MSG = 83; static final int SERVICE_FGS_TIMEOUT_MSG = 84; static final int SERVICE_FGS_CRASH_TIMEOUT_MSG = 85; + static final int FOLLOW_UP_OOMADJUSTER_UPDATE_MSG = 86; static final int FIRST_BROADCAST_QUEUE_MSG = 200; @@ -2036,6 +2037,9 @@ public class ActivityManagerService extends IActivityManager.Stub case SERVICE_FGS_CRASH_TIMEOUT_MSG: { mServices.onFgsCrashTimeout((ServiceRecord) msg.obj); } break; + case FOLLOW_UP_OOMADJUSTER_UPDATE_MSG: { + handleFollowUpOomAdjusterUpdate(); + } break; } } } @@ -5103,6 +5107,15 @@ public class ActivityManagerService extends IActivityManager.Stub mAnrHelper.appNotResponding(app, TimeoutRecord.forAppStart(anrMessage)); } + private void handleFollowUpOomAdjusterUpdate() { + // Remove any existing duplicate messages on the handler here while no lock is being held. + // If another follow up update is needed, it will be scheduled by OomAdjuster. + mHandler.removeMessages(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG); + synchronized (this) { + mOomAdjuster.updateOomAdjFollowUpTargetsLocked(); + } + } + /** * @return The last part of the string of an intent's action. */ @@ -10224,6 +10237,19 @@ public class ActivityManagerService extends IActivityManager.Stub addStartInfoTimestampInternal(key, timestampNs, userId, callingUid); } + @Override + public void reportStartInfoViewTimestamps(long renderThreadDrawStartTimeNs, + long framePresentedTimeNs) { + int callingUid = Binder.getCallingUid(); + int userId = UserHandle.getUserId(callingUid); + addStartInfoTimestampInternal( + ApplicationStartInfo.START_TIMESTAMP_INITIAL_RENDERTHREAD_FRAME, + renderThreadDrawStartTimeNs, userId, callingUid); + addStartInfoTimestampInternal( + ApplicationStartInfo.START_TIMESTAMP_SURFACEFLINGER_COMPOSITION_COMPLETE, + framePresentedTimeNs, userId, callingUid); + } + private void addStartInfoTimestampInternal(int key, long timestampNs, int userId, int uid) { mProcessList.getAppStartInfoTracker().addTimestampToStart( Settings.getPackageNameForUid(mContext, uid), diff --git a/services/core/java/com/android/server/am/AppStartInfoTracker.java b/services/core/java/com/android/server/am/AppStartInfoTracker.java index 3042b2a50d50..4a7ad31cba3e 100644 --- a/services/core/java/com/android/server/am/AppStartInfoTracker.java +++ b/services/core/java/com/android/server/am/AppStartInfoTracker.java @@ -1195,21 +1195,8 @@ public final class AppStartInfoTracker { // Records are sorted newest to oldest, grab record at index 0. ApplicationStartInfo startInfo = mInfos.get(0); - int startupState = startInfo.getStartupState(); - // If startup state is error then don't accept any further timestamps. - if (startupState == ApplicationStartInfo.STARTUP_STATE_ERROR) { - if (DEBUG) Slog.d(TAG, "Startup state is error, not accepting new timestamps."); - return; - } - - // If startup state is first frame drawn then only accept fully drawn timestamp. - if (startupState == ApplicationStartInfo.STARTUP_STATE_FIRST_FRAME_DRAWN - && key != ApplicationStartInfo.START_TIMESTAMP_FULLY_DRAWN) { - if (DEBUG) { - Slog.d(TAG, "Startup state is first frame drawn and timestamp is not fully " - + "drawn, not accepting new timestamps."); - } + if (!isAddTimestampAllowed(startInfo, key, timestampNs)) { return; } @@ -1222,6 +1209,55 @@ public final class AppStartInfoTracker { } } + private boolean isAddTimestampAllowed(ApplicationStartInfo startInfo, int key, + long timestampNs) { + int startupState = startInfo.getStartupState(); + + // If startup state is error then don't accept any further timestamps. + if (startupState == ApplicationStartInfo.STARTUP_STATE_ERROR) { + if (DEBUG) Slog.d(TAG, "Startup state is error, not accepting new timestamps."); + return false; + } + + Map<Integer, Long> timestamps = startInfo.getStartupTimestamps(); + + if (startupState == ApplicationStartInfo.STARTUP_STATE_FIRST_FRAME_DRAWN) { + switch (key) { + case ApplicationStartInfo.START_TIMESTAMP_FULLY_DRAWN: + // Allowed, continue to confirm it's not already added. + break; + case ApplicationStartInfo.START_TIMESTAMP_INITIAL_RENDERTHREAD_FRAME: + Long firstFrameTimeNs = timestamps + .get(ApplicationStartInfo.START_TIMESTAMP_FIRST_FRAME); + if (firstFrameTimeNs == null) { + // This should never happen. State can't be first frame drawn if first + // frame timestamp was not provided. + return false; + } + + if (timestampNs > firstFrameTimeNs) { + // Initial renderthread frame has to occur before first frame. + return false; + } + + // Allowed, continue to confirm it's not already added. + break; + case ApplicationStartInfo.START_TIMESTAMP_SURFACEFLINGER_COMPOSITION_COMPLETE: + // Allowed, continue to confirm it's not already added. + break; + default: + return false; + } + } + + if (timestamps.get(key) != null) { + // Timestamp should not occur more than once for a given start. + return false; + } + + return true; + } + @GuardedBy("mLock") void dumpLocked(PrintWriter pw, String prefix, SimpleDateFormat sdf) { if (mMonitoringModeEnabled) { diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java index fc81d3e8c1b6..3b73e0690a83 100644 --- a/services/core/java/com/android/server/am/OomAdjuster.java +++ b/services/core/java/com/android/server/am/OomAdjuster.java @@ -49,6 +49,7 @@ import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_BIND_SERVICE; import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_COMPONENT_DISABLED; import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_EXECUTING_SERVICE; import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_FINISH_RECEIVER; +import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_FOLLOW_UP; import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_GET_PROVIDER; import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_NONE; import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_PROCESS_BEGIN; @@ -68,9 +69,7 @@ import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_UNBIND_SERVICE; import static android.content.Context.BIND_TREAT_LIKE_VISIBLE_FOREGROUND_SERVICE; import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_CAMERA; import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION; -import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK; import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONE; -import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_PHONE_CALL; import static android.media.audio.Flags.roForegroundAudioControl; import static android.os.Process.THREAD_GROUP_BACKGROUND; import static android.os.Process.THREAD_GROUP_DEFAULT; @@ -91,6 +90,7 @@ import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PSS; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_UID_OBSERVERS; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_USAGE_STATS; import static com.android.server.am.ActivityManagerService.DISPATCH_OOM_ADJ_OBSERVER_MSG; +import static com.android.server.am.ActivityManagerService.FOLLOW_UP_OOMADJUSTER_UPDATE_MSG; import static com.android.server.am.ActivityManagerService.IDLE_UIDS_MSG; import static com.android.server.am.ActivityManagerService.TAG_BACKUP; import static com.android.server.am.ActivityManagerService.TAG_LRU; @@ -226,6 +226,8 @@ public class OomAdjuster { return AppProtoEnums.OOM_ADJ_REASON_RESTRICTION_CHANGE; case OOM_ADJ_REASON_COMPONENT_DISABLED: return AppProtoEnums.OOM_ADJ_REASON_COMPONENT_DISABLED; + case OOM_ADJ_REASON_FOLLOW_UP: + return AppProtoEnums.OOM_ADJ_REASON_FOLLOW_UP; default: return AppProtoEnums.OOM_ADJ_REASON_UNKNOWN_TO_PROTO; } @@ -280,6 +282,8 @@ public class OomAdjuster { return OOM_ADJ_REASON_METHOD + "_restrictionChange"; case OOM_ADJ_REASON_COMPONENT_DISABLED: return OOM_ADJ_REASON_METHOD + "_componentDisabled"; + case OOM_ADJ_REASON_FOLLOW_UP: + return OOM_ADJ_REASON_METHOD + "_followUp"; default: return "_unknown"; } @@ -370,6 +374,7 @@ public class OomAdjuster { protected final int[] mTmpSchedGroup = new int[1]; final ActivityManagerService mService; + final Injector mInjector; final ProcessList mProcessList; final ActivityManagerGlobalLock mProcLock; @@ -420,12 +425,33 @@ public class OomAdjuster { @GuardedBy("mService") protected int mProcessStateCurTop = PROCESS_STATE_TOP; - /** Overrideable by a test */ + @GuardedBy("mService") + private final ArraySet<ProcessRecord> mFollowUpUpdateSet = new ArraySet<>(); + + private static final long NO_FOLLOW_UP_TIME = Long.MAX_VALUE; + @GuardedBy("mService") + private long mNextFollowUpUpdateUptimeMs = NO_FOLLOW_UP_TIME; + @VisibleForTesting - protected boolean isChangeEnabled(@CachedCompatChangeId int cachedCompatChangeId, + public static class Injector { + boolean isChangeEnabled(@CachedCompatChangeId int cachedCompatChangeId, + ApplicationInfo app, boolean defaultValue) { + return PlatformCompatCache.getInstance() + .isChangeEnabled(cachedCompatChangeId, app, defaultValue); + } + + long getUptimeMillis() { + return SystemClock.uptimeMillis(); + } + + long getElapsedRealtimeMillis() { + return SystemClock.elapsedRealtime(); + } + } + + boolean isChangeEnabled(@CachedCompatChangeId int cachedCompatChangeId, ApplicationInfo app, boolean defaultValue) { - return PlatformCompatCache.getInstance() - .isChangeEnabled(cachedCompatChangeId, app, defaultValue); + return mInjector.isChangeEnabled(cachedCompatChangeId, app, defaultValue); } OomAdjuster(ActivityManagerService service, ProcessList processList, ActiveUids activeUids) { @@ -443,7 +469,18 @@ public class OomAdjuster { OomAdjuster(ActivityManagerService service, ProcessList processList, ActiveUids activeUids, ServiceThread adjusterThread) { + this(service, processList, activeUids, adjusterThread, new Injector()); + } + + OomAdjuster(ActivityManagerService service, ProcessList processList, ActiveUids activeUids, + Injector injector) { + this(service, processList, activeUids, createAdjusterThread(), injector); + } + + OomAdjuster(ActivityManagerService service, ProcessList processList, ActiveUids activeUids, + ServiceThread adjusterThread, Injector injector) { mService = service; + mInjector = injector; mProcessList = processList; mProcLock = service.mProcLock; mActiveUids = activeUids; @@ -631,8 +668,8 @@ public class OomAdjuster { // processes, its adj could be still unknown as of now, assign one. processes.add(app); assignCachedAdjIfNecessary(processes); - applyOomAdjLSP(app, false, SystemClock.uptimeMillis(), - SystemClock.elapsedRealtime(), oomAdjReason); + applyOomAdjLSP(app, false, mInjector.getUptimeMillis(), + mInjector.getElapsedRealtimeMillis(), oomAdjReason); } mTmpProcessList.clear(); mService.clearPendingTopAppLocked(); @@ -849,6 +886,40 @@ public class OomAdjuster { } @GuardedBy("mService") + void updateOomAdjFollowUpTargetsLocked() { + final long now = mInjector.getUptimeMillis(); + long nextFollowUpUptimeMs = Long.MAX_VALUE; + mNextFollowUpUpdateUptimeMs = NO_FOLLOW_UP_TIME; + for (int i = mFollowUpUpdateSet.size() - 1; i >= 0; i--) { + final ProcessRecord proc = mFollowUpUpdateSet.valueAtUnchecked(i); + final long followUpUptimeMs = proc.mState.getFollowupUpdateUptimeMs(); + + if (proc.isKilled()) { + // Process is dead, just remove from follow up set. + mFollowUpUpdateSet.removeAt(i); + } else if (followUpUptimeMs <= now) { + // Add processes that need a follow up update. + mPendingProcessSet.add(proc); + proc.mState.setFollowupUpdateUptimeMs(NO_FOLLOW_UP_TIME); + mFollowUpUpdateSet.removeAt(i); + } else if (followUpUptimeMs < nextFollowUpUptimeMs) { + // Figure out when to schedule the next follow up update. + nextFollowUpUptimeMs = followUpUptimeMs; + } else if (followUpUptimeMs == NO_FOLLOW_UP_TIME) { + // The follow up is no longer needed for this process. + mFollowUpUpdateSet.removeAt(i); + } + } + + if (nextFollowUpUptimeMs != Long.MAX_VALUE) { + // There is still at least one process that needs a follow up. + scheduleFollowUpOomAdjusterUpdateLocked(nextFollowUpUptimeMs, now); + } + + updateOomAdjPendingTargetsLocked(OOM_ADJ_REASON_FOLLOW_UP); + } + + @GuardedBy("mService") protected void performUpdateOomAdjPendingTargetsLocked(@OomAdjReason int oomAdjReason) { final ProcessRecord topApp = mService.getTopApp(); @@ -899,8 +970,8 @@ public class OomAdjuster { if (startProfiling) { Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, oomAdjReasonToString(oomAdjReason)); } - final long now = SystemClock.uptimeMillis(); - final long nowElapsed = SystemClock.elapsedRealtime(); + final long now = mInjector.getUptimeMillis(); + final long nowElapsed = mInjector.getElapsedRealtimeMillis(); final long oldTime = now - mConstants.mMaxEmptyTimeMillis; final int numProc = activeProcesses.size(); @@ -1026,7 +1097,7 @@ public class OomAdjuster { updateUidsLSP(activeUids, nowElapsed); synchronized (mService.mProcessStats.mLock) { - final long nowUptime = SystemClock.uptimeMillis(); + final long nowUptime = mInjector.getUptimeMillis(); if (mService.mProcessStats.shouldWriteNowLocked(nowUptime)) { mService.mHandler.post(new ActivityManagerService.ProcStatsRunnable(mService, mService.mProcessStats)); @@ -1037,7 +1108,7 @@ public class OomAdjuster { } if (DEBUG_OOM_ADJ) { - final long duration = SystemClock.uptimeMillis() - now; + final long duration = mInjector.getUptimeMillis() - now; if (false) { Slog.d(TAG_OOM_ADJ, "Did OOM ADJ in " + duration + "ms", new RuntimeException("here").fillInStackTrace()); @@ -1051,7 +1122,7 @@ public class OomAdjuster { protected void assignCachedAdjIfNecessary(ArrayList<ProcessRecord> lruList) { final int numLru = lruList.size(); if (mConstants.USE_TIERED_CACHED_ADJ) { - final long now = SystemClock.uptimeMillis(); + final long now = mInjector.getUptimeMillis(); for (int i = numLru - 1; i >= 0; i--) { ProcessRecord app = lruList.get(i); final ProcessStateRecord state = app.mState; @@ -1734,6 +1805,10 @@ public class OomAdjuster { int prevProcState = getInitialProcState(app); int prevCapability = getInitialCapability(app); + // Remove any follow up update this process might have. It will be rescheduled if still + // needed. + app.mState.setFollowupUpdateUptimeMs(NO_FOLLOW_UP_TIME); + if (app.getThread() == null) { state.setAdjSeq(mAdjSeq); state.setCurrentSchedulingGroup(SCHED_GROUP_BACKGROUND); @@ -2001,6 +2076,8 @@ public class OomAdjuster { if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to recent fg: " + app); } + maybeSetProcessFollowUpUpdateLocked(app, + state.getLastTopTime() + mConstants.TOP_TO_FGS_GRACE_DURATION, now); } // If the app was recently in the foreground and has expedited jobs running, @@ -2021,6 +2098,9 @@ public class OomAdjuster { if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to recent fg for EJ: " + app); } + maybeSetProcessFollowUpUpdateLocked(app, + state.getLastTopTime() + mConstants.TOP_TO_ALMOST_PERCEPTIBLE_GRACE_DURATION, + now); } if (adj > PERCEPTIBLE_APP_ADJ @@ -2086,7 +2166,7 @@ public class OomAdjuster { // app to be demoted to cached. if (procState >= PROCESS_STATE_LAST_ACTIVITY && state.getSetProcState() == PROCESS_STATE_LAST_ACTIVITY - && (state.getLastStateTime() + mConstants.MAX_PREVIOUS_TIME) < now) { + && (state.getLastStateTime() + mConstants.MAX_PREVIOUS_TIME) <= now) { procState = PROCESS_STATE_LAST_ACTIVITY; schedGroup = SCHED_GROUP_BACKGROUND; state.setAdjType("previous-expired"); @@ -2110,6 +2190,14 @@ public class OomAdjuster { reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise procstate to prev: " + app); } } + final long lastStateTime; + if (state.getSetProcState() == PROCESS_STATE_LAST_ACTIVITY) { + lastStateTime = state.getLastStateTime(); + } else { + lastStateTime = now; + } + maybeSetProcessFollowUpUpdateLocked(app, + lastStateTime + mConstants.MAX_PREVIOUS_TIME, now); } } @@ -2198,6 +2286,8 @@ public class OomAdjuster { reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise adj to started service: " + app); } + maybeSetProcessFollowUpUpdateLocked(app, + s.lastActivity + mConstants.MAX_SERVICE_INACTIVITY, now); } } // If we have let the service slide into the background @@ -2217,14 +2307,8 @@ public class OomAdjuster { != 0 ? PROCESS_CAPABILITY_FOREGROUND_LOCATION : 0; if (roForegroundAudioControl()) { // flag check - // TODO revisit restriction of FOREGROUND_AUDIO_CONTROL when it can be - // limited to specific FGS types - //final int fgsAudioType = FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK - // | FOREGROUND_SERVICE_TYPE_CAMERA - // | FOREGROUND_SERVICE_TYPE_MICROPHONE - // | FOREGROUND_SERVICE_TYPE_PHONE_CALL; - //capabilityFromFGS |= (psr.getForegroundServiceTypes() & fgsAudioType) != 0 - // ? PROCESS_CAPABILITY_FOREGROUND_AUDIO_CONTROL : 0; + // TODO(b/335373208) - revisit restriction of FOREGROUND_AUDIO_CONTROL + // when it can be limited to specific FGS types capabilityFromFGS |= PROCESS_CAPABILITY_FOREGROUND_AUDIO_CONTROL; } @@ -2352,6 +2436,8 @@ public class OomAdjuster { reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise adj to recent provider: " + app); } + maybeSetProcessFollowUpUpdateLocked(app, + ppr.getLastProviderTime() + mConstants.CONTENT_PROVIDER_RETAIN_TIME, now); } if (procState > PROCESS_STATE_LAST_ACTIVITY) { procState = PROCESS_STATE_LAST_ACTIVITY; @@ -2360,6 +2446,8 @@ public class OomAdjuster { reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise procstate to recent provider: " + app); } + maybeSetProcessFollowUpUpdateLocked(app, + ppr.getLastProviderTime() + mConstants.CONTENT_PROVIDER_RETAIN_TIME, now); } } @@ -3672,7 +3760,7 @@ public class OomAdjuster { if (N <= 0) { return; } - final long nowElapsed = SystemClock.elapsedRealtime(); + final long nowElapsed = mInjector.getElapsedRealtimeMillis(); final long maxBgTime = nowElapsed - mConstants.BACKGROUND_SETTLE_TIME; long nextTime = 0; if (mService.mLocalPowerManager != null) { @@ -3907,7 +3995,7 @@ public class OomAdjuster { } // Take a dry run of the computeServiceHostOomAdjLSP, this would't be expensive // since it's only evaluating one service connection. - return computeServiceHostOomAdjLSP(cr, app, client, SystemClock.uptimeMillis(), + return computeServiceHostOomAdjLSP(cr, app, client, mInjector.getUptimeMillis(), mService.getTopApp(), false, false, false, OOM_ADJ_REASON_NONE, CACHED_APP_MIN_ADJ, false, true /* dryRun */); } @@ -3942,7 +4030,7 @@ public class OomAdjuster { // The provider host process has better states than the client, so no change. return false; } - return computeProviderHostOomAdjLSP(null, app, client, SystemClock.uptimeMillis(), + return computeProviderHostOomAdjLSP(null, app, client, mInjector.getUptimeMillis(), mService.getTopApp(), false, false, false, OOM_ADJ_REASON_NONE, CACHED_APP_MIN_ADJ, false, true /* dryRun */); } @@ -3970,4 +4058,43 @@ public class OomAdjuster { } return false; } + + @GuardedBy("mService") + private void maybeSetProcessFollowUpUpdateLocked(ProcessRecord proc, + long updateUptimeMs, long now) { + if (!Flags.followUpOomadjUpdates()) { + return; + } + if (updateUptimeMs <= now) { + // Time sensitive period has already passed. No need to schedule a follow up. + return; + } + + mFollowUpUpdateSet.add(proc); + proc.mState.setFollowupUpdateUptimeMs(updateUptimeMs); + + scheduleFollowUpOomAdjusterUpdateLocked(updateUptimeMs, now); + } + + + @GuardedBy("mService") + private void scheduleFollowUpOomAdjusterUpdateLocked(long updateUptimeMs, + long now) { + if (updateUptimeMs + mConstants.FOLLOW_UP_OOMADJ_UPDATE_WAIT_DURATION + >= mNextFollowUpUpdateUptimeMs) { + // Update time is too close or later than the next follow up update. + return; + } + if (updateUptimeMs < now + mConstants.FOLLOW_UP_OOMADJ_UPDATE_WAIT_DURATION) { + // Use a minimum delay for the follow up to possibly batch multiple process + // evaluations and avoid rapid updates. + updateUptimeMs = now + mConstants.FOLLOW_UP_OOMADJ_UPDATE_WAIT_DURATION; + } + + // Schedulate a follow up update. Don't bother deleting existing handler messages, they + // will be cleared during the message while no locks are being held. + mNextFollowUpUpdateUptimeMs = updateUptimeMs; + mService.mHandler.sendEmptyMessageAtTime(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG, + mNextFollowUpUpdateUptimeMs); + } } diff --git a/services/core/java/com/android/server/am/OomAdjusterModernImpl.java b/services/core/java/com/android/server/am/OomAdjusterModernImpl.java index a67af89ad39d..21842db590b0 100644 --- a/services/core/java/com/android/server/am/OomAdjusterModernImpl.java +++ b/services/core/java/com/android/server/am/OomAdjusterModernImpl.java @@ -68,7 +68,6 @@ import android.app.ActivityManager; import android.app.ActivityManagerInternal.OomAdjReason; import android.content.pm.ServiceInfo; import android.os.IBinder; -import android.os.SystemClock; import android.os.Trace; import android.util.ArrayMap; import android.util.ArraySet; @@ -764,6 +763,11 @@ public class OomAdjusterModernImpl extends OomAdjuster { super(service, processList, activeUids, adjusterThread); } + OomAdjusterModernImpl(ActivityManagerService service, ProcessList processList, + ActiveUids activeUids, Injector injector) { + super(service, processList, activeUids, injector); + } + private final ProcessRecordNodes mProcessRecordProcStateNodes = new ProcessRecordNodes( ProcessRecordNode.NODE_TYPE_PROC_STATE, PROC_STATE_SLOTS.length); private final ProcessRecordNodes mProcessRecordAdjNodes = new ProcessRecordNodes( @@ -924,8 +928,8 @@ public class OomAdjusterModernImpl extends OomAdjuster { @GuardedBy({"mService", "mProcLock"}) private void fullUpdateLSP(@OomAdjReason int oomAdjReason) { final ProcessRecord topApp = mService.getTopApp(); - final long now = SystemClock.uptimeMillis(); - final long nowElapsed = SystemClock.elapsedRealtime(); + final long now = mInjector.getUptimeMillis(); + final long nowElapsed = mInjector.getElapsedRealtimeMillis(); final long oldTime = now - mConstants.mMaxEmptyTimeMillis; mAdjSeq++; @@ -1015,8 +1019,8 @@ public class OomAdjusterModernImpl extends OomAdjuster { @GuardedBy({"mService", "mProcLock"}) private void partialUpdateLSP(@OomAdjReason int oomAdjReason, ArraySet<ProcessRecord> targets) { final ProcessRecord topApp = mService.getTopApp(); - final long now = SystemClock.uptimeMillis(); - final long nowElapsed = SystemClock.elapsedRealtime(); + final long now = mInjector.getUptimeMillis(); + final long nowElapsed = mInjector.getElapsedRealtimeMillis(); final long oldTime = now - mConstants.mMaxEmptyTimeMillis; ActiveUids activeUids = mTmpUidRecords; diff --git a/services/core/java/com/android/server/am/ProcessStateRecord.java b/services/core/java/com/android/server/am/ProcessStateRecord.java index 8de748e37b5e..7c64298a6053 100644 --- a/services/core/java/com/android/server/am/ProcessStateRecord.java +++ b/services/core/java/com/android/server/am/ProcessStateRecord.java @@ -449,6 +449,9 @@ final class ProcessStateRecord { @GuardedBy("mService") private boolean mScheduleLikeTopApp = false; + @GuardedBy("mService") + private long mFollowupUpdateUptimeMs = Long.MAX_VALUE; + ProcessStateRecord(ProcessRecord app) { mApp = app; mService = app.mService; @@ -1164,6 +1167,16 @@ final class ProcessStateRecord { mScheduleLikeTopApp = scheduleLikeTopApp; } + @GuardedBy("mService") + long getFollowupUpdateUptimeMs() { + return mFollowupUpdateUptimeMs; + } + + @GuardedBy("mService") + void setFollowupUpdateUptimeMs(long updateUptimeMs) { + mFollowupUpdateUptimeMs = updateUptimeMs; + } + @GuardedBy(anyOf = {"mService", "mProcLock"}) public String makeAdjReason() { if (mAdjSource != null || mAdjTarget != null) { diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java index 3df56877cbd6..cbea7aa9a92c 100644 --- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java +++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java @@ -142,6 +142,7 @@ public class SettingsToPropertiesMapper { "arc_next", "art_mainline", "art_performance", + "attack_tools", "avic", "biometrics", "biometrics_framework", diff --git a/services/core/java/com/android/server/am/flags.aconfig b/services/core/java/com/android/server/am/flags.aconfig index 2abfad90ee0b..bb52857a99c4 100644 --- a/services/core/java/com/android/server/am/flags.aconfig +++ b/services/core/java/com/android/server/am/flags.aconfig @@ -142,3 +142,13 @@ flag { bug: "244232958" is_fixed_read_only: true } + +flag { + name: "follow_up_oomadj_updates" + namespace: "backstage_power" + description: "Schedule follow up OomAdjuster updates for time sensitive states." + bug: "333450932" + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/services/core/java/com/android/server/camera/CameraServiceProxy.java b/services/core/java/com/android/server/camera/CameraServiceProxy.java index 390ee96a3417..17835b2d085b 100644 --- a/services/core/java/com/android/server/camera/CameraServiceProxy.java +++ b/services/core/java/com/android/server/camera/CameraServiceProxy.java @@ -18,6 +18,10 @@ package com.android.server.camera; import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE; import static android.os.Build.VERSION_CODES.M; +import static com.android.internal.util.FrameworkStatsLog.CAMERA_FEATURE_COMBINATION_QUERY_EVENT__STATUS_CODE__OK; +import static com.android.internal.util.FrameworkStatsLog.CAMERA_FEATURE_COMBINATION_QUERY_EVENT__STATUS_CODE__ERROR_ILLEGAL_ARGUMENT; +import static com.android.internal.util.FrameworkStatsLog.CAMERA_FEATURE_COMBINATION_QUERY_EVENT__STATUS_CODE__ERROR_INVALID_OPERATION; + import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -40,6 +44,7 @@ import android.content.res.Configuration; import android.graphics.ImageFormat; import android.graphics.Rect; import android.hardware.CameraExtensionSessionStats; +import android.hardware.CameraFeatureCombinationStats; import android.hardware.CameraSessionStats; import android.hardware.CameraStreamStats; import android.hardware.ICameraService; @@ -217,7 +222,7 @@ public class CameraServiceProxy extends SystemService // Map of currently active camera IDs private final ArrayMap<String, CameraUsageEvent> mActiveCameraUsage = new ArrayMap<>(); - private final List<CameraUsageEvent> mCameraUsageHistory = new ArrayList<>(); + private final List<CameraEvent> mCameraEventHistory = new ArrayList<CameraEvent>(); private static final String NFC_NOTIFICATION_PROP = "ro.camera.notify_nfc"; private static final IBinder nfcInterfaceToken = new Binder(); @@ -228,9 +233,16 @@ public class CameraServiceProxy extends SystemService /*corePoolSize*/ 1); /** + * Interface to track camera analytics + */ + private interface CameraEvent { + void logSelf(); + } + + /** * Structure to track camera usage */ - private static class CameraUsageEvent { + private static class CameraUsageEvent implements CameraEvent { public final String mCameraId; public final int mCameraFacing; public final String mClientName; @@ -311,6 +323,214 @@ public class CameraServiceProxy extends SystemService public long getDuration() { return mCompleted ? mDurationOrStartTimeMs : 0; } + + public void logSelf() { + int facing = FrameworkStatsLog.CAMERA_ACTION_EVENT__FACING__UNKNOWN; + switch(mCameraFacing) { + case CameraSessionStats.CAMERA_FACING_BACK: + facing = FrameworkStatsLog.CAMERA_ACTION_EVENT__FACING__BACK; + break; + case CameraSessionStats.CAMERA_FACING_FRONT: + facing = FrameworkStatsLog.CAMERA_ACTION_EVENT__FACING__FRONT; + break; + case CameraSessionStats.CAMERA_FACING_EXTERNAL: + facing = FrameworkStatsLog.CAMERA_ACTION_EVENT__FACING__EXTERNAL; + break; + default: + Slog.w(TAG, "Unknown camera facing: " + mCameraFacing); + } + + int extensionType = FrameworkStatsLog.CAMERA_ACTION_EVENT__EXT_TYPE__EXTENSION_NONE; + boolean extensionIsAdvanced = false; + int extensionCaptureFormat = ImageFormat.UNKNOWN; + if (mExtSessionStats != null) { + switch (mExtSessionStats.type) { + case CameraExtensionSessionStats.Type.EXTENSION_AUTOMATIC: + extensionType = FrameworkStatsLog + .CAMERA_ACTION_EVENT__EXT_TYPE__EXTENSION_AUTOMATIC; + break; + case CameraExtensionSessionStats.Type.EXTENSION_FACE_RETOUCH: + extensionType = FrameworkStatsLog + .CAMERA_ACTION_EVENT__EXT_TYPE__EXTENSION_FACE_RETOUCH; + break; + case CameraExtensionSessionStats.Type.EXTENSION_BOKEH: + extensionType = + FrameworkStatsLog.CAMERA_ACTION_EVENT__EXT_TYPE__EXTENSION_BOKEH; + break; + case CameraExtensionSessionStats.Type.EXTENSION_HDR: + extensionType = + FrameworkStatsLog.CAMERA_ACTION_EVENT__EXT_TYPE__EXTENSION_HDR; + break; + case CameraExtensionSessionStats.Type.EXTENSION_NIGHT: + extensionType = + FrameworkStatsLog.CAMERA_ACTION_EVENT__EXT_TYPE__EXTENSION_NIGHT; + break; + default: + Slog.w(TAG, "Unknown extension type: " + mExtSessionStats.type); + } + extensionIsAdvanced = mExtSessionStats.isAdvanced; + if (Flags.analytics24q3()) { + extensionCaptureFormat = mExtSessionStats.captureFormat; + } + } + + int streamCount = 0; + if (mStreamStats != null) { + streamCount = mStreamStats.size(); + } + if (CameraServiceProxy.DEBUG) { + String ultrawideDebug = Flags.logUltrawideUsage() + ? ", wideAngleUsage " + mUsedUltraWide + : ""; + String zoomOverrideDebug = Flags.logZoomOverrideUsage() + ? ", zoomOverrideUsage " + mUsedZoomOverride + : ""; + String mostRequestedFpsRangeDebug = Flags.analytics24q3() + ? ", mostRequestedFpsRange " + mMostRequestedFpsRange + : ""; + String extensionCaptureFormatDebug = Flags.analytics24q3() + ? " extensionCaptureFormat " + mExtSessionStats.captureFormat + : ""; + + Slog.v(TAG, "CAMERA_ACTION_EVENT: action " + mAction + + " clientName " + mClientName + + ", duration " + getDuration() + + ", APILevel " + mAPILevel + + ", cameraId " + mCameraId + + ", facing " + facing + + ", isNdk " + mIsNdk + + ", latencyMs " + mLatencyMs + + ", operatingMode " + mOperatingMode + + ", internalReconfigure " + mInternalReconfigure + + ", requestCount " + mRequestCount + + ", resultErrorCount " + mResultErrorCount + + ", deviceError " + mDeviceError + + ", streamCount is " + streamCount + + ", userTag is " + mUserTag + + ", videoStabilizationMode " + mVideoStabilizationMode + + ultrawideDebug + + zoomOverrideDebug + + mostRequestedFpsRangeDebug + + ", logId " + mLogId + + ", sessionIndex " + mSessionIndex + + ", mExtSessionStats {type " + extensionType + + " isAdvanced " + extensionIsAdvanced + + extensionCaptureFormatDebug + "}"); + } + + // Convert from CameraStreamStats to CameraStreamProto + CameraStreamProto[] streamProtos = new CameraStreamProto[MAX_STREAM_STATISTICS]; + for (int i = 0; i < MAX_STREAM_STATISTICS; i++) { + streamProtos[i] = new CameraStreamProto(); + if (i < streamCount) { + CameraStreamStats streamStats = mStreamStats.get(i); + streamProtos[i].width = streamStats.getWidth(); + streamProtos[i].height = streamStats.getHeight(); + streamProtos[i].format = streamStats.getFormat(); + streamProtos[i].dataSpace = streamStats.getDataSpace(); + streamProtos[i].usage = streamStats.getUsage(); + streamProtos[i].requestCount = streamStats.getRequestCount(); + streamProtos[i].errorCount = streamStats.getErrorCount(); + streamProtos[i].firstCaptureLatencyMillis = streamStats.getStartLatencyMs(); + streamProtos[i].maxHalBuffers = streamStats.getMaxHalBuffers(); + streamProtos[i].maxAppBuffers = streamStats.getMaxAppBuffers(); + streamProtos[i].histogramType = streamStats.getHistogramType(); + streamProtos[i].histogramBins = streamStats.getHistogramBins(); + streamProtos[i].histogramCounts = streamStats.getHistogramCounts(); + streamProtos[i].dynamicRangeProfile = streamStats.getDynamicRangeProfile(); + streamProtos[i].streamUseCase = streamStats.getStreamUseCase(); + streamProtos[i].colorSpace = streamStats.getColorSpace(); + + if (CameraServiceProxy.DEBUG) { + String histogramTypeName = + cameraHistogramTypeToString(streamProtos[i].histogramType); + Slog.v(TAG, "Stream " + i + ": width " + streamProtos[i].width + + ", height " + streamProtos[i].height + + ", format " + streamProtos[i].format + + ", maxPreviewFps " + streamStats.getMaxPreviewFps() + + ", dataSpace " + streamProtos[i].dataSpace + + ", usage " + streamProtos[i].usage + + ", requestCount " + streamProtos[i].requestCount + + ", errorCount " + streamProtos[i].errorCount + + ", firstCaptureLatencyMillis " + + streamProtos[i].firstCaptureLatencyMillis + + ", maxHalBuffers " + streamProtos[i].maxHalBuffers + + ", maxAppBuffers " + streamProtos[i].maxAppBuffers + + ", histogramType " + histogramTypeName + + ", histogramBins " + + Arrays.toString(streamProtos[i].histogramBins) + + ", histogramCounts " + + Arrays.toString(streamProtos[i].histogramCounts) + + ", dynamicRangeProfile " + streamProtos[i].dynamicRangeProfile + + ", streamUseCase " + streamProtos[i].streamUseCase + + ", colorSpace " + streamProtos[i].colorSpace); + } + } + } + FrameworkStatsLog.write(FrameworkStatsLog.CAMERA_ACTION_EVENT, getDuration(), + mAPILevel, mClientName, facing, mCameraId, mAction, mIsNdk, + mLatencyMs, mOperatingMode, mInternalReconfigure, + mRequestCount, mResultErrorCount, mDeviceError, + streamCount, MessageNano.toByteArray(streamProtos[0]), + MessageNano.toByteArray(streamProtos[1]), + MessageNano.toByteArray(streamProtos[2]), + MessageNano.toByteArray(streamProtos[3]), + MessageNano.toByteArray(streamProtos[4]), + mUserTag, mVideoStabilizationMode, + mLogId, mSessionIndex, + extensionType, extensionIsAdvanced, mUsedUltraWide, + mUsedZoomOverride, + mMostRequestedFpsRange.getLower(), mMostRequestedFpsRange.getUpper(), + extensionCaptureFormat); + + } + } + /** + * Structure to track feature combination query + */ + private static class CameraFeatureCombinationQueryEvent implements CameraEvent { + private CameraFeatureCombinationStats mFeatureCombinationStats; + + CameraFeatureCombinationQueryEvent(CameraFeatureCombinationStats featureCombinationStats) { + mFeatureCombinationStats = featureCombinationStats; + } + + public void logSelf() { + int statusCode = -1; + switch (mFeatureCombinationStats.mStatus) { + case 0: + statusCode = CAMERA_FEATURE_COMBINATION_QUERY_EVENT__STATUS_CODE__OK; + break; + case ICameraService.ERROR_ILLEGAL_ARGUMENT: + statusCode = CAMERA_FEATURE_COMBINATION_QUERY_EVENT__STATUS_CODE__ERROR_ILLEGAL_ARGUMENT; + break; + case ICameraService.ERROR_INVALID_OPERATION: + statusCode = CAMERA_FEATURE_COMBINATION_QUERY_EVENT__STATUS_CODE__ERROR_INVALID_OPERATION; + break; + default: + break; + } + if (statusCode == -1) { + Slog.w(TAG, "Unknown feature combination query status code: " + + mFeatureCombinationStats.mStatus); + return; + } + + if (CameraServiceProxy.DEBUG) { + Slog.v(TAG, "CAMERA_FEATURE_COMBINATION_QUERY_EVENT: uid " + + mFeatureCombinationStats.mUid + + ", cameraId " + mFeatureCombinationStats.mCameraId + + ", queryType " + mFeatureCombinationStats.mQueryType + + ", featureCombination " + mFeatureCombinationStats.mFeatureCombination + + ", status " + statusCode); + } + FrameworkStatsLog.write(FrameworkStatsLog.CAMERA_FEATURE_COMBINATION_QUERY_EVENT, + mFeatureCombinationStats.mUid, + mFeatureCombinationStats.mCameraId, + mFeatureCombinationStats.mQueryType, + mFeatureCombinationStats.mFeatureCombination, + statusCode); + } } private final class DisplayWindowListener extends IDisplayWindowListener.Stub { @@ -625,6 +845,32 @@ public class CameraServiceProxy extends SystemService } @Override + public void notifyFeatureCombinationStats(CameraFeatureCombinationStats featureCombStats) { + if (!Flags.analytics24q3()) { + return; + } + if (Binder.getCallingUid() != Process.CAMERASERVER_UID) { + Slog.e(TAG, "Calling UID: " + Binder.getCallingUid() + " doesn't match expected " + + " camera service UID!"); + return; + } + + if (DEBUG) { + String combinationTypeStr = cameraFeatureCombinationTypeToString( + featureCombStats.mQueryType); + String combinationStr = cameraFeatureCombinationToString( + featureCombStats.mFeatureCombination); + Slog.v(TAG, "Camera " + featureCombStats.mCameraId + + " query " + combinationTypeStr + + " combination " + combinationStr + + " for client UID " + featureCombStats.mUid + + " returns " + featureCombStats.mStatus); + } + + updateFeatureCombinationQuery(featureCombStats); + } + + @Override public boolean isCameraDisabled(int userId) { if (Binder.getCallingUid() != Process.CAMERASERVER_UID) { Slog.e(TAG, "Calling UID: " + Binder.getCallingUid() @@ -684,7 +930,7 @@ public class CameraServiceProxy extends SystemService switch (cmd.replace('-', '_')) { case "dump_events": int eventCount = mCameraServiceProxy.getUsageEventCount(); - mCameraServiceProxy.dumpUsageEvents(); + mCameraServiceProxy.dumpCameraEvents(); pw.println("Camera usage events dumped: " + eventCount); break; default: @@ -865,18 +1111,18 @@ public class CameraServiceProxy extends SystemService } private class EventWriterTask implements Runnable { - private ArrayList<CameraUsageEvent> mEventList; + private List<CameraEvent> mEventList; private static final long WRITER_SLEEP_MS = 100; - public EventWriterTask(ArrayList<CameraUsageEvent> eventList) { + EventWriterTask(List<CameraEvent> eventList) { mEventList = eventList; } @Override public void run() { if (mEventList != null) { - for (CameraUsageEvent event : mEventList) { - logCameraUsageEvent(event); + for (CameraEvent event : mEventList) { + event.logSelf(); try { Thread.sleep(WRITER_SLEEP_MS); } catch (InterruptedException e) {} @@ -884,170 +1130,6 @@ public class CameraServiceProxy extends SystemService mEventList.clear(); } } - - /** - * Write camera usage events to stats log. - * Package-private - */ - private void logCameraUsageEvent(CameraUsageEvent e) { - int facing = FrameworkStatsLog.CAMERA_ACTION_EVENT__FACING__UNKNOWN; - switch(e.mCameraFacing) { - case CameraSessionStats.CAMERA_FACING_BACK: - facing = FrameworkStatsLog.CAMERA_ACTION_EVENT__FACING__BACK; - break; - case CameraSessionStats.CAMERA_FACING_FRONT: - facing = FrameworkStatsLog.CAMERA_ACTION_EVENT__FACING__FRONT; - break; - case CameraSessionStats.CAMERA_FACING_EXTERNAL: - facing = FrameworkStatsLog.CAMERA_ACTION_EVENT__FACING__EXTERNAL; - break; - default: - Slog.w(TAG, "Unknown camera facing: " + e.mCameraFacing); - } - - int extensionType = FrameworkStatsLog.CAMERA_ACTION_EVENT__EXT_TYPE__EXTENSION_NONE; - boolean extensionIsAdvanced = false; - int extensionCaptureFormat = ImageFormat.UNKNOWN; - if (e.mExtSessionStats != null) { - switch (e.mExtSessionStats.type) { - case CameraExtensionSessionStats.Type.EXTENSION_AUTOMATIC: - extensionType = FrameworkStatsLog - .CAMERA_ACTION_EVENT__EXT_TYPE__EXTENSION_AUTOMATIC; - break; - case CameraExtensionSessionStats.Type.EXTENSION_FACE_RETOUCH: - extensionType = FrameworkStatsLog - .CAMERA_ACTION_EVENT__EXT_TYPE__EXTENSION_FACE_RETOUCH; - break; - case CameraExtensionSessionStats.Type.EXTENSION_BOKEH: - extensionType = - FrameworkStatsLog.CAMERA_ACTION_EVENT__EXT_TYPE__EXTENSION_BOKEH; - break; - case CameraExtensionSessionStats.Type.EXTENSION_HDR: - extensionType = - FrameworkStatsLog.CAMERA_ACTION_EVENT__EXT_TYPE__EXTENSION_HDR; - break; - case CameraExtensionSessionStats.Type.EXTENSION_NIGHT: - extensionType = - FrameworkStatsLog.CAMERA_ACTION_EVENT__EXT_TYPE__EXTENSION_NIGHT; - break; - default: - Slog.w(TAG, "Unknown extension type: " + e.mExtSessionStats.type); - } - extensionIsAdvanced = e.mExtSessionStats.isAdvanced; - if (Flags.analytics24q3()) { - extensionCaptureFormat = e.mExtSessionStats.captureFormat; - } - } - - int streamCount = 0; - if (e.mStreamStats != null) { - streamCount = e.mStreamStats.size(); - } - if (CameraServiceProxy.DEBUG) { - String ultrawideDebug = Flags.logUltrawideUsage() - ? ", wideAngleUsage " + e.mUsedUltraWide - : ""; - String zoomOverrideDebug = Flags.logZoomOverrideUsage() - ? ", zoomOverrideUsage " + e.mUsedZoomOverride - : ""; - String mostRequestedFpsRangeDebug = Flags.analytics24q3() - ? ", mostRequestedFpsRange " + e.mMostRequestedFpsRange - : ""; - String extensionCaptureFormatDebug = Flags.analytics24q3() - ? " extensionCaptureFormat " + e.mExtSessionStats.captureFormat - : ""; - - Slog.v(TAG, "CAMERA_ACTION_EVENT: action " + e.mAction - + " clientName " + e.mClientName - + ", duration " + e.getDuration() - + ", APILevel " + e.mAPILevel - + ", cameraId " + e.mCameraId - + ", facing " + facing - + ", isNdk " + e.mIsNdk - + ", latencyMs " + e.mLatencyMs - + ", operatingMode " + e.mOperatingMode - + ", internalReconfigure " + e.mInternalReconfigure - + ", requestCount " + e.mRequestCount - + ", resultErrorCount " + e.mResultErrorCount - + ", deviceError " + e.mDeviceError - + ", streamCount is " + streamCount - + ", userTag is " + e.mUserTag - + ", videoStabilizationMode " + e.mVideoStabilizationMode - + ultrawideDebug - + zoomOverrideDebug - + mostRequestedFpsRangeDebug - + ", logId " + e.mLogId - + ", sessionIndex " + e.mSessionIndex - + ", mExtSessionStats {type " + extensionType - + " isAdvanced " + extensionIsAdvanced - + extensionCaptureFormatDebug + "}"); - } - - // Convert from CameraStreamStats to CameraStreamProto - CameraStreamProto[] streamProtos = new CameraStreamProto[MAX_STREAM_STATISTICS]; - for (int i = 0; i < MAX_STREAM_STATISTICS; i++) { - streamProtos[i] = new CameraStreamProto(); - if (i < streamCount) { - CameraStreamStats streamStats = e.mStreamStats.get(i); - streamProtos[i].width = streamStats.getWidth(); - streamProtos[i].height = streamStats.getHeight(); - streamProtos[i].format = streamStats.getFormat(); - streamProtos[i].dataSpace = streamStats.getDataSpace(); - streamProtos[i].usage = streamStats.getUsage(); - streamProtos[i].requestCount = streamStats.getRequestCount(); - streamProtos[i].errorCount = streamStats.getErrorCount(); - streamProtos[i].firstCaptureLatencyMillis = streamStats.getStartLatencyMs(); - streamProtos[i].maxHalBuffers = streamStats.getMaxHalBuffers(); - streamProtos[i].maxAppBuffers = streamStats.getMaxAppBuffers(); - streamProtos[i].histogramType = streamStats.getHistogramType(); - streamProtos[i].histogramBins = streamStats.getHistogramBins(); - streamProtos[i].histogramCounts = streamStats.getHistogramCounts(); - streamProtos[i].dynamicRangeProfile = streamStats.getDynamicRangeProfile(); - streamProtos[i].streamUseCase = streamStats.getStreamUseCase(); - streamProtos[i].colorSpace = streamStats.getColorSpace(); - - if (CameraServiceProxy.DEBUG) { - String histogramTypeName = - cameraHistogramTypeToString(streamProtos[i].histogramType); - Slog.v(TAG, "Stream " + i + ": width " + streamProtos[i].width - + ", height " + streamProtos[i].height - + ", format " + streamProtos[i].format - + ", maxPreviewFps " + streamStats.getMaxPreviewFps() - + ", dataSpace " + streamProtos[i].dataSpace - + ", usage " + streamProtos[i].usage - + ", requestCount " + streamProtos[i].requestCount - + ", errorCount " + streamProtos[i].errorCount - + ", firstCaptureLatencyMillis " - + streamProtos[i].firstCaptureLatencyMillis - + ", maxHalBuffers " + streamProtos[i].maxHalBuffers - + ", maxAppBuffers " + streamProtos[i].maxAppBuffers - + ", histogramType " + histogramTypeName - + ", histogramBins " - + Arrays.toString(streamProtos[i].histogramBins) - + ", histogramCounts " - + Arrays.toString(streamProtos[i].histogramCounts) - + ", dynamicRangeProfile " + streamProtos[i].dynamicRangeProfile - + ", streamUseCase " + streamProtos[i].streamUseCase - + ", colorSpace " + streamProtos[i].colorSpace); - } - } - } - FrameworkStatsLog.write(FrameworkStatsLog.CAMERA_ACTION_EVENT, e.getDuration(), - e.mAPILevel, e.mClientName, facing, e.mCameraId, e.mAction, e.mIsNdk, - e.mLatencyMs, e.mOperatingMode, e.mInternalReconfigure, - e.mRequestCount, e.mResultErrorCount, e.mDeviceError, - streamCount, MessageNano.toByteArray(streamProtos[0]), - MessageNano.toByteArray(streamProtos[1]), - MessageNano.toByteArray(streamProtos[2]), - MessageNano.toByteArray(streamProtos[3]), - MessageNano.toByteArray(streamProtos[4]), - e.mUserTag, e.mVideoStabilizationMode, - e.mLogId, e.mSessionIndex, - extensionType, extensionIsAdvanced, e.mUsedUltraWide, - e.mUsedZoomOverride, - e.mMostRequestedFpsRange.getLower(), e.mMostRequestedFpsRange.getUpper(), - extensionCaptureFormat); - } } /** @@ -1055,22 +1137,22 @@ public class CameraServiceProxy extends SystemService */ int getUsageEventCount() { synchronized (mLock) { - return mCameraUsageHistory.size(); + return mCameraEventHistory.size(); } } /** - * Dump camera usage events to log. + * Dump camera events to log. * Package-private */ - void dumpUsageEvents() { + void dumpCameraEvents() { synchronized(mLock) { // Randomize order of events so that it's not meaningful - Collections.shuffle(mCameraUsageHistory); + Collections.shuffle(mCameraEventHistory); mLogWriterService.execute(new EventWriterTask( - new ArrayList<CameraUsageEvent>(mCameraUsageHistory))); + new ArrayList(mCameraEventHistory))); - mCameraUsageHistory.clear(); + mCameraEventHistory.clear(); } final long ident = Binder.clearCallingIdentity(); try { @@ -1288,7 +1370,7 @@ public class CameraServiceProxy extends SystemService cameraId, facing, clientName, apiLevel, isNdk, FrameworkStatsLog.CAMERA_ACTION_EVENT__ACTION__OPEN, latencyMs, sessionType, deviceError, logId, sessionIdx); - mCameraUsageHistory.add(openEvent); + mCameraEventHistory.add(openEvent); break; case CameraSessionStats.CAMERA_STATE_ACTIVE: // Check current active camera IDs to see if this package is already talking to @@ -1323,7 +1405,7 @@ public class CameraServiceProxy extends SystemService /*userTag*/"", /*videoStabilizationMode*/-1, /*usedUltraWide*/false, /*usedZoomOverride*/false, new Range<Integer>(0, 0), new CameraExtensionSessionStats()); - mCameraUsageHistory.add(oldEvent); + mCameraEventHistory.add(oldEvent); } break; case CameraSessionStats.CAMERA_STATE_IDLE: @@ -1335,7 +1417,7 @@ public class CameraServiceProxy extends SystemService resultErrorCount, deviceError, streamStats, userTag, videoStabilizationMode, usedUltraWide, usedZoomOverride, mostRequestedFpsRange, extSessionStats); - mCameraUsageHistory.add(doneEvent); + mCameraEventHistory.add(doneEvent); // Do not double count device error deviceError = false; @@ -1362,11 +1444,11 @@ public class CameraServiceProxy extends SystemService cameraId, facing, clientName, apiLevel, isNdk, FrameworkStatsLog.CAMERA_ACTION_EVENT__ACTION__CLOSE, latencyMs, sessionType, deviceError, logId, sessionIdx); - mCameraUsageHistory.add(closeEvent); + mCameraEventHistory.add(closeEvent); } - if (mCameraUsageHistory.size() > MAX_USAGE_HISTORY) { - dumpUsageEvents(); + if (mCameraEventHistory.size() > MAX_USAGE_HISTORY) { + dumpCameraEvents(); } break; @@ -1378,6 +1460,18 @@ public class CameraServiceProxy extends SystemService } } + private void updateFeatureCombinationQuery(CameraFeatureCombinationStats featureCombStats) { + synchronized (mLock) { + CameraFeatureCombinationQueryEvent e = + new CameraFeatureCombinationQueryEvent(featureCombStats); + mCameraEventHistory.add(e); + + if (mCameraEventHistory.size() > MAX_USAGE_HISTORY) { + dumpCameraEvents(); + } + } + } + private void notifyNfcService(boolean enablePolling) { NfcManager nfcManager = mContext.getSystemService(NfcManager.class); if (nfcManager == null) { @@ -1434,4 +1528,41 @@ public class CameraServiceProxy extends SystemService return "HISTOGRAM_TYPE_UNKNOWN"; } + private static String cameraFeatureCombinationTypeToString(int featureCombinationType) { + switch (featureCombinationType) { + case CameraFeatureCombinationStats.QueryType.QUERY_FEATURE_COMBINATION: + return "QUERY_FEATURE_COMBINATION"; + case CameraFeatureCombinationStats.QueryType.QUERY_SESSION_CHARACTERISTICS: + return "QUERY_SESSION_CHARACTERISTICS"; + default: + break; + } + return "FEATURE_COMBINATION_TYPE_UNKNOWN"; + } + + private static String cameraFeatureCombinationToString(long featureCombination) { + StringBuilder combinationStr = new StringBuilder("{"); + if ((featureCombination & CameraFeatureCombinationStats.CAMERA_FEATURE_60_FPS) != 0) { + combinationStr.append("60fps "); + } + if ((featureCombination & CameraFeatureCombinationStats.CAMERA_FEATURE_STABILIZATION) + != 0) { + combinationStr.append("stabilization "); + } + if ((featureCombination & CameraFeatureCombinationStats.CAMERA_FEATURE_HLG10) != 0) { + combinationStr.append("hlg10 "); + } + if ((featureCombination & CameraFeatureCombinationStats.CAMERA_FEATURE_JPEG) != 0) { + combinationStr.append("jpeg "); + } + if ((featureCombination & CameraFeatureCombinationStats.CAMERA_FEATURE_JPEG_R) != 0) { + combinationStr.append("jpeg_r "); + } + if ((featureCombination & CameraFeatureCombinationStats.CAMERA_FEATURE_4K) != 0) { + combinationStr.append("4k "); + } + combinationStr.append("}"); + + return combinationStr.toString(); + } } diff --git a/services/core/java/com/android/server/camera/CameraStatsJobService.java b/services/core/java/com/android/server/camera/CameraStatsJobService.java index b8a6846ced76..1227ca7ef057 100644 --- a/services/core/java/com/android/server/camera/CameraStatsJobService.java +++ b/services/core/java/com/android/server/camera/CameraStatsJobService.java @@ -21,14 +21,13 @@ import android.app.job.JobParameters; import android.app.job.JobScheduler; import android.app.job.JobService; import android.content.ComponentName; -import android.content.ContentResolver; import android.content.Context; import android.util.Slog; -import java.util.concurrent.TimeUnit; - import com.android.server.LocalServices; +import java.util.concurrent.TimeUnit; + /** * A JobService to periodically collect camera usage stats. */ @@ -50,7 +49,7 @@ public class CameraStatsJobService extends JobService { return false; } - serviceProxy.dumpUsageEvents(); + serviceProxy.dumpCameraEvents(); return false; } diff --git a/services/core/java/com/android/server/contentcapture/ContentCaptureManagerInternal.java b/services/core/java/com/android/server/contentcapture/ContentCaptureManagerInternal.java index 0812fd9fe36c..de7341d2acfb 100644 --- a/services/core/java/com/android/server/contentcapture/ContentCaptureManagerInternal.java +++ b/services/core/java/com/android/server/contentcapture/ContentCaptureManagerInternal.java @@ -21,6 +21,7 @@ import android.annotation.UserIdInt; import android.app.assist.ActivityId; import android.content.ComponentName; import android.content.ContentCaptureOptions; +import android.content.Intent; import android.os.Bundle; import android.os.IBinder; import android.service.contentcapture.ActivityEvent.ActivityEventType; @@ -40,6 +41,14 @@ public abstract class ContentCaptureManagerInternal { public abstract boolean isContentCaptureServiceForUser(int uid, @UserIdInt int userId); /** + * Notifies the intelligence service of new intent data associated with an activity start event. + * + * @return {@code false} if there was no service set for the given user + */ + public abstract boolean sendActivityStartAssistData(@UserIdInt int userId, + @NonNull IBinder activityToken, @NonNull Intent intentData); + + /** * Notifies the intelligence service of new assist data for the given activity. * * @return {@code false} if there was no service set for the given user diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java index 1949e6f2981e..7b5cff739ba1 100644 --- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java +++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java @@ -54,7 +54,6 @@ import com.android.internal.display.BrightnessSynchronizer; import com.android.internal.os.BackgroundThread; import com.android.server.EventLogTags; import com.android.server.display.brightness.BrightnessEvent; -import com.android.server.display.brightness.clamper.BrightnessClamperController; import com.android.server.display.config.HysteresisLevels; import com.android.server.display.feature.DisplayManagerFlags; @@ -256,7 +255,6 @@ public class AutomaticBrightnessController { // Controls Brightness range (including High Brightness Mode). private final BrightnessRangeController mBrightnessRangeController; - private final BrightnessClamperController mBrightnessClamperController; // Throttles (caps) maximum allowed brightness private final BrightnessThrottler mBrightnessThrottler; @@ -295,7 +293,6 @@ public class AutomaticBrightnessController { BrightnessRangeController brightnessModeController, BrightnessThrottler brightnessThrottler, int ambientLightHorizonShort, int ambientLightHorizonLong, float userLux, float userNits, - BrightnessClamperController brightnessClamperController, DisplayManagerFlags displayManagerFlags) { this(new Injector(), callbacks, looper, sensorManager, lightSensor, brightnessMappingStrategyMap, lightSensorWarmUpTime, brightnessMin, brightnessMax, @@ -306,7 +303,7 @@ public class AutomaticBrightnessController { screenBrightnessThresholds, ambientBrightnessThresholdsIdle, screenBrightnessThresholdsIdle, context, brightnessModeController, brightnessThrottler, ambientLightHorizonShort, ambientLightHorizonLong, userLux, - userNits, brightnessClamperController, displayManagerFlags + userNits, displayManagerFlags ); } @@ -325,7 +322,6 @@ public class AutomaticBrightnessController { BrightnessRangeController brightnessRangeController, BrightnessThrottler brightnessThrottler, int ambientLightHorizonShort, int ambientLightHorizonLong, float userLux, float userNits, - BrightnessClamperController brightnessClamperController, DisplayManagerFlags displayManagerFlags) { mInjector = injector; mClock = injector.createClock(displayManagerFlags.offloadControlsDozeAutoBrightness()); @@ -370,7 +366,6 @@ public class AutomaticBrightnessController { mForegroundAppCategory = ApplicationInfo.CATEGORY_UNDEFINED; mPendingForegroundAppCategory = ApplicationInfo.CATEGORY_UNDEFINED; mBrightnessRangeController = brightnessRangeController; - mBrightnessClamperController = brightnessClamperController; mBrightnessThrottler = brightnessThrottler; mBrightnessMappingStrategyMap = brightnessMappingStrategyMap; mDisplayManagerFlags = displayManagerFlags; @@ -771,7 +766,6 @@ public class AutomaticBrightnessController { mAmbientBrightnessThresholds.getDarkeningThreshold(lux); } mBrightnessRangeController.onAmbientLuxChange(mAmbientLux); - mBrightnessClamperController.onAmbientLuxChange(mAmbientLux); // If the short term model was invalidated and the change is drastic enough, reset it. mShortTermModel.maybeReset(mAmbientLux); diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index 5fd025399b12..2d5f38e2dd8c 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -3406,6 +3406,31 @@ public final class DisplayManagerService extends SystemService { } } + boolean requestDisplayPower(int displayId, boolean on) { + synchronized (mSyncRoot) { + final var display = mLogicalDisplayMapper.getDisplayLocked(displayId); + if (display == null) { + Slog.w(TAG, "requestDisplayPower: Cannot find a display with displayId=" + + displayId); + return false; + } + final BrightnessPair brightnessPair = mDisplayBrightnesses.get(displayId); + var runnable = display.getPrimaryDisplayDeviceLocked().requestDisplayStateLocked( + on ? Display.STATE_ON : Display.STATE_OFF, + on ? brightnessPair.brightness : PowerManager.BRIGHTNESS_OFF_FLOAT, + brightnessPair.sdrBrightness, + display.getDisplayOffloadSessionLocked()); + if (runnable == null) { + Slog.w(TAG, "requestDisplayPower: Cannot update the power state to ON=" + on + + " for a display with displayId=" + displayId + ", runnable is null"); + return false; + } + runnable.run(); + Slog.i(TAG, "requestDisplayPower(displayId=" + displayId + ", on=" + on + ")"); + } + return true; + } + /** * This is the object that everything in the display manager locks on. * We make it an inner class within the {@link DisplayManagerService} to so that it is @@ -4629,6 +4654,12 @@ public final class DisplayManagerService extends SystemService { DisplayManagerService.this.enableConnectedDisplay(displayId, false); } + @EnforcePermission(MANAGE_DISPLAYS) + public boolean requestDisplayPower(int displayId, boolean on) { + requestDisplayPower_enforcePermission(); + return DisplayManagerService.this.requestDisplayPower(displayId, on); + } + @EnforcePermission(RESTRICT_DISPLAY_MODES) @Override // Binder call public void requestDisplayModes(IBinder token, int displayId, @Nullable int[] modeIds) { diff --git a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java index 8c39d7de54f7..d973b71366b1 100644 --- a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java +++ b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java @@ -106,6 +106,10 @@ class DisplayManagerShellCommand extends ShellCommand { return setDisplayEnabled(true); case "disable-display": return setDisplayEnabled(false); + case "power-on": + return requestDisplayPower(true); + case "power-off": + return requestDisplayPower(false); default: return handleDefaultCommands(cmd); } @@ -592,4 +596,21 @@ class DisplayManagerShellCommand extends ShellCommand { mService.enableConnectedDisplay(displayId, enable); return 0; } + + private int requestDisplayPower(boolean enable) { + final String displayIdText = getNextArg(); + if (displayIdText == null) { + getErrPrintWriter().println("Error: no displayId specified"); + return 1; + } + final int displayId; + try { + displayId = Integer.parseInt(displayIdText); + } catch (NumberFormatException e) { + getErrPrintWriter().println("Error: invalid displayId: '" + displayIdText + "'"); + return 1; + } + mService.requestDisplayPower(displayId, enable); + return 0; + } } diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java index 195a516b663c..65a729a0293e 100644 --- a/services/core/java/com/android/server/display/DisplayPowerController.java +++ b/services/core/java/com/android/server/display/DisplayPowerController.java @@ -587,7 +587,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call mUniqueDisplayId, mThermalBrightnessThrottlingDataId, logicalDisplay.getPowerThrottlingDataIdLocked(), - mDisplayDeviceConfig), mContext, flags); + mDisplayDeviceConfig), mContext, flags, mSensorManager); // Seed the cached brightness saveBrightnessInfo(getScreenBrightnessSetting()); mAutomaticBrightnessStrategy = @@ -1422,7 +1422,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call : AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED; mBrightnessRangeController.setAutoBrightnessEnabled(autoBrightnessState); - mBrightnessClamperController.setAutoBrightnessState(autoBrightnessState); boolean updateScreenBrightnessSetting = displayBrightnessState.shouldUpdateScreenBrightnessSetting(); @@ -1549,7 +1548,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call // we broadcast this change through setting. final float unthrottledBrightnessState = brightnessState; DisplayBrightnessState clampedState = mBrightnessClamperController.clamp(mPowerRequest, - brightnessState, slowChange); + brightnessState, slowChange, /* displayState= */ state); brightnessState = clampedState.getBrightness(); slowChange = clampedState.isSlowChange(); @@ -2478,7 +2477,6 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call public void setBrightnessToFollow(float leadDisplayBrightness, float nits, float ambientLux, boolean slowChange) { mBrightnessRangeController.onAmbientLuxChange(ambientLux); - mBrightnessClamperController.onAmbientLuxChange(ambientLux); if (nits == BrightnessMappingStrategy.INVALID_NITS) { mDisplayBrightnessController.setBrightnessToFollow(leadDisplayBrightness, slowChange); } else { @@ -3194,7 +3192,7 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call screenBrightnessThresholds, ambientBrightnessThresholdsIdle, screenBrightnessThresholdsIdle, context, brightnessModeController, brightnessThrottler, ambientLightHorizonShort, ambientLightHorizonLong, userLux, - userNits, brightnessClamperController, displayManagerFlags); + userNits, displayManagerFlags); } BrightnessMappingStrategy getDefaultModeBrightnessMapper(Context context, @@ -3243,10 +3241,10 @@ final class DisplayPowerController implements AutomaticBrightnessController.Call BrightnessClamperController getBrightnessClamperController(Handler handler, BrightnessClamperController.ClamperChangeListener clamperChangeListener, BrightnessClamperController.DisplayDeviceData data, Context context, - DisplayManagerFlags flags) { + DisplayManagerFlags flags, SensorManager sensorManager) { return new BrightnessClamperController(handler, clamperChangeListener, data, context, - flags); + flags, sensorManager); } DisplayWhiteBalanceController getDisplayWhiteBalanceController(Handler handler, diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java index 11ef5776a47a..101ad307f50a 100644 --- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java +++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessClamperController.java @@ -16,21 +16,30 @@ package com.android.server.display.brightness.clamper; +import static android.view.Display.STATE_ON; + import static com.android.server.display.brightness.clamper.BrightnessClamper.Type; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; +import android.content.res.Resources; +import android.hardware.Sensor; +import android.hardware.SensorEvent; +import android.hardware.SensorEventListener; +import android.hardware.SensorManager; import android.hardware.display.BrightnessInfo; import android.hardware.display.DisplayManagerInternal; import android.os.Handler; import android.os.HandlerExecutor; import android.os.PowerManager; +import android.os.SystemClock; import android.provider.DeviceConfig; import android.provider.DeviceConfigInterface; import android.util.IndentingPrintWriter; import android.util.Slog; +import com.android.internal.R; import com.android.internal.annotations.VisibleForTesting; import com.android.server.display.DisplayBrightnessState; import com.android.server.display.DisplayDeviceConfig; @@ -41,20 +50,30 @@ import com.android.server.display.brightness.BrightnessReason; import com.android.server.display.config.SensorData; import com.android.server.display.feature.DeviceConfigParameterProvider; import com.android.server.display.feature.DisplayManagerFlags; +import com.android.server.display.utils.AmbientFilter; +import com.android.server.display.utils.AmbientFilterFactory; +import com.android.server.display.utils.DebugUtils; +import com.android.server.display.utils.SensorUtils; import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Executor; +import java.util.concurrent.TimeUnit; /** * Clampers controller, all in DisplayControllerHandler */ public class BrightnessClamperController { private static final String TAG = "BrightnessClamperController"; + // To enable these logs, run: + // 'adb shell setprop persist.log.tag.BrightnessClamperController DEBUG && adb reboot' + private static final boolean DEBUG = DebugUtils.isDebuggable(TAG); + public static final float INVALID_LUX = -1f; private final DeviceConfigParameterProvider mDeviceConfigParameterProvider; private final Handler mHandler; + private final SensorManager mSensorManager; private final ClamperChangeListener mClamperChangeListenerExternal; private final Executor mExecutor; private final List<BrightnessClamper<? super DisplayDeviceData>> mClampers; @@ -66,24 +85,55 @@ public class BrightnessClamperController { private float mCustomAnimationRate = DisplayBrightnessState.CUSTOM_ANIMATION_RATE_NOT_SET; @Nullable private Type mClamperType = null; - private int mAutoBrightnessState = -1; - + private final SensorEventListener mLightSensorListener; + private Sensor mRegisteredLightSensor = null; + private Sensor mLightSensor; + private String mLightSensorType; + private String mLightSensorName; + private AmbientFilter mAmbientFilter; + private final DisplayDeviceConfig mDisplayDeviceConfig; + private final Resources mResources; + private final int mLightSensorRate; + + private final Injector mInjector; private boolean mClamperApplied = false; public BrightnessClamperController(Handler handler, ClamperChangeListener clamperChangeListener, DisplayDeviceData data, Context context, - DisplayManagerFlags flags) { - this(new Injector(), handler, clamperChangeListener, data, context, flags); + DisplayManagerFlags flags, SensorManager sensorManager) { + this(null, handler, clamperChangeListener, data, context, flags, sensorManager); } @VisibleForTesting BrightnessClamperController(Injector injector, Handler handler, ClamperChangeListener clamperChangeListener, DisplayDeviceData data, Context context, - DisplayManagerFlags flags) { - mDeviceConfigParameterProvider = injector.getDeviceConfigParameterProvider(); + DisplayManagerFlags flags, SensorManager sensorManager) { + mInjector = injector == null ? new Injector() : injector; + mDeviceConfigParameterProvider = mInjector.getDeviceConfigParameterProvider(); mHandler = handler; + mSensorManager = sensorManager; + mDisplayDeviceConfig = data.mDisplayDeviceConfig; + mLightSensorListener = new SensorEventListener() { + @Override + public void onSensorChanged(SensorEvent event) { + long now = SystemClock.elapsedRealtime(); + mAmbientFilter.addValue(TimeUnit.NANOSECONDS.toMillis(event.timestamp), + event.values[0]); + final float lux = mAmbientFilter.getEstimate(now); + mModifiers.forEach(mModifier -> mModifier.setAmbientLux(lux)); + } + + @Override + public void onAccuracyChanged(Sensor sensor, int accuracy) { + // unused + } + }; + mClamperChangeListenerExternal = clamperChangeListener; mExecutor = new HandlerExecutor(handler); + mResources = context.getResources(); + mLightSensorRate = context.getResources().getInteger( + R.integer.config_autoBrightnessLightSensorRate); Runnable clamperChangeRunnableInternal = this::recalculateBrightnessCap; @@ -93,10 +143,10 @@ public class BrightnessClamperController { } }; - mClampers = injector.getClampers(handler, clamperChangeListenerInternal, data, flags, + mClampers = mInjector.getClampers(handler, clamperChangeListenerInternal, data, flags, context); - mModifiers = injector.getModifiers(flags, context, handler, clamperChangeListener, - data.mDisplayDeviceConfig); + mModifiers = mInjector.getModifiers(flags, context, handler, clamperChangeListener, + data.mDisplayDeviceConfig, mSensorManager); mOnPropertiesChangedListener = properties -> mClampers.forEach(BrightnessClamper::onDeviceConfigChanged); start(); @@ -114,7 +164,7 @@ public class BrightnessClamperController { * Called in DisplayControllerHandler */ public DisplayBrightnessState clamp(DisplayManagerInternal.DisplayPowerRequest request, - float brightnessValue, boolean slowChange) { + float brightnessValue, boolean slowChange, int displayState) { float cappedBrightness = Math.min(brightnessValue, mBrightnessCap); DisplayBrightnessState.Builder builder = DisplayBrightnessState.builder(); @@ -133,6 +183,12 @@ public class BrightnessClamperController { mClamperApplied = false; } + if (displayState != STATE_ON) { + unregisterSensorListener(); + } else { + maybeRegisterLightSensor(); + } + for (int i = 0; i < mModifiers.size(); i++) { mModifiers.get(i).apply(request, builder); } @@ -175,6 +231,8 @@ public class BrightnessClamperController { writer.println(" mBrightnessCap: " + mBrightnessCap); writer.println(" mClamperType: " + mClamperType); writer.println(" mClamperApplied: " + mClamperApplied); + writer.println(" mLightSensor=" + mLightSensor); + writer.println(" mRegisteredLightSensor=" + mRegisteredLightSensor); IndentingPrintWriter ipw = new IndentingPrintWriter(writer, " "); mClampers.forEach(clamper -> clamper.dump(ipw)); mModifiers.forEach(modifier -> modifier.dump(ipw)); @@ -191,26 +249,6 @@ public class BrightnessClamperController { mModifiers.forEach(BrightnessStateModifier::stop); } - /** - * Notifies modifiers that ambient lux has changed. - * @param ambientLux current lux, debounced - */ - public void onAmbientLuxChange(float ambientLux) { - mModifiers.forEach(modifier -> modifier.onAmbientLuxChange(ambientLux)); - } - - /** - * Sets the autobrightness state for clampers that need to be aware of the state. - * @param state autobrightness state - */ - public void setAutoBrightnessState(int state) { - if (state == mAutoBrightnessState) { - return; - } - mModifiers.forEach(modifier -> modifier.setAutoBrightnessState(state)); - mAutoBrightnessState = state; - recalculateBrightnessCap(); - } // Called in DisplayControllerHandler private void recalculateBrightnessCap() { @@ -243,6 +281,10 @@ public class BrightnessClamperController { if (!mClampers.isEmpty()) { mDeviceConfigParameterProvider.addOnPropertiesChangedListener( mExecutor, mOnPropertiesChangedListener); + reloadLightSensorData(mDisplayDeviceConfig); + mLightSensor = mInjector.getLightSensor( + mSensorManager, mLightSensorType, mLightSensorName); + maybeRegisterLightSensor(); } } @@ -281,7 +323,7 @@ public class BrightnessClamperController { List<BrightnessStateModifier> getModifiers(DisplayManagerFlags flags, Context context, Handler handler, ClamperChangeListener listener, - DisplayDeviceConfig displayDeviceConfig) { + DisplayDeviceConfig displayDeviceConfig, SensorManager sensorManager) { List<BrightnessStateModifier> modifiers = new ArrayList<>(); modifiers.add(new DisplayDimModifier(context)); modifiers.add(new BrightnessLowPowerModeModifier()); @@ -292,13 +334,19 @@ public class BrightnessClamperController { } return modifiers; } + + Sensor getLightSensor(SensorManager sensorManager, String type, String name) { + return SensorUtils.findSensor(sensorManager, type, + name, Sensor.TYPE_LIGHT); + } + } /** * Config Data for clampers */ public static class DisplayDeviceData implements BrightnessThermalClamper.ThermalData, - BrightnessPowerClamper.PowerData, + BrightnessPowerClamper.PowerData, BrightnessWearBedtimeModeClamper.WearBedtimeModeData { @NonNull private final String mUniqueDisplayId; @@ -368,4 +416,51 @@ public class BrightnessClamperController { return mDisplayDeviceConfig.getTempSensor(); } } + + private void maybeRegisterLightSensor() { + if (mModifiers.stream().noneMatch(BrightnessStateModifier::shouldListenToLightSensor)) { + return; + } + + if (mRegisteredLightSensor == mLightSensor) { + return; + } + + if (mRegisteredLightSensor != null) { + unregisterSensorListener(); + } + + mAmbientFilter = AmbientFilterFactory.createBrightnessFilter(TAG, mResources); + mSensorManager.registerListener(mLightSensorListener, + mLightSensor, mLightSensorRate * 1000, mHandler); + mRegisteredLightSensor = mLightSensor; + + if (DEBUG) { + Slog.d(TAG, "maybeRegisterLightSensor"); + } + } + + private void unregisterSensorListener() { + mSensorManager.unregisterListener(mLightSensorListener); + mRegisteredLightSensor = null; + mModifiers.forEach(mModifier -> mModifier.setAmbientLux(INVALID_LUX)); // set lux to invalid + if (DEBUG) { + Slog.d(TAG, "unregisterSensorListener"); + } + } + + private void reloadLightSensorData(DisplayDeviceConfig displayDeviceConfig) { + // The displayDeviceConfig (ddc) contains display specific preferences. When loaded, + // it naturally falls back to the global config.xml. + if (displayDeviceConfig != null + && displayDeviceConfig.getAmbientLightSensor() != null) { + // This covers both the ddc and the config.xml fallback + mLightSensorType = displayDeviceConfig.getAmbientLightSensor().type; + mLightSensorName = displayDeviceConfig.getAmbientLightSensor().name; + } else if (mLightSensorName == null && mLightSensorType == null) { + mLightSensorType = mResources.getString( + com.android.internal.R.string.config_displayLightSensorType); + mLightSensorName = ""; + } + } } diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessLowLuxModifier.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessLowLuxModifier.java index 7ba4a4d9c4dd..951980adac8c 100644 --- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessLowLuxModifier.java +++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessLowLuxModifier.java @@ -16,7 +16,6 @@ package com.android.server.display.brightness.clamper; -import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED; import android.content.ContentResolver; import android.content.Context; @@ -30,6 +29,7 @@ import android.util.Slog; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.display.BrightnessSynchronizer; +import com.android.server.display.BrightnessMappingStrategy; import com.android.server.display.DisplayBrightnessState; import com.android.server.display.DisplayDeviceConfig; import com.android.server.display.brightness.BrightnessReason; @@ -56,7 +56,6 @@ public class BrightnessLowLuxModifier extends BrightnessModifier { private float mBrightnessLowerBound; private float mMinNitsAllowed; private boolean mIsActive; - private boolean mAutoBrightnessEnabled; private float mAmbientLux; private final DisplayDeviceConfig mDisplayDeviceConfig; @@ -87,15 +86,15 @@ public class BrightnessLowLuxModifier extends BrightnessModifier { mContentResolver, Settings.Secure.EVEN_DIMMER_MIN_NITS, /* def= */ MIN_NITS_DEFAULT, userId); - boolean isActive = isSettingEnabled() && mAutoBrightnessEnabled; - - float luxBasedNitsLowerBound = mDisplayDeviceConfig.getMinNitsFromLux(mAmbientLux); + boolean isActive = isSettingEnabled() + && mAmbientLux != BrightnessMappingStrategy.INVALID_LUX; final int reason; float minNitsAllowed = -1f; // undefined, if setting is off. final float minBrightnessAllowed; if (isActive) { + float luxBasedNitsLowerBound = mDisplayDeviceConfig.getMinNitsFromLux(mAmbientLux); minNitsAllowed = Math.max(settingNitsLowerBound, luxBasedNitsLowerBound); minBrightnessAllowed = getBrightnessFromNits(minNitsAllowed); @@ -127,6 +126,12 @@ public class BrightnessLowLuxModifier extends BrightnessModifier { } @VisibleForTesting + public void setAmbientLux(float lux) { + mAmbientLux = lux; + recalculateLowerBound(); + } + + @VisibleForTesting public boolean isActive() { return mIsActive; } @@ -164,10 +169,10 @@ public class BrightnessLowLuxModifier extends BrightnessModifier { @Override public void apply(DisplayManagerInternal.DisplayPowerRequest request, DisplayBrightnessState.Builder stateBuilder) { + stateBuilder.setMinBrightness(mBrightnessLowerBound); float boundedBrightness = Math.max(mBrightnessLowerBound, stateBuilder.getBrightness()); stateBuilder.setBrightness(boundedBrightness); - if (BrightnessSynchronizer.floatEquals(stateBuilder.getBrightness(), mBrightnessLowerBound)) { stateBuilder.getBrightnessReason().addModifier(mReason); @@ -180,14 +185,8 @@ public class BrightnessLowLuxModifier extends BrightnessModifier { } @Override - public void onAmbientLuxChange(float ambientLux) { - mAmbientLux = ambientLux; - recalculateLowerBound(); - } - - @Override - public void setAutoBrightnessState(int state) { - mAutoBrightnessEnabled = state == AUTO_BRIGHTNESS_ENABLED; + public boolean shouldListenToLightSensor() { + return isSettingEnabled(); } @Override @@ -217,6 +216,7 @@ public class BrightnessLowLuxModifier extends BrightnessModifier { } private final class SettingsObserver extends ContentObserver { + SettingsObserver(Handler handler) { super(handler); mContentResolver.registerContentObserver( diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessLowPowerModeModifier.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessLowPowerModeModifier.java index b478952ee5bd..5661ede4b2ef 100644 --- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessLowPowerModeModifier.java +++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessLowPowerModeModifier.java @@ -51,4 +51,14 @@ class BrightnessLowPowerModeModifier extends BrightnessModifier { IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); super.dump(ipw); } + + @Override + public boolean shouldListenToLightSensor() { + return false; + } + + @Override + public void setAmbientLux(float lux) { + // unused + } } diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessModifier.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessModifier.java index db5a524da71d..be8fa5a0f0ce 100644 --- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessModifier.java +++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessModifier.java @@ -68,14 +68,4 @@ abstract class BrightnessModifier implements BrightnessStateModifier { public void stop() { // do nothing } - - @Override - public void onAmbientLuxChange(float ambientLux) { - // do nothing - } - - @Override - public void setAutoBrightnessState(int state) { - // do nothing - } } diff --git a/services/core/java/com/android/server/display/brightness/clamper/BrightnessStateModifier.java b/services/core/java/com/android/server/display/brightness/clamper/BrightnessStateModifier.java index 1606159cb247..fd40ce363016 100644 --- a/services/core/java/com/android/server/display/brightness/clamper/BrightnessStateModifier.java +++ b/services/core/java/com/android/server/display/brightness/clamper/BrightnessStateModifier.java @@ -44,14 +44,15 @@ public interface BrightnessStateModifier { void stop(); /** - * Allows modifiers to react to ambient lux changes. - * @param ambientLux current debounced lux. + * + * @return whether the brightness state modifier needs to listen to the ambient lux in order to + * calculate its bounds. */ - void onAmbientLuxChange(float ambientLux); + boolean shouldListenToLightSensor(); /** - * Sets the autobrightness state for clampers that need to be aware of the state. - * @param state autobrightness state + * Current ambient lux + * @param lux - ambient lux */ - void setAutoBrightnessState(int state); + void setAmbientLux(float lux); } diff --git a/services/core/java/com/android/server/display/brightness/clamper/DisplayDimModifier.java b/services/core/java/com/android/server/display/brightness/clamper/DisplayDimModifier.java index 4ff7bdb93d22..ab880bf28743 100644 --- a/services/core/java/com/android/server/display/brightness/clamper/DisplayDimModifier.java +++ b/services/core/java/com/android/server/display/brightness/clamper/DisplayDimModifier.java @@ -76,4 +76,14 @@ class DisplayDimModifier extends BrightnessModifier { IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); super.dump(ipw); } + + @Override + public boolean shouldListenToLightSensor() { + return false; + } + + @Override + public void setAmbientLux(float lux) { + // unused + } } diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index 0bd40d16ba4c..e5dbce9931a1 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -3352,6 +3352,10 @@ public class InputManagerService extends IInputManager.Stub mPointerIconCache.setPointerFillStyle(fillStyle); } + void setPointerScale(float scale) { + mPointerIconCache.setPointerScale(scale); + } + interface KeyboardBacklightControllerInterface { default void incrementKeyboardBacklight(int deviceId) {} default void decrementKeyboardBacklight(int deviceId) {} diff --git a/services/core/java/com/android/server/input/InputSettingsObserver.java b/services/core/java/com/android/server/input/InputSettingsObserver.java index 9585b49be3a9..593b0917efc7 100644 --- a/services/core/java/com/android/server/input/InputSettingsObserver.java +++ b/services/core/java/com/android/server/input/InputSettingsObserver.java @@ -16,6 +16,7 @@ package com.android.server.input; +import static android.view.PointerIcon.DEFAULT_POINTER_SCALE; import static android.view.PointerIcon.POINTER_ICON_VECTOR_STYLE_FILL_BLACK; import static android.view.flags.Flags.enableVectorCursorA11ySettings; @@ -101,7 +102,9 @@ class InputSettingsObserver extends ContentObserver { Map.entry(Settings.Secure.getUriFor(Settings.Secure.STYLUS_POINTER_ICON_ENABLED), (reason) -> updateStylusPointerIconEnabled()), Map.entry(Settings.System.getUriFor(Settings.System.POINTER_FILL_STYLE), - (reason) -> updatePointerFillStyleFromSettings())); + (reason) -> updatePointerFillStyleFromSettings()), + Map.entry(Settings.System.getUriFor(Settings.System.POINTER_SCALE), + (reason) -> updatePointerScaleFromSettings())); } /** @@ -277,4 +280,14 @@ class InputSettingsObserver extends ContentObserver { UserHandle.USER_CURRENT); mService.setPointerFillStyle(pointerFillStyle); } + + private void updatePointerScaleFromSettings() { + if (!enableVectorCursorA11ySettings()) { + return; + } + final float pointerScale = Settings.System.getFloatForUser(mContext.getContentResolver(), + Settings.System.POINTER_SCALE, DEFAULT_POINTER_SCALE, + UserHandle.USER_CURRENT); + mService.setPointerScale(pointerScale); + } } diff --git a/services/core/java/com/android/server/input/PointerIconCache.java b/services/core/java/com/android/server/input/PointerIconCache.java index 936e17f51919..44622d80da0e 100644 --- a/services/core/java/com/android/server/input/PointerIconCache.java +++ b/services/core/java/com/android/server/input/PointerIconCache.java @@ -16,6 +16,7 @@ package com.android.server.input; +import static android.view.PointerIcon.DEFAULT_POINTER_SCALE; import static android.view.PointerIcon.POINTER_ICON_VECTOR_STYLE_FILL_BLACK; import android.annotation.NonNull; @@ -63,6 +64,8 @@ final class PointerIconCache { @GuardedBy("mLoadedPointerIconsByDisplayAndType") private @PointerIcon.PointerIconVectorStyleFill int mPointerIconFillStyle = POINTER_ICON_VECTOR_STYLE_FILL_BLACK; + @GuardedBy("mLoadedPointerIconsByDisplayAndType") + private float mPointerIconScale = DEFAULT_POINTER_SCALE; private final DisplayManager.DisplayListener mDisplayListener = new DisplayManager.DisplayListener() { @@ -117,6 +120,11 @@ final class PointerIconCache { mUiThreadHandler.post(() -> handleSetPointerFillStyle(fillStyle)); } + /** Set the scale for vector pointer icons. */ + public void setPointerScale(float scale) { + mUiThreadHandler.post(() -> handleSetPointerScale(scale)); + } + /** * Get a loaded system pointer icon. This will fetch the icon from the cache, or load it if * it isn't already cached. @@ -137,7 +145,7 @@ final class PointerIconCache { theme.applyStyle(PointerIcon.vectorFillStyleToResource(mPointerIconFillStyle), /* force= */ true); icon = PointerIcon.getLoadedSystemIcon(new ContextThemeWrapper(context, theme), - type, mUseLargePointerIcons); + type, mUseLargePointerIcons, mPointerIconScale); iconsByType.put(type, icon); } return Objects.requireNonNull(icon); @@ -215,6 +223,19 @@ final class PointerIconCache { mNative.reloadPointerIcons(); } + @android.annotation.UiThread + private void handleSetPointerScale(float scale) { + synchronized (mLoadedPointerIconsByDisplayAndType) { + if (mPointerIconScale == scale) { + return; + } + mPointerIconScale = scale; + // Clear all cached icons on all displays. + mLoadedPointerIconsByDisplayAndType.clear(); + } + mNative.reloadPointerIcons(); + } + // Updates the cached display density for the given displayId, and returns true if // the cached density changed. @GuardedBy("mLoadedPointerIconsByDisplayAndType") diff --git a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java index 3d75c48311b2..9e8bf0e2a060 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java @@ -17,6 +17,7 @@ package com.android.server.inputmethod; import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED; +import static android.content.Context.DEVICE_ID_DEFAULT; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; import static android.view.Display.INVALID_DISPLAY; @@ -91,6 +92,7 @@ final class InputMethodBindingController { /** The display id for which the latest startInput was called. */ @GuardedBy("ImfLock.class") private int mDisplayIdToShowIme = INVALID_DISPLAY; + @GuardedBy("ImfLock.class") private int mDeviceIdToShowIme = DEVICE_ID_DEFAULT; @Nullable private CountDownLatch mLatchForTesting; @@ -609,4 +611,14 @@ final class InputMethodBindingController { int getDisplayIdToShowIme() { return mDisplayIdToShowIme; } + + @GuardedBy("ImfLock.class") + void setDeviceIdToShowIme(int deviceId) { + mDeviceIdToShowIme = deviceId; + } + + @GuardedBy("ImfLock.class") + int getDeviceIdToShowIme() { + return mDeviceIdToShowIme; + } } diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index d236d7a960f2..223d54842b16 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -382,10 +382,6 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. @MultiUserUnawareField private HardwareKeyboardShortcutController mHardwareKeyboardShortcutController; - @GuardedBy("ImfLock.class") - @MultiUserUnawareField - private int mDeviceIdToShowIme = DEVICE_ID_DEFAULT; - @Nullable private StatusBarManagerInternal mStatusBarManagerInternal; @SharedByAllUsersField @@ -490,10 +486,17 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. @GuardedBy("ImfLock.class") @NonNull + UserDataRepository.UserData getUserData(@UserIdInt int userId) { + return mUserDataRepository.getOrCreate(userId); + } + + @GuardedBy("ImfLock.class") + @NonNull InputMethodBindingController getInputMethodBindingController(@UserIdInt int userId) { - return mUserDataRepository.getOrCreate(userId).mBindingController; + return getUserData(userId).mBindingController; } + /** * Id obtained with {@link InputMethodInfo#getId()} for the currently selected input method. * This is to be synchronized with the secure settings keyed with @@ -1172,7 +1175,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. final int userId = user.getUserIdentifier(); SecureSettingsWrapper.onUserStarting(userId); synchronized (ImfLock.class) { - mService.mUserDataRepository.getOrCreate(userId); + mService.getUserData(userId); if (mService.mExperimentalConcurrentMultiUserModeEnabled) { if (mService.mCurrentUserId != userId && mService.mSystemReady) { mService.experimentalInitializeVisibleBackgroundUserLocked(userId); @@ -1294,7 +1297,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. bindingControllerForTesting != null ? bindingControllerForTesting : bindingControllerFactory); for (int id : mUserManagerInternal.getUserIds()) { - mUserDataRepository.getOrCreate(id); + getUserData(id); } final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId); @@ -2143,7 +2146,8 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. if (deviceMethodId == null) { mVisibilityStateComputer.getImePolicy().setImeHiddenByDisplayPolicy(true); } else if (!Objects.equals(deviceMethodId, selectedMethodId)) { - setInputMethodLocked(deviceMethodId, NOT_A_SUBTYPE_ID, mDeviceIdToShowIme); + setInputMethodLocked(deviceMethodId, NOT_A_SUBTYPE_ID, + bindingController.getDeviceIdToShowIme()); selectedMethodId = deviceMethodId; } @@ -2256,11 +2260,12 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. } final InputMethodSettings settings = InputMethodSettingsRepository.get(userId); - final int oldDeviceId = mDeviceIdToShowIme; final var bindingController = getInputMethodBindingController(userId); + final int oldDeviceId = bindingController.getDeviceIdToShowIme(); final int displayIdToShowIme = bindingController.getDisplayIdToShowIme(); - mDeviceIdToShowIme = mVdmInternal.getDeviceIdForDisplayId(displayIdToShowIme); - if (mDeviceIdToShowIme == DEVICE_ID_DEFAULT) { + final int newDeviceId = mVdmInternal.getDeviceIdForDisplayId(displayIdToShowIme); + bindingController.setDeviceIdToShowIme(newDeviceId); + if (newDeviceId == DEVICE_ID_DEFAULT) { if (oldDeviceId == DEVICE_ID_DEFAULT) { return currentMethodId; } @@ -2272,13 +2277,12 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. return defaultDeviceMethodId; } - final String deviceMethodId = - mVirtualDeviceMethodMap.get(mDeviceIdToShowIme, currentMethodId); + final String deviceMethodId = mVirtualDeviceMethodMap.get(newDeviceId, currentMethodId); if (Objects.equals(deviceMethodId, currentMethodId)) { return currentMethodId; } else if (!settings.getMethodMap().containsKey(deviceMethodId)) { if (DEBUG) { - Slog.v(TAG, "Disabling IME on virtual device with id " + mDeviceIdToShowIme + Slog.v(TAG, "Disabling IME on virtual device with id " + newDeviceId + " because its custom input method is not available: " + deviceMethodId); } return null; @@ -2293,7 +2297,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. if (DEBUG) { Slog.v(TAG, "Switching current input method from " + currentMethodId + " to device-specific one " + deviceMethodId + " because the current display " - + displayIdToShowIme + " belongs to device with id " + mDeviceIdToShowIme); + + displayIdToShowIme + " belongs to device with id " + newDeviceId); } return deviceMethodId; } @@ -2492,8 +2496,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. @GuardedBy("ImfLock.class") void resetCurrentMethodAndClientLocked(@UnbindReason int unbindClientReason) { - final var bindingController = - mUserDataRepository.getOrCreate(mCurrentUserId).mBindingController; + final var bindingController = getInputMethodBindingController(mCurrentUserId); bindingController.setSelectedMethodId(null); // Callback before clean-up binding states. @@ -2934,7 +2937,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. * <li> * {@link PackageManager#COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED} is not updated. * </li> - * <li>{@link #mDeviceIdToShowIme} is ignored.</li> + * <li>{@link InputMethodBindingController#getDeviceIdToShowIme()} is ignored.</li> * <li>{@link #mSwitchingController} is ignored.</li> * <li>{@link #mHardwareKeyboardShortcutController} is ignored.</li> * <li>{@link #mPreventImeStartupUnlessTextEditor} is ignored.</li> @@ -3004,7 +3007,8 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. } } - if (mDeviceIdToShowIme == DEVICE_ID_DEFAULT) { + final var bindingController = getInputMethodBindingController(mCurrentUserId); + if (bindingController.getDeviceIdToShowIme() == DEVICE_ID_DEFAULT) { String ime = SecureSettingsWrapper.getString( Settings.Secure.DEFAULT_INPUT_METHOD, null, userId); String defaultDeviceIme = SecureSettingsWrapper.getString( @@ -3086,8 +3090,9 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. throw getExceptionForUnknownImeId(id); } + final var bindingController = getInputMethodBindingController(userId); // See if we need to notify a subtype change within the same IME. - if (id.equals(getSelectedMethodIdLocked())) { + if (id.equals(bindingController.getSelectedMethodId())) { final int subtypeCount = info.getSubtypeCount(); if (subtypeCount <= 0) { notifyInputMethodSubtypeChangedLocked(userId, info, null); @@ -3123,7 +3128,8 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. } // Changing to a different IME. - if (mDeviceIdToShowIme != DEVICE_ID_DEFAULT && deviceId == DEVICE_ID_DEFAULT) { + if (bindingController.getDeviceIdToShowIme() != DEVICE_ID_DEFAULT + && deviceId == DEVICE_ID_DEFAULT) { // This change should only be applicable to the default device but the current input // method is a custom one specific to a virtual device. So only update the settings // entry used to restore the default device input method once we want to show the IME @@ -3143,7 +3149,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. // mCurMethodId should be updated after setSelectedInputMethodAndSubtypeLocked() // because mCurMethodId is stored as a history in // setSelectedInputMethodAndSubtypeLocked(). - getInputMethodBindingController(userId).setSelectedMethodId(id); + bindingController.setSelectedMethodId(id); if (mActivityManagerInternal.isSystemReady()) { Intent intent = new Intent(Intent.ACTION_INPUT_METHOD_CHANGED); @@ -5241,7 +5247,8 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. return; } - final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId); + final int userId = mCurrentUserId; + final InputMethodSettings settings = InputMethodSettingsRepository.get(userId); boolean reenableMinimumNonAuxSystemImes = false; // TODO: The following code should find better place to live. @@ -5304,18 +5311,18 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. updateDefaultVoiceImeIfNeededLocked(); // TODO: Instantiate mSwitchingController for each user. - if (settings.getUserId() == mSwitchingController.getUserId()) { + if (userId == mSwitchingController.getUserId()) { mSwitchingController.resetCircularListLocked(settings.getMethodMap()); } else { mSwitchingController = InputMethodSubtypeSwitchingController.createInstanceLocked( mContext, settings.getMethodMap(), mCurrentUserId); } // TODO: Instantiate mHardwareKeyboardShortcutController for each user. - if (settings.getUserId() == mHardwareKeyboardShortcutController.getUserId()) { + if (userId == mHardwareKeyboardShortcutController.getUserId()) { mHardwareKeyboardShortcutController.reset(settings.getMethodMap()); } else { mHardwareKeyboardShortcutController = new HardwareKeyboardShortcutController( - settings.getMethodMap(), settings.getUserId()); + settings.getMethodMap(), userId); } sendOnNavButtonFlagsChangedLocked(); @@ -5323,7 +5330,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. // Notify InputMethodListListeners of the new installed InputMethods. final List<InputMethodInfo> inputMethodList = settings.getMethodList(); mHandler.obtainMessage(MSG_DISPATCH_ON_INPUT_METHOD_LIST_UPDATED, - settings.getUserId(), 0 /* unused */, inputMethodList).sendToTarget(); + userId, 0 /* unused */, inputMethodList).sendToTarget(); } @GuardedBy("ImfLock.class") @@ -5381,7 +5388,8 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. */ @GuardedBy("ImfLock.class") private boolean setInputMethodEnabledLocked(String id, boolean enabled) { - final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId); + final int userId = mCurrentUserId; + final InputMethodSettings settings = InputMethodSettingsRepository.get(userId); if (enabled) { final String enabledImeIdsStr = settings.getEnabledInputMethodsStr(); final String newEnabledImeIdsStr = InputMethodUtils.concatEnabledImeIds( @@ -5400,7 +5408,8 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. StringBuilder builder = new StringBuilder(); if (settings.buildAndPutEnabledInputMethodsStrRemovingId( builder, enabledInputMethodsList, id)) { - if (mDeviceIdToShowIme == DEVICE_ID_DEFAULT) { + final var bindingController = getInputMethodBindingController(userId); + if (bindingController.getDeviceIdToShowIme() == DEVICE_ID_DEFAULT) { // Disabled input method is currently selected, switch to another one. final String selId = settings.getSelectedInputMethod(); if (id.equals(selId) && !chooseNewDefaultIMELocked()) { @@ -5458,9 +5467,9 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. @GuardedBy("ImfLock.class") private void resetSelectedInputMethodAndSubtypeLocked(String newDefaultIme) { - mDeviceIdToShowIme = DEVICE_ID_DEFAULT; final var bindingController = getInputMethodBindingController(mCurrentUserId); bindingController.setDisplayIdToShowIme(INVALID_DISPLAY); + bindingController.setDeviceIdToShowIme(DEVICE_ID_DEFAULT); final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId); settings.putSelectedDefaultDeviceInputMethod(null); @@ -5517,44 +5526,18 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. */ @GuardedBy("ImfLock.class") InputMethodSubtype getCurrentInputMethodSubtypeLocked() { - String selectedMethodId = getSelectedMethodIdLocked(); + final int userId = mCurrentUserId; + final var selectedMethodId = getInputMethodBindingController(userId).getSelectedMethodId(); if (selectedMethodId == null) { return null; } - final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId); - final boolean subtypeIsSelected = settings.isSubtypeSelected(); + final InputMethodSettings settings = InputMethodSettingsRepository.get(userId); final InputMethodInfo imi = settings.getMethodMap().get(selectedMethodId); if (imi == null || imi.getSubtypeCount() == 0) { return null; } - if (!subtypeIsSelected || mCurrentSubtype == null - || !SubtypeUtils.isValidSubtypeId(imi, mCurrentSubtype.hashCode())) { - int subtypeId = settings.getSelectedInputMethodSubtypeId(selectedMethodId); - if (subtypeId == NOT_A_SUBTYPE_ID) { - // If there are no selected subtypes, the framework will try to find - // the most applicable subtype from explicitly or implicitly enabled - // subtypes. - List<InputMethodSubtype> explicitlyOrImplicitlyEnabledSubtypes = - settings.getEnabledInputMethodSubtypeList(imi, true); - // If there is only one explicitly or implicitly enabled subtype, - // just returns it. - if (explicitlyOrImplicitlyEnabledSubtypes.size() == 1) { - mCurrentSubtype = explicitlyOrImplicitlyEnabledSubtypes.get(0); - } else if (explicitlyOrImplicitlyEnabledSubtypes.size() > 1) { - final String locale = SystemLocaleWrapper.get(settings.getUserId()) - .get(0).toString(); - mCurrentSubtype = SubtypeUtils.findLastResortApplicableSubtype( - explicitlyOrImplicitlyEnabledSubtypes, - SubtypeUtils.SUBTYPE_MODE_KEYBOARD, locale, true); - if (mCurrentSubtype == null) { - mCurrentSubtype = SubtypeUtils.findLastResortApplicableSubtype( - explicitlyOrImplicitlyEnabledSubtypes, null, locale, true); - } - } - } else { - mCurrentSubtype = SubtypeUtils.getSubtypes(imi).get(subtypeId); - } - } + mCurrentSubtype = SubtypeUtils.getCurrentInputMethodSubtype(imi, settings, + mCurrentSubtype); return mCurrentSubtype; } @@ -6132,7 +6115,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. p.println(" mStylusIds=" + (mStylusIds != null ? Arrays.toString(mStylusIds.toArray()) : "")); p.println(" mSwitchingController:"); - mSwitchingController.dump(p); + mSwitchingController.dump(p, " "); p.println(" mStartInputHistory:"); mStartInputHistory.dump(pw, " "); diff --git a/services/core/java/com/android/server/inputmethod/InputMethodSettings.java b/services/core/java/com/android/server/inputmethod/InputMethodSettings.java index a558838172f8..7ce4074bb1d0 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodSettings.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodSettings.java @@ -51,8 +51,23 @@ final class InputMethodSettings { public static final boolean DEBUG = false; private static final String TAG = "InputMethodSettings"; - private static final int NOT_A_SUBTYPE_ID = InputMethodUtils.NOT_A_SUBTYPE_ID; - private static final String NOT_A_SUBTYPE_ID_STR = String.valueOf(NOT_A_SUBTYPE_ID); + /** + * An integer code that represents "no subtype" when a subtype hashcode is used. + * + * <p>Due to historical confusions with {@link InputMethodUtils#NOT_A_SUBTYPE_ID}, we have + * used {@code -1} here. We cannot change this value as it's already saved into secure settings. + * </p> + */ + private static final int INVALID_SUBTYPE_HASHCODE = -1; + /** + * A string code that represents "no subtype" when a subtype hashcode is used. + * + * <p>Due to historical confusions with {@link InputMethodUtils#NOT_A_SUBTYPE_ID}, we have + * used {@code "-1"} here. We cannot change this value as it's already saved into secure + * settings.</p> + */ + private static final String INVALID_SUBTYPE_HASHCODE_STR = + String.valueOf(INVALID_SUBTYPE_HASHCODE); private static final char INPUT_METHOD_SEPARATOR = InputMethodUtils.INPUT_METHOD_SEPARATOR; private static final char INPUT_METHOD_SUBTYPE_SEPARATOR = InputMethodUtils.INPUT_METHOD_SUBTYPE_SEPARATOR; @@ -259,34 +274,33 @@ final class InputMethodSettings { } private void saveSubtypeHistory( - List<Pair<String, String>> savedImes, String newImeId, String newSubtypeId) { + List<Pair<String, String>> savedImes, String newImeId, String newSubtypeHashCodeStr) { final StringBuilder builder = new StringBuilder(); boolean isImeAdded = false; - if (!TextUtils.isEmpty(newImeId) && !TextUtils.isEmpty(newSubtypeId)) { + if (!TextUtils.isEmpty(newImeId) && !TextUtils.isEmpty(newSubtypeHashCodeStr)) { builder.append(newImeId).append(INPUT_METHOD_SUBTYPE_SEPARATOR).append( - newSubtypeId); + newSubtypeHashCodeStr); isImeAdded = true; } for (int i = 0; i < savedImes.size(); ++i) { final Pair<String, String> ime = savedImes.get(i); final String imeId = ime.first; - String subtypeId = ime.second; - if (TextUtils.isEmpty(subtypeId)) { - subtypeId = NOT_A_SUBTYPE_ID_STR; + String subtypeHashCodeStr = ime.second; + if (TextUtils.isEmpty(subtypeHashCodeStr)) { + subtypeHashCodeStr = INVALID_SUBTYPE_HASHCODE_STR; } if (isImeAdded) { builder.append(INPUT_METHOD_SEPARATOR); } else { isImeAdded = true; } - builder.append(imeId).append(INPUT_METHOD_SUBTYPE_SEPARATOR).append( - subtypeId); + builder.append(imeId).append(INPUT_METHOD_SUBTYPE_SEPARATOR).append(subtypeHashCodeStr); } // Remove the last INPUT_METHOD_SEPARATOR putSubtypeHistoryStr(builder.toString()); } - private void addSubtypeToHistory(String imeId, String subtypeId) { + private void addSubtypeToHistory(String imeId, String subtypeHashCodeStr) { final List<Pair<String, String>> subtypeHistory = loadInputMethodAndSubtypeHistory(); for (int i = 0; i < subtypeHistory.size(); ++i) { final Pair<String, String> ime = subtypeHistory.get(i); @@ -301,9 +315,9 @@ final class InputMethodSettings { } } if (DEBUG) { - Slog.v(TAG, "Add subtype to the history: " + imeId + ", " + subtypeId); + Slog.v(TAG, "Add subtype to the history: " + imeId + ", " + subtypeHashCodeStr); } - saveSubtypeHistory(subtypeHistory, imeId, subtypeId); + saveSubtypeHistory(subtypeHistory, imeId, subtypeHashCodeStr); } private void putSubtypeHistoryStr(@NonNull String str) { @@ -413,26 +427,26 @@ final class InputMethodSettings { for (int j = 0; j < explicitlyEnabledSubtypes.size(); ++j) { final String s = explicitlyEnabledSubtypes.get(j); if (s.equals(subtypeHashCode)) { - // If both imeId and subtypeId are enabled, return subtypeId. + // If both imeId and subtype are enabled, return subtypeId. try { final int hashCode = Integer.parseInt(subtypeHashCode); - // Check whether the subtype id is valid or not - if (SubtypeUtils.isValidSubtypeId(imi, hashCode)) { + // Check whether the subtype is valid or not + if (SubtypeUtils.isValidSubtypeHashCode(imi, hashCode)) { return s; } else { - return NOT_A_SUBTYPE_ID_STR; + return INVALID_SUBTYPE_HASHCODE_STR; } } catch (NumberFormatException e) { - return NOT_A_SUBTYPE_ID_STR; + return INVALID_SUBTYPE_HASHCODE_STR; } } } } - // If imeId was enabled but subtypeId was disabled. - return NOT_A_SUBTYPE_ID_STR; + // If imeId was enabled but subtype was disabled. + return INVALID_SUBTYPE_HASHCODE_STR; } } - // If both imeId and subtypeId are disabled, return null + // If both imeId and subtype are disabled, return null return null; } @@ -451,14 +465,14 @@ final class InputMethodSettings { String nextImsStr = inputMethodSplitter.next(); subtypeSplitter.setString(nextImsStr); if (subtypeSplitter.hasNext()) { - String subtypeId = NOT_A_SUBTYPE_ID_STR; + String subtypeHashCodeStr = INVALID_SUBTYPE_HASHCODE_STR; // The first element is ime id. String imeId = subtypeSplitter.next(); while (subtypeSplitter.hasNext()) { - subtypeId = subtypeSplitter.next(); + subtypeHashCodeStr = subtypeSplitter.next(); break; } - imsList.add(new Pair<>(imeId, subtypeId)); + imsList.add(new Pair<>(imeId, subtypeHashCodeStr)); } } return imsList; @@ -528,13 +542,8 @@ final class InputMethodSettings { return imi; } - boolean isSubtypeSelected() { - return getSelectedInputMethodSubtypeHashCode() != NOT_A_SUBTYPE_ID; - } - private int getSelectedInputMethodSubtypeHashCode() { - return getInt(Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE, - NOT_A_SUBTYPE_ID); + return getInt(Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE, INVALID_SUBTYPE_HASHCODE); } @UserIdInt @@ -545,7 +554,7 @@ final class InputMethodSettings { int getSelectedInputMethodSubtypeId(String selectedImiId) { final InputMethodInfo imi = mMethodMap.get(selectedImiId); if (imi == null) { - return NOT_A_SUBTYPE_ID; + return InputMethodUtils.NOT_A_SUBTYPE_ID; } final int subtypeHashCode = getSelectedInputMethodSubtypeHashCode(); return SubtypeUtils.getSubtypeIdFromHashCode(imi, subtypeHashCode); @@ -553,12 +562,12 @@ final class InputMethodSettings { void saveCurrentInputMethodAndSubtypeToHistory(String curMethodId, InputMethodSubtype currentSubtype) { - String subtypeId = NOT_A_SUBTYPE_ID_STR; + String subtypeHashCodeStr = INVALID_SUBTYPE_HASHCODE_STR; if (currentSubtype != null) { - subtypeId = String.valueOf(currentSubtype.hashCode()); + subtypeHashCodeStr = String.valueOf(currentSubtype.hashCode()); } if (InputMethodUtils.canAddToLastInputMethod(currentSubtype)) { - addSubtypeToHistory(curMethodId, subtypeId); + addSubtypeToHistory(curMethodId, subtypeHashCodeStr); } } @@ -583,7 +592,7 @@ final class InputMethodSettings { } final int subtypeHashCode = getSelectedInputMethodSubtypeHashCode(); - if (subtypeHashCode != NOT_A_SUBTYPE_ID) { + if (subtypeHashCode != INVALID_SUBTYPE_HASHCODE) { final int subtypeIndex = SubtypeUtils.getSubtypeIdFromHashCode(imi, subtypeHashCode); if (subtypeIndex >= 0) { @@ -645,10 +654,10 @@ final class InputMethodSettings { final IntArray validSubtypeHashCodes = new IntArray(subtypeHashCodes.length); for (int subtypeHashCode : subtypeHashCodes) { - if (subtypeHashCode == NOT_A_SUBTYPE_ID) { - continue; // NOT_A_SUBTYPE_ID must not be saved + if (subtypeHashCode == INVALID_SUBTYPE_HASHCODE) { + continue; // INVALID_SUBTYPE_HASHCODE must not be saved } - if (!SubtypeUtils.isValidSubtypeId(imi, subtypeHashCode)) { + if (!SubtypeUtils.isValidSubtypeHashCode(imi, subtypeHashCode)) { continue; // this subtype does not exist in InputMethodInfo. } if (validSubtypeHashCodes.indexOf(subtypeHashCode) >= 0) { diff --git a/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java b/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java index 23f947cc8452..770e3b267145 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodSubtypeSwitchingController.java @@ -465,11 +465,11 @@ final class InputMethodSubtypeSwitchingController { return result; } - protected void dump(final Printer pw) { - pw.println(" mSwitchingAwareRotationList:"); - mSwitchingAwareRotationList.dump(pw, " "); - pw.println(" mSwitchingUnawareRotationList:"); - mSwitchingUnawareRotationList.dump(pw, " "); + protected void dump(@NonNull Printer pw, @NonNull String prefix) { + pw.println(prefix + "mSwitchingAwareRotationList:"); + mSwitchingAwareRotationList.dump(pw, prefix + " "); + pw.println(prefix + "mSwitchingUnawareRotationList:"); + mSwitchingUnawareRotationList.dump(pw, prefix + " "); } } @@ -529,11 +529,11 @@ final class InputMethodSubtypeSwitchingController { return mController.getNextInputMethod(onlyCurrentIme, imi, subtype); } - public void dump(final Printer pw) { + public void dump(@NonNull Printer pw, @NonNull String prefix) { if (mController != null) { - mController.dump(pw); + mController.dump(pw, prefix); } else { - pw.println(" mController=null"); + pw.println(prefix + "mController=null"); } } } diff --git a/services/core/java/com/android/server/inputmethod/SubtypeUtils.java b/services/core/java/com/android/server/inputmethod/SubtypeUtils.java index 3d5c867768ac..1b4c0d6ef4d5 100644 --- a/services/core/java/com/android/server/inputmethod/SubtypeUtils.java +++ b/services/core/java/com/android/server/inputmethod/SubtypeUtils.java @@ -16,9 +16,11 @@ package com.android.server.inputmethod; +import android.annotation.AnyThread; import android.annotation.NonNull; import android.annotation.Nullable; import android.os.LocaleList; +import android.provider.Settings; import android.text.TextUtils; import android.util.ArrayMap; import android.util.Slog; @@ -100,7 +102,7 @@ final class SubtypeUtils { return subtypes; } - static boolean isValidSubtypeId(InputMethodInfo imi, int subtypeHashCode) { + static boolean isValidSubtypeHashCode(InputMethodInfo imi, int subtypeHashCode) { return getSubtypeIdFromHashCode(imi, subtypeHashCode) != NOT_A_SUBTYPE_ID; } @@ -289,4 +291,54 @@ final class SubtypeUtils { } return applicableSubtype; } + + /** + * Returns a {@link InputMethodSubtype} available in {@code imi} based on + * {@link Settings.Secure#SELECTED_INPUT_METHOD_SUBTYPE}. + * + * @param imi {@link InputMethodInfo} to find out the current + * {@link InputMethodSubtype} + * @param settings {@link InputMethodSettings} to be used to find out the current + * {@link InputMethodSubtype} + * @param currentSubtype the current value that will be used as fallback + * @return {@link InputMethodSubtype} to be used as the current {@link InputMethodSubtype} + */ + @AnyThread + @Nullable + static InputMethodSubtype getCurrentInputMethodSubtype( + @NonNull InputMethodInfo imi, @NonNull InputMethodSettings settings, + @Nullable InputMethodSubtype currentSubtype) { + final int userId = settings.getUserId(); + final int selectedSubtypeHashCode = SecureSettingsWrapper.getInt( + Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE, NOT_A_SUBTYPE_ID, userId); + if (selectedSubtypeHashCode != NOT_A_SUBTYPE_ID && currentSubtype != null + && isValidSubtypeHashCode(imi, currentSubtype.hashCode())) { + return currentSubtype; + } + + final int subtypeId = settings.getSelectedInputMethodSubtypeId(imi.getId()); + if (subtypeId != NOT_A_SUBTYPE_ID) { + return imi.getSubtypeAt(subtypeId); + } + + // If there are no selected subtypes, the framework will try to find the most applicable + // subtype from explicitly or implicitly enabled subtypes. + final List<InputMethodSubtype> subtypes = settings.getEnabledInputMethodSubtypeList(imi, + true); + if (subtypes.isEmpty()) { + return currentSubtype; + } + // If there is only one explicitly or implicitly enabled subtype, + // just returns it. + if (subtypes.size() == 1) { + return subtypes.get(0); + } + final String locale = SystemLocaleWrapper.get(userId).get(0).toString(); + final var subtype = findLastResortApplicableSubtype(subtypes, SUBTYPE_MODE_KEYBOARD, locale, + true); + if (subtype != null) { + return subtype; + } + return findLastResortApplicableSubtype(subtypes, null, locale, true); + } } diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index f03c639c103c..d9e22c5a270f 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -28,6 +28,7 @@ import static android.Manifest.permission.READ_PHONE_STATE; import static android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE; import static android.app.ActivityManager.PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK; import static android.app.ActivityManager.PROCESS_CAPABILITY_USER_RESTRICTED_NETWORK; +import static android.app.ActivityManager.PROCESS_STATE_LAST_ACTIVITY; import static android.app.ActivityManager.PROCESS_STATE_UNKNOWN; import static android.app.ActivityManager.isProcStateConsideredInteraction; import static android.app.ActivityManager.printCapabilitiesSummary; @@ -523,6 +524,12 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { */ private boolean mUseMeteredFirewallChains; + /** + * Whether or not sensitive process states and non-sensitive process-states have different + * delays before network is blocked after transitioning to background. + */ + private boolean mUseDifferentDelaysForBackgroundChain; + // See main javadoc for instructions on how to use these locks. final Object mUidRulesFirstLock = new Object(); final Object mNetworkPoliciesSecondLock = new Object(); @@ -552,10 +559,43 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { * {@link NetworkPolicyManager#BACKGROUND_THRESHOLD_STATE} will lose network access. * The delay is meant to prevent churn due to quick process-state changes. * Note that there is no delay while granting network access. + * + * This is only used when the flag {@link #mUseDifferentDelaysForBackgroundChain} is disabled. */ @VisibleForTesting long mBackgroundRestrictionDelayMs = TimeUnit.SECONDS.toMillis(5); + /** + * Short delay after which a uid going into a process state having priority equal to + * {@link NetworkPolicyManager#BACKGROUND_THRESHOLD_STATE} or lower will lose network access. + * + * This will apply to apps that should be fine with losing network access immediately. + * It is only meant as a debounce to prevent churn due to quick process-state changes. + * Note that there is no delay while granting network access. + * + * This is only used when the flag {@link #mUseDifferentDelaysForBackgroundChain} is enabled. + */ + @VisibleForTesting + long mBackgroundRestrictionShortDelayMs = TimeUnit.SECONDS.toMillis(2); + + /** + * Long delay after which a uid going into a process state having priority equal to + * {@link NetworkPolicyManager#BACKGROUND_THRESHOLD_STATE} or lower will lose network access. + * + * Unlike {@link #mBackgroundRestrictionShortDelayMs}, this is meant to be applied to apps + * in sensitive proc-states like {@link ActivityManager#PROCESS_STATE_TOP_SLEEPING} and + * {@link ActivityManager#PROCESS_STATE_LAST_ACTIVITY}, where the user may switch to this app + * before this period and any latency in granting network access before resuming app activities + * may degrade experience. + * + * This is only used when the flag {@link #mUseDifferentDelaysForBackgroundChain} is enabled. + */ + @VisibleForTesting + long mBackgroundRestrictionLongDelayMs = TimeUnit.SECONDS.toMillis(20); + + @GuardedBy("mUidRulesFirstLock") + private long mNextProcessBackgroundUidsTime = Long.MAX_VALUE; + /** Defined network policies. */ @GuardedBy("mNetworkPoliciesSecondLock") final ArrayMap<NetworkTemplate, NetworkPolicy> mNetworkPolicy = new ArrayMap<>(); @@ -1007,6 +1047,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class); mUseMeteredFirewallChains = Flags.useMeteredFirewallChains(); + mUseDifferentDelaysForBackgroundChain = Flags.useDifferentDelaysForBackgroundChain(); synchronized (mUidRulesFirstLock) { synchronized (mNetworkPoliciesSecondLock) { @@ -1241,11 +1282,21 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { // different chains may change. return true; } - if (mBackgroundNetworkRestricted && (previousProcState >= BACKGROUND_THRESHOLD_STATE) + if (mBackgroundNetworkRestricted) { + if ((previousProcState >= BACKGROUND_THRESHOLD_STATE) != (newProcState >= BACKGROUND_THRESHOLD_STATE)) { - // Proc-state change crossed BACKGROUND_THRESHOLD_STATE: Network rules for the - // BACKGROUND chain may change. - 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; @@ -4045,6 +4096,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { + mBackgroundNetworkRestricted); fout.println(Flags.FLAG_USE_METERED_FIREWALL_CHAINS + ": " + mUseMeteredFirewallChains); + fout.println(Flags.FLAG_USE_DIFFERENT_DELAYS_FOR_BACKGROUND_CHAIN + ": " + + mUseDifferentDelaysForBackgroundChain); fout.println(); fout.println("mRestrictBackgroundLowPowerMode: " + mRestrictBackgroundLowPowerMode); @@ -4188,20 +4241,34 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { 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); + if (mBackgroundNetworkRestricted) { + 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.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.decreaseIndent(); + } + fout.println(); } final SparseBooleanArray knownUids = new SparseBooleanArray(); @@ -4337,6 +4404,15 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { || isProcStateAllowedNetworkWhileBackground(mUidState.get(uid)); } + private long getBackgroundTransitioningDelay(int procState) { + if (mUseDifferentDelaysForBackgroundChain) { + return procState <= PROCESS_STATE_LAST_ACTIVITY ? mBackgroundRestrictionLongDelayMs + : mBackgroundRestrictionShortDelayMs; + } else { + return mBackgroundRestrictionDelayMs; + } + } + /** * Process state of UID changed; if needed, will trigger * {@link #updateRulesForDataUsageRestrictionsUL(int)} and @@ -4387,19 +4463,41 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { mBackgroundTransitioningUids.delete(uid); updateRuleForBackgroundUL(uid); updatePowerRestrictionRules = true; - } else if (wasAllowed && !isAllowed) { + } else if (!isAllowed) { + final int transitionIdx = mBackgroundTransitioningUids.indexOfKey(uid); final long completionTimeMs = SystemClock.uptimeMillis() - + mBackgroundRestrictionDelayMs; - if (mBackgroundTransitioningUids.indexOfKey(uid) < 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); + + 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; + } } - if (!mHandler.hasMessages(MSG_PROCESS_BACKGROUND_TRANSITIONING_UIDS)) { - // Many uids may be in this "transitioning" state at the same time, so - // using one message at a time to avoid congestion in the MessageQueue. + 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; } } } @@ -5750,10 +5848,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { updateRuleForBackgroundUL(uid); updateRulesForPowerRestrictionsUL(uid, false); } - } - if (nextCheckTime < Long.MAX_VALUE) { - mHandler.sendEmptyMessageAtTime(MSG_PROCESS_BACKGROUND_TRANSITIONING_UIDS, - nextCheckTime); + mNextProcessBackgroundUidsTime = nextCheckTime; + if (nextCheckTime < Long.MAX_VALUE) { + mHandler.sendEmptyMessageAtTime( + MSG_PROCESS_BACKGROUND_TRANSITIONING_UIDS, nextCheckTime); + } } return true; } diff --git a/services/core/java/com/android/server/net/flags.aconfig b/services/core/java/com/android/server/net/flags.aconfig index e986dd81b94b..586baf022897 100644 --- a/services/core/java/com/android/server/net/flags.aconfig +++ b/services/core/java/com/android/server/net/flags.aconfig @@ -17,3 +17,13 @@ flag { purpose: PURPOSE_BUGFIX } } + +flag { + name: "use_different_delays_for_background_chain" + namespace: "backstage_power" + description: "Grant longer grace periods for sensitive process-states before blocking network using the background chain" + bug: "323963467" + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 60147ef2af90..a8adc06f1860 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -284,6 +284,7 @@ import android.service.notification.NotificationRecordProto; import android.service.notification.NotificationServiceDumpProto; import android.service.notification.NotificationStats; import android.service.notification.StatusBarNotification; +import android.service.notification.ZenDeviceEffects; import android.service.notification.ZenModeConfig; import android.service.notification.ZenModeProto; import android.service.notification.ZenPolicy; @@ -5508,6 +5509,14 @@ public class NotificationManagerService extends SystemService { } @Override + public void setManualZenRuleDeviceEffects(ZenDeviceEffects effects) throws RemoteException { + checkCallerIsSystem(); + + mZenModeHelper.setManualZenRuleDeviceEffects(effects, computeZenOrigin(true), + "Update manual mode non-policy settings", Binder.getCallingUid()); + } + + @Override public boolean updateAutomaticZenRule(String id, AutomaticZenRule automaticZenRule, boolean fromUser) throws RemoteException { validateAutomaticZenRule(id, automaticZenRule); diff --git a/services/core/java/com/android/server/notification/ZenModeConditions.java b/services/core/java/com/android/server/notification/ZenModeConditions.java index 86aa2d8b0a10..02b5f973d6b1 100644 --- a/services/core/java/com/android/server/notification/ZenModeConditions.java +++ b/services/core/java/com/android/server/notification/ZenModeConditions.java @@ -68,16 +68,17 @@ public class ZenModeConditions implements ConditionProviders.Callback { public void evaluateConfig(ZenModeConfig config, ComponentName trigger, boolean processSubscriptions) { if (config == null) return; - if (config.manualRule != null && config.manualRule.condition != null + if (!android.app.Flags.modesUi() && config.manualRule != null + && config.manualRule.condition != null && !config.manualRule.isTrueOrUnknown()) { if (DEBUG) Log.d(TAG, "evaluateConfig: clearing manual rule"); config.manualRule = null; } final ArraySet<Uri> current = new ArraySet<>(); - evaluateRule(config.manualRule, current, null, processSubscriptions); + evaluateRule(config.manualRule, current, null, processSubscriptions, true); for (ZenRule automaticRule : config.automaticRules.values()) { if (automaticRule.component != null) { - evaluateRule(automaticRule, current, trigger, processSubscriptions); + evaluateRule(automaticRule, current, trigger, processSubscriptions, false); updateSnoozing(automaticRule); } } @@ -131,7 +132,7 @@ public class ZenModeConditions implements ConditionProviders.Callback { // Only valid for CPS backed rules private void evaluateRule(ZenRule rule, ArraySet<Uri> current, ComponentName trigger, - boolean processSubscriptions) { + boolean processSubscriptions, boolean isManual) { if (rule == null || rule.conditionId == null) return; if (rule.configurationActivity != null) return; final Uri id = rule.conditionId; @@ -153,8 +154,10 @@ public class ZenModeConditions implements ConditionProviders.Callback { } // empty rule? disable and bail early if (rule.component == null && rule.enabler == null) { - Log.w(TAG, "No component found for automatic rule: " + rule.conditionId); - rule.enabled = false; + if (!android.app.Flags.modesUi() || (android.app.Flags.modesUi() && !isManual)) { + Log.w(TAG, "No component found for automatic rule: " + rule.conditionId); + rule.enabled = false; + } return; } if (current != null) { diff --git a/services/core/java/com/android/server/notification/ZenModeEventLogger.java b/services/core/java/com/android/server/notification/ZenModeEventLogger.java index 418eacc7ca65..ec5d96df3430 100644 --- a/services/core/java/com/android/server/notification/ZenModeEventLogger.java +++ b/services/core/java/com/android/server/notification/ZenModeEventLogger.java @@ -18,8 +18,8 @@ package com.android.server.notification; import static android.app.NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND; import static android.provider.Settings.Global.ZEN_MODE_OFF; -import static android.service.notification.NotificationServiceProto.CHANNEL_POLICY_PRIORITY; import static android.service.notification.NotificationServiceProto.CHANNEL_POLICY_NONE; +import static android.service.notification.NotificationServiceProto.CHANNEL_POLICY_PRIORITY; import static android.service.notification.NotificationServiceProto.RULE_TYPE_AUTOMATIC; import static android.service.notification.NotificationServiceProto.RULE_TYPE_MANUAL; import static android.service.notification.NotificationServiceProto.RULE_TYPE_UNKNOWN; @@ -352,8 +352,10 @@ class ZenModeEventLogger { ZenModeDiff.RuleDiff manualDiff = diff.getManualRuleDiff(); if (manualDiff != null && manualDiff.hasDiff()) { // a diff in the manual rule doesn't *necessarily* mean that it's responsible for - // the change -- only if it's been added or removed. - if (manualDiff.wasAdded() || manualDiff.wasRemoved()) { + // the change -- only if it's been added or removed or updated. + if (manualDiff.wasAdded() || manualDiff.wasRemoved() + || (Flags.modesUi() + && (manualDiff.becameActive() || manualDiff.becameInactive()))) { return RULE_TYPE_MANUAL; } } @@ -391,10 +393,8 @@ class ZenModeEventLogger { if (config == null) { return rules; } - - if (config.manualRule != null) { - // If the manual rule is non-null, then it's active. We make a copy and set the rule - // type so that the correct value gets logged. + if (config.isManualActive()) { + // We make a copy and set the rule type so that the correct value gets logged. ZenRule rule = config.manualRule.copy(); rule.type = ACTIVE_RULE_TYPE_MANUAL; rules.add(rule); @@ -592,10 +592,10 @@ class ZenModeEventLogger { // This applies to both call and message senders, but not conversation senders, // where they use the same enum values. proto.write(DNDPolicyProto.ALLOW_CALLS_FROM, - ZenAdapters.notificationPolicySendersToZenPolicyPeopleType( + ZenAdapters.prioritySendersToPeopleType( mNewPolicy.allowCallsFrom())); proto.write(DNDPolicyProto.ALLOW_MESSAGES_FROM, - ZenAdapters.notificationPolicySendersToZenPolicyPeopleType( + ZenAdapters.prioritySendersToPeopleType( mNewPolicy.allowMessagesFrom())); proto.write(DNDPolicyProto.ALLOW_CONVERSATIONS_FROM, mNewPolicy.allowConversationsFrom()); diff --git a/services/core/java/com/android/server/notification/ZenModeHelper.java b/services/core/java/com/android/server/notification/ZenModeHelper.java index 454bd20c59e2..267291c1a970 100644 --- a/services/core/java/com/android/server/notification/ZenModeHelper.java +++ b/services/core/java/com/android/server/notification/ZenModeHelper.java @@ -24,6 +24,10 @@ 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.service.notification.Condition.SOURCE_UNKNOWN; +import static android.service.notification.Condition.SOURCE_USER_ACTION; +import static android.service.notification.Condition.STATE_FALSE; +import static android.service.notification.Condition.STATE_TRUE; import static android.service.notification.NotificationServiceProto.ROOT_CONFIG; import static android.service.notification.ZenModeConfig.UPDATE_ORIGIN_APP; import static android.service.notification.ZenModeConfig.UPDATE_ORIGIN_INIT; @@ -216,7 +220,9 @@ public class ZenModeHelper { mAppOps = context.getSystemService(AppOpsManager.class); mNotificationManager = context.getSystemService(NotificationManager.class); - mDefaultConfig = readDefaultConfig(mContext.getResources()); + mDefaultConfig = Flags.modesUi() + ? ZenModeConfig.getDefaultConfig() + : readDefaultConfig(mContext.getResources()); updateDefaultConfigAutomaticRules(); if (Flags.modesApi()) { updateDefaultAutomaticRulePolicies(); @@ -612,7 +618,7 @@ public class ZenModeHelper { if (rule != null) { Condition deactivated = new Condition(rule.conditionId, mContext.getString(R.string.zen_mode_implicit_deactivated), - Condition.STATE_FALSE); + STATE_FALSE); setAutomaticZenRuleStateLocked(newConfig, Collections.singletonList(rule), deactivated, UPDATE_ORIGIN_APP, callingUid); } @@ -627,8 +633,7 @@ public class ZenModeHelper { // would apply if changing the global interruption filter. We only do this // for newly created rules, as existing rules have a pre-existing policy // (whether initialized here or set via app or user). - rule.zenPolicy = mConfig.toZenPolicy(); - + rule.zenPolicy = mConfig.getZenPolicy().copy(); newConfig.automaticRules.put(rule.id, rule); } // If the user has changed the rule's *zenMode*, then don't let app overwrite it. @@ -639,7 +644,7 @@ public class ZenModeHelper { rule.snoozing = false; rule.condition = new Condition(rule.conditionId, mContext.getString(R.string.zen_mode_implicit_activated), - Condition.STATE_TRUE); + STATE_TRUE); setConfigLocked(newConfig, /* triggeringComponent= */ null, UPDATE_ORIGIN_APP, "applyGlobalZenModeAsImplicitZenRule", callingUid); @@ -687,7 +692,7 @@ public class ZenModeHelper { // would take effect if changing the global policy. // Note that NotificationManager.Policy cannot have any unset priority // categories, but *can* have unset visual effects, which is why we do this. - newZenPolicy = mConfig.toZenPolicy().overwrittenWith(newZenPolicy); + newZenPolicy = mConfig.getZenPolicy().overwrittenWith(newZenPolicy); } updatePolicy( rule, @@ -878,7 +883,7 @@ public class ZenModeHelper { if (rule == null || !canManageAutomaticZenRule(rule)) { return Condition.STATE_UNKNOWN; } - return rule.condition != null ? rule.condition.state : Condition.STATE_FALSE; + return rule.condition != null ? rule.condition.state : STATE_FALSE; } } @@ -929,7 +934,7 @@ public class ZenModeHelper { Condition condition, @ConfigChangeOrigin int origin, int callingUid) { if (rules == null || rules.isEmpty()) return; - if (Flags.modesApi() && condition.source == Condition.SOURCE_USER_ACTION) { + if (Flags.modesApi() && condition.source == SOURCE_USER_ACTION) { origin = UPDATE_ORIGIN_USER; // Although coming from app, it's actually a user action. } @@ -1285,7 +1290,7 @@ public class ZenModeHelper { if (isNew) { // Newly created rule with no provided policy; fill in with the default. zenRule.zenPolicy = - Flags.modesUi() ? mDefaultConfig.toZenPolicy() : mConfig.toZenPolicy(); + Flags.modesUi() ? mDefaultConfig.getZenPolicy() : mConfig.getZenPolicy(); return true; } // Otherwise, a null policy means no policy changes, so we can stop here. @@ -1296,7 +1301,7 @@ public class ZenModeHelper { // fields in the bitmask should be marked as updated. ZenPolicy oldPolicy = zenRule.zenPolicy != null ? zenRule.zenPolicy - : (Flags.modesUi() ? mDefaultConfig.toZenPolicy() : mConfig.toZenPolicy()); + : (Flags.modesUi() ? mDefaultConfig.getZenPolicy() : mConfig.getZenPolicy()); // If this is updating a rule rather than creating a new one, keep any fields from the // old policy if they are unspecified in the new policy. For newly created rules, oldPolicy @@ -1524,9 +1529,15 @@ public class ZenModeHelper { + " conditionId=" + conditionId + " reason=" + reason + " setRingerMode=" + setRingerMode); newConfig = mConfig.copy(); - if (zenMode == Global.ZEN_MODE_OFF) { - newConfig.manualRule = null; - if (!Flags.modesUi() || origin != UPDATE_ORIGIN_USER) { + if (Flags.modesUi()) { + newConfig.manualRule.enabler = caller; + newConfig.manualRule.conditionId = conditionId != null ? conditionId : Uri.EMPTY; + newConfig.manualRule.pkg = PACKAGE_ANDROID; + newConfig.manualRule.zenMode = zenMode; + newConfig.manualRule.condition = new Condition(newConfig.manualRule.conditionId, "", + zenMode == Global.ZEN_MODE_OFF ? STATE_FALSE : STATE_TRUE, + origin == UPDATE_ORIGIN_USER ? SOURCE_USER_ACTION : SOURCE_UNKNOWN); + if (zenMode == Global.ZEN_MODE_OFF && origin != UPDATE_ORIGIN_USER) { // User deactivation of DND means just turning off the manual DND rule. // For API calls (different origin) keep old behavior of snoozing all rules. for (ZenRule automaticRule : newConfig.automaticRules.values()) { @@ -1536,20 +1547,51 @@ public class ZenModeHelper { } } } else { - final ZenRule newRule = new ZenRule(); - newRule.enabled = true; - newRule.zenMode = zenMode; - newRule.conditionId = conditionId; - newRule.enabler = caller; - if (Flags.modesApi()) { - newRule.allowManualInvocation = true; + if (zenMode == Global.ZEN_MODE_OFF) { + newConfig.manualRule = null; + // User deactivation of DND means just turning off the manual DND rule. + // For API calls (different origin) keep old behavior of snoozing all rules. + for (ZenRule automaticRule : newConfig.automaticRules.values()) { + if (automaticRule.isAutomaticActive()) { + automaticRule.snoozing = true; + } + } + + } else { + final ZenRule newRule = new ZenRule(); + newRule.enabled = true; + newRule.zenMode = zenMode; + newRule.conditionId = conditionId; + newRule.enabler = caller; + if (Flags.modesApi()) { + newRule.allowManualInvocation = true; + } + newConfig.manualRule = newRule; } - newConfig.manualRule = newRule; } setConfigLocked(newConfig, origin, reason, null, setRingerMode, callingUid); } } + public void setManualZenRuleDeviceEffects(ZenDeviceEffects deviceEffects, + @ConfigChangeOrigin int origin, String reason, int callingUid) { + if (!Flags.modesUi()) { + return; + } + ZenModeConfig newConfig; + synchronized (mConfigLock) { + if (mConfig == null) return; + if (DEBUG) Log.d(TAG, "updateManualRule " + deviceEffects + + " reason=" + reason + + " callingUid=" + callingUid); + newConfig = mConfig.copy(); + + newConfig.manualRule.pkg = PACKAGE_ANDROID; + newConfig.manualRule.zenDeviceEffects = deviceEffects; + setConfigLocked(newConfig, origin, reason, null, true, callingUid); + } + } + void dump(ProtoOutputStream proto) { proto.write(ZenModeProto.ZEN_MODE, mZenMode); synchronized (mConfigLock) { @@ -1558,7 +1600,7 @@ public class ZenModeHelper { } for (ZenRule rule : mConfig.automaticRules.values()) { if (rule.enabled && rule.condition != null - && rule.condition.state == Condition.STATE_TRUE + && rule.condition.state == STATE_TRUE && !rule.snoozing) { rule.dumpDebug(proto, ZenModeProto.ENABLED_ACTIVE_CONDITIONS); } @@ -1592,33 +1634,7 @@ public class ZenModeHelper { private static void dump(PrintWriter pw, String prefix, String var, ZenModeConfig config) { pw.print(prefix); pw.print(var); pw.print('='); - if (config == null) { - pw.println(config); - return; - } - pw.printf("allow(alarms=%b,media=%b,system=%b,calls=%b,callsFrom=%s,repeatCallers=%b," - + "messages=%b,messagesFrom=%s,conversations=%b,conversationsFrom=%s," - + "events=%b,reminders=%b", - config.allowAlarms, config.allowMedia, config.allowSystem, - config.allowCalls, ZenModeConfig.sourceToString(config.allowCallsFrom), - config.allowRepeatCallers, config.allowMessages, - ZenModeConfig.sourceToString(config.allowMessagesFrom), - config.allowConversations, - ZenPolicy.conversationTypeToString(config.allowConversationsFrom), - config.allowEvents, config.allowReminders); - if (Flags.modesApi()) { - pw.printf(",priorityChannels=%b", config.allowPriorityChannels); - } - pw.printf(")\n"); - pw.print(prefix); - pw.printf(" disallow(visualEffects=%s)\n", config.suppressedVisualEffects); - pw.print(prefix); pw.print(" manualRule="); pw.println(config.manualRule); - if (config.automaticRules.isEmpty()) return; - final int N = config.automaticRules.size(); - for (int i = 0; i < N; i++) { - pw.print(prefix); pw.print(i == 0 ? " automaticRules=" : " "); - pw.println(config.automaticRules.valueAt(i)); - } + pw.println(config); } public void readXml(TypedXmlPullParser parser, boolean forRestore, int userId) @@ -1629,7 +1645,9 @@ public class ZenModeHelper { if (config != null) { if (forRestore) { config.user = userId; - config.manualRule = null; // don't restore the manual rule + if (!Flags.modesUi()) { + config.manualRule = null; // don't restore the manual rule + } } // booleans to determine whether to reset the rules to the default rules @@ -1653,7 +1671,7 @@ public class ZenModeHelper { // rules with null ZenPolicies explicitly as a copy of the global policy. if (Flags.modesApi() && config.version < ZenModeConfig.XML_VERSION_MODES_API) { // Keep the manual ("global") policy that from config. - ZenPolicy manualRulePolicy = config.toZenPolicy(); + ZenPolicy manualRulePolicy = config.getZenPolicy(); if (automaticRule.zenPolicy == null) { automaticRule.zenPolicy = manualRulePolicy; } else { @@ -1842,7 +1860,7 @@ public class ZenModeHelper { */ @VisibleForTesting protected ZenPolicy getDefaultZenPolicy() { - return mDefaultConfig.toZenPolicy(); + return mDefaultConfig.getZenPolicy(); } @GuardedBy("mConfigLock") @@ -2008,7 +2026,7 @@ public class ZenModeHelper { private int computeZenMode() { synchronized (mConfigLock) { if (mConfig == null) return Global.ZEN_MODE_OFF; - if (mConfig.manualRule != null) return mConfig.manualRule.zenMode; + if (mConfig.isManualActive()) return mConfig.manualRule.zenMode; int zen = Global.ZEN_MODE_OFF; for (ZenRule automaticRule : mConfig.automaticRules.values()) { if (automaticRule.isAutomaticActive()) { @@ -2047,19 +2065,19 @@ public class ZenModeHelper { if (Flags.modesApi()) { if (useManualConfig) { // manual rule is configured using the settings stored directly in mConfig - policy.apply(mConfig.toZenPolicy()); + policy.apply(mConfig.getZenPolicy()); } else { // under modes_api flag, an active automatic rule with no specified policy // inherits the device default settings as stored in mDefaultConfig. While the // rule's policy fields should be set upon creation, this is a fallback to // catch any that may have fallen through the cracks. Log.wtf(TAG, "active automatic rule found with no specified policy: " + rule); - policy.apply( - Flags.modesUi() ? mDefaultConfig.toZenPolicy() : mConfig.toZenPolicy()); + policy.apply(Flags.modesUi() + ? mDefaultConfig.getZenPolicy() : mConfig.getZenPolicy()); } } else { - // active rule with no specified policy inherits the global config settings - policy.apply(mConfig.toZenPolicy()); + // active rule with no specified policy inherits the manual rule config settings + policy.apply(mConfig.getZenPolicy()); } } } @@ -2071,7 +2089,7 @@ public class ZenModeHelper { if (mConfig == null) return; ZenPolicy policy = new ZenPolicy(); ZenDeviceEffects.Builder deviceEffectsBuilder = new ZenDeviceEffects.Builder(); - if (mConfig.manualRule != null) { + if (mConfig.isManualActive()) { applyCustomPolicy(policy, mConfig.manualRule, true); if (Flags.modesApi()) { deviceEffectsBuilder.add(mConfig.manualRule.zenDeviceEffects); @@ -2154,7 +2172,7 @@ public class ZenModeHelper { // Should be checked before calling, but just in case. return; } - ZenPolicy defaultPolicy = mDefaultConfig.toZenPolicy(); + ZenPolicy defaultPolicy = mDefaultConfig.getZenPolicy(); for (ZenRule rule : mDefaultConfig.automaticRules.values()) { if (ZenModeConfig.DEFAULT_RULE_IDS.contains(rule.id) && rule.zenPolicy == null) { rule.zenPolicy = defaultPolicy.copy(); @@ -2335,17 +2353,17 @@ public class ZenModeHelper { final ZenModeConfig config = mConfigs.valueAt(i); events.add(FrameworkStatsLog.buildStatsEvent(DND_MODE_RULE, /* optional int32 user = 1 */ user, - /* optional bool enabled = 2 */ config.manualRule != null, + /* optional bool enabled = 2 */ config.isManualActive(), /* optional bool channels_bypassing = 3 */ config.areChannelsBypassingDnd, /* optional LoggedZenMode zen_mode = 4 */ ROOT_CONFIG, /* optional string id = 5 */ "", // empty for root config /* optional int32 uid = 6 */ Process.SYSTEM_UID, // system owns root config - /* optional DNDPolicyProto policy = 7 */ config.toZenPolicy().toProto(), + /* optional DNDPolicyProto policy = 7 */ config.getZenPolicy().toProto(), /* optional int32 rule_modified_fields = 8 */ 0, /* optional int32 policy_modified_fields = 9 */ 0, /* optional int32 device_effects_modified_fields = 10 */ 0, /* optional ActiveRuleType rule_type = 11 */ TYPE_UNKNOWN)); - if (config.manualRule != null) { + if (config.isManualActive()) { ruleToProtoLocked(user, config.manualRule, true, events); } for (ZenRule rule : config.automaticRules.values()) { diff --git a/services/core/java/com/android/server/pm/KillAppBlocker.java b/services/core/java/com/android/server/pm/KillAppBlocker.java index e2901c39e612..7f5ad9d32e6b 100644 --- a/services/core/java/com/android/server/pm/KillAppBlocker.java +++ b/services/core/java/com/android/server/pm/KillAppBlocker.java @@ -83,13 +83,13 @@ final class KillAppBlocker { } } - void waitAppProcessGone(ActivityManagerInternal mAmi, Computer snapshot, + void waitAppProcessGone(ActivityManagerInternal ami, Computer snapshot, UserManagerService userManager, String packageName) { if (!mRegistered) { return; } synchronized (this) { - if (mAmi != null) { + if (ami != null) { int[] users = userManager.getUserIds(); for (int i = 0; i < users.length; i++) { @@ -97,12 +97,16 @@ final class KillAppBlocker { final int uid = snapshot.getPackageUidInternal( packageName, MATCH_ALL, userId, Process.SYSTEM_UID); if (uid != INVALID_UID) { - if (mAmi.getUidProcessState(uid) != PROCESS_STATE_NONEXISTENT) { + if (ami.getUidProcessState(uid) != PROCESS_STATE_NONEXISTENT) { mActiveUids.add(uid); } } } } + if (mActiveUids.size() == 0) { + // no active uid + return; + } } try { diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java index cd1d7996fbac..ea71953d7cb3 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java @@ -165,6 +165,7 @@ import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Optional; import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; @@ -368,18 +369,32 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt return false; } + int userId = UserHandle.getUserId(uid); + + boolean isInSetup = + getSecureInt(Settings.Secure.USER_SETUP_COMPLETE, userId) + .map(setupState -> setupState == 0) + .orElse(false); + if (isInSetup) { + return true; + } + + boolean isInDeferredSetup = + getSecureInt(Settings.Secure.USER_SETUP_PERSONALIZATION_STATE, userId) + .map(state -> + state == Settings.Secure.USER_SETUP_PERSONALIZATION_STARTED) + .orElse(false); + return isInDeferredSetup; + } + + private Optional<Integer> getSecureInt(String settingName, int userId) { try { - int userId = UserHandle.getUserId(uid); - boolean isInSetup = Settings.Secure.getIntForUser(mContext.getContentResolver(), - Settings.Secure.USER_SETUP_COMPLETE, userId) == 0; - boolean isInDeferredSetup = Settings.Secure.getIntForUser( - mContext.getContentResolver(), - Settings.Secure.USER_SETUP_PERSONALIZATION_STATE, userId) - == Settings.Secure.USER_SETUP_PERSONALIZATION_STARTED; - return isInSetup || isInDeferredSetup; + return Optional.of( + Settings.Secure.getIntForUser( + mContext.getContentResolver(), settingName, userId)); } catch (Settings.SettingNotFoundException e) { - Slog.w(LOG_TAG, "Failed to check if the user is in restore: " + e); - return false; + Slog.i(LOG_TAG, "Setting " + settingName + " not found", e); + return Optional.empty(); } } diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 12e51809fabf..4e224a3cb38a 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -2882,6 +2882,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { } ContentResolver resolver = mContext.getContentResolver(); boolean updateRotation = false; + boolean updateKidsModeSettings = false; + final boolean kidsModeEnabled; synchronized (mLock) { mEndcallBehavior = Settings.System.getIntForUser(resolver, Settings.System.END_BUTTON_BEHAVIOR, @@ -2995,20 +2997,23 @@ public class PhoneWindowManager implements WindowManagerPolicy { Secure.STYLUS_BUTTONS_ENABLED, 1, UserHandle.USER_CURRENT) == 1; mInputManagerInternal.setStylusButtonMotionEventsEnabled(mStylusButtonsEnabled); - final boolean kidsModeEnabled = Settings.Secure.getIntForUser(resolver, + kidsModeEnabled = Settings.Secure.getIntForUser(resolver, Settings.Secure.NAV_BAR_KIDS_MODE, 0, UserHandle.USER_CURRENT) == 1; if (mKidsModeEnabled != kidsModeEnabled) { mKidsModeEnabled = kidsModeEnabled; - updateKidsModeSettings(); + updateKidsModeSettings = true; } } + if (updateKidsModeSettings) { + updateKidsModeSettings(kidsModeEnabled); + } if (updateRotation) { updateRotation(true); } } - private void updateKidsModeSettings() { - if (mKidsModeEnabled) { + private void updateKidsModeSettings(boolean kidsModeEnabled) { + if (kidsModeEnabled) { // Needed since many Kids apps aren't optimised to support both orientations and it // will be hard for kids to understand the app compat mode. // TODO(229961548): Remove ignoreOrientationRequest exception for Kids Mode once diff --git a/services/core/java/com/android/server/power/hint/HintManagerService.java b/services/core/java/com/android/server/power/hint/HintManagerService.java index df502eb08be2..06595acfd012 100644 --- a/services/core/java/com/android/server/power/hint/HintManagerService.java +++ b/services/core/java/com/android/server/power/hint/HintManagerService.java @@ -19,6 +19,7 @@ package com.android.server.power.hint; import static android.os.Flags.adpfUseFmqChannel; import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR; +import static com.android.server.power.hint.Flags.adpfSessionTag; import static com.android.server.power.hint.Flags.powerhintThreadCleanup; import android.annotation.NonNull; @@ -28,6 +29,8 @@ import android.app.ActivityManagerInternal; import android.app.StatsManager; import android.app.UidObserver; import android.content.Context; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager; import android.hardware.power.ChannelConfig; import android.hardware.power.IPower; import android.hardware.power.SessionConfig; @@ -130,6 +133,7 @@ public final class HintManagerService extends SystemService { private final IPower mPowerHal; private int mPowerHalVersion; + private final PackageManager mPackageManager; private static final String PROPERTY_SF_ENABLE_CPU_HINT = "debug.sf.enable_adpf_cpu_hint"; private static final String PROPERTY_HWUI_ENABLE_HINT_MANAGER = "debug.hwui.use_hint_manager"; @@ -151,6 +155,11 @@ public final class HintManagerService extends SystemService { mCleanUpHandler = null; mNonIsolatedTids = null; } + if (adpfSessionTag()) { + mPackageManager = mContext.getPackageManager(); + } else { + mPackageManager = null; + } mActiveSessions = new ArrayMap<>(); mChannelMap = new ArrayMap<>(); mNativeWrapper = injector.createNativeWrapper(); @@ -819,6 +828,25 @@ public final class HintManagerService extends SystemService { throw new SecurityException(errMsg); } + if (adpfSessionTag() && tag == SessionTag.APP) { + // If the category of the app is a game, + // we change the session tag to SessionTag.GAME + // as it was not previously classified + switch (getUidApplicationCategory(callingUid)) { + case ApplicationInfo.CATEGORY_GAME: + tag = SessionTag.GAME; + break; + case ApplicationInfo.CATEGORY_UNDEFINED: + // We use CATEGORY_UNDEFINED to filter the case when + // PackageManager.NameNotFoundException is caught, + // which should not happen. + tag = SessionTag.APP; + break; + default: + tag = SessionTag.APP; + } + } + Long halSessionPtr = null; if (mConfigCreationSupport.get()) { try { @@ -857,7 +885,10 @@ public final class HintManagerService extends SystemService { } } - logPerformanceHintSessionAtom(callingUid, halSessionPtr, durationNanos, tids); + final long sessionId = config != null ? config.id : halSessionPtr; + logPerformanceHintSessionAtom( + callingUid, sessionId, durationNanos, tids, tag); + synchronized (mLock) { AppHintSession hs = new AppHintSession(callingUid, callingTgid, tids, token, halSessionPtr, durationNanos); @@ -944,9 +975,20 @@ public final class HintManagerService extends SystemService { } private void logPerformanceHintSessionAtom(int uid, long sessionId, - long targetDuration, int[] tids) { + long targetDuration, int[] tids, @SessionTag int sessionTag) { FrameworkStatsLog.write(FrameworkStatsLog.PERFORMANCE_HINT_SESSION_REPORTED, uid, - sessionId, targetDuration, tids.length); + sessionId, targetDuration, tids.length, sessionTag); + } + + private int getUidApplicationCategory(int uid) { + try { + final String packageName = mPackageManager.getNameForUid(uid); + final ApplicationInfo applicationInfo = + mPackageManager.getApplicationInfo(packageName, PackageManager.MATCH_ALL); + return applicationInfo.category; + } catch (PackageManager.NameNotFoundException e) { + return ApplicationInfo.CATEGORY_UNDEFINED; + } } } diff --git a/services/core/java/com/android/server/power/hint/TEST_MAPPING b/services/core/java/com/android/server/power/hint/TEST_MAPPING index ce6277d2f490..34c25c6eb360 100644 --- a/services/core/java/com/android/server/power/hint/TEST_MAPPING +++ b/services/core/java/com/android/server/power/hint/TEST_MAPPING @@ -10,16 +10,18 @@ "exclude-annotation": "androidx.test.filters.FlakyTest" } ] - }, + } + ], + "postsubmit": [ { "name": "CtsStatsdAtomHostTestCases", "options": [ {"exclude-annotation": "androidx.test.filters.FlakyTest"}, {"exclude-annotation": "org.junit.Ignore"}, - {"include-filter": "android.cts.statsdatom.powermanager"} + {"include-filter": "android.cts.statsdatom.performancehintmanager"} ], "file_patterns": [ - "(/|^)ThermalManagerService.java" + "(/|^)HintManagerService.java" ] } ] diff --git a/services/core/java/com/android/server/power/hint/flags.aconfig b/services/core/java/com/android/server/power/hint/flags.aconfig index 099774420d23..55afa05f66fe 100644 --- a/services/core/java/com/android/server/power/hint/flags.aconfig +++ b/services/core/java/com/android/server/power/hint/flags.aconfig @@ -7,3 +7,10 @@ flag { description: "Feature flag for auto PowerHintSession dead thread cleanup" bug: "296160319" } + +flag { + name: "adpf_session_tag" + namespace: "game" + description: "Feature flag for adding session tag to hint session atom" + bug: "345011125" +} diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index 78a6816593ae..5159fc4009f7 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -156,6 +156,7 @@ import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANG import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_SIZE_COMPAT_MODE; import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__STATE__NOT_LETTERBOXED; import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__STATE__NOT_VISIBLE; +import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM; import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; import static com.android.server.wm.ActivityRecord.State.DESTROYED; @@ -2982,7 +2983,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A void removeStartingWindowAnimation(boolean prepareAnimation) { mTransferringSplashScreenState = TRANSFER_SPLASH_SCREEN_IDLE; - if (task != null) { + if (mStartingData != null && task != null) { task.mSharedStartingData = null; } if (mStartingWindow == null) { @@ -6032,14 +6033,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A true /* activityChange */, true /* updateOomAdj */, true /* addPendingTopUid */); } - final ContentCaptureManagerInternal contentCaptureService = - LocalServices.getService(ContentCaptureManagerInternal.class); - if (contentCaptureService != null) { - contentCaptureService.notifyActivityEvent(mUserId, mActivityComponent, - ActivityEvent.TYPE_ACTIVITY_STARTED, - new ActivityId(getTask() != null ? getTask().mTaskId : INVALID_TASK_ID, - shareableActivityToken)); - } + mAtmService.mH.post(this::notifyActivityStartedToContentCaptureService); break; case PAUSED: mAtmService.updateBatteryStats(this, false); @@ -6071,6 +6065,22 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } } + private void notifyActivityStartedToContentCaptureService() { + final ContentCaptureManagerInternal contentCaptureService = + LocalServices.getService(ContentCaptureManagerInternal.class); + if (contentCaptureService != null) { + // For ACTIVITY_STARTED content capture is directly invoked to avoid persisting + // to UsageStats. + contentCaptureService.notifyActivityEvent(mUserId, mActivityComponent, + ActivityEvent.TYPE_ACTIVITY_STARTED, + new ActivityId(getTask() != null ? getTask().mTaskId : INVALID_TASK_ID, + shareableActivityToken)); + + contentCaptureService.sendActivityStartAssistData(mUserId, + shareableActivityToken, intent); + } + } + State getState() { return mState; } @@ -8689,7 +8699,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A rotation = mDisplayContent.getRotation(); } if (!mOptOutEdgeToEdge && (!mResolveConfigHint.mUseOverrideInsetsForConfig - || getCompatDisplayInsets() != null || isFloating(parentWindowingMode) + || getCompatDisplayInsets() != null + || (isFloating(parentWindowingMode) + // Check the windowing mode of activity as well in case it is switching + // between PiP and fullscreen. + && isFloating(inOutConfig.windowConfiguration.getWindowingMode())) || rotation == ROTATION_UNDEFINED)) { // If the insets configuration decoupled logic is not enabled for the app, or the app // already has a compat override, or the context doesn't contain enough info to diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java index 3c556bf7b126..6288a429af81 100644 --- a/services/core/java/com/android/server/wm/InsetsPolicy.java +++ b/services/core/java/com/android/server/wm/InsetsPolicy.java @@ -424,7 +424,9 @@ class InsetsPolicy { // Use task bounds to calculating rounded corners if the task is not floating. final InsetsState state = copyState ? new InsetsState(originalState) : originalState; - state.setRoundedCornerFrame(task.getBounds()); + state.setRoundedCornerFrame(token.isFixedRotationTransforming() + ? token.getFixedRotationTransformDisplayBounds() + : task.getBounds()); return state; } } diff --git a/services/incremental/Android.bp b/services/incremental/Android.bp index 957d7c352bdf..ce05c820cbbc 100644 --- a/services/incremental/Android.bp +++ b/services/incremental/Android.bp @@ -66,7 +66,6 @@ cc_defaults { "libprotobuf-cpp-lite", "service.incremental.proto", "libvold_binder", - "libc++fs", "libziparchive_for_incfs", ], shared_libs: [ diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index cfe4e17eb1be..107c2947c550 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -1196,11 +1196,12 @@ public final class SystemServer implements Dumpable { mSystemServiceManager.startService(RecoverySystemService.Lifecycle.class); t.traceEnd(); + // Initialize RescueParty. + RescueParty.registerHealthObserver(mSystemContext); if (!Flags.recoverabilityDetection()) { // Now that we have the bare essentials of the OS up and running, take // note that we just booted, which might send out a rescue party if // we're stuck in a runtime restart loop. - RescueParty.registerHealthObserver(mSystemContext); PackageWatchdog.getInstance(mSystemContext).noteBoot(); } @@ -2917,10 +2918,10 @@ public final class SystemServer implements Dumpable { t.traceEnd(); if (Flags.recoverabilityDetection()) { - // Now that we have the essential services needed for rescue party, initialize - // RescuParty. note that we just booted, which might send out a rescue party if - // we're stuck in a runtime restart loop. - RescueParty.registerHealthObserver(mSystemContext); + // Now that we have the essential services needed for mitigations, register the boot + // with package watchdog. + // Note that we just booted, which might send out a rescue party if we're stuck in a + // runtime restart loop. PackageWatchdog.getInstance(mSystemContext).noteBoot(); } diff --git a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt index 649955614851..78dbc60dbae0 100644 --- a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt +++ b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt @@ -533,8 +533,7 @@ class PermissionService(private val service: AccessCheckingService) : val packageState = packageManagerLocal.withFilteredSnapshot(Binder.getCallingUid(), userId).use { it.getPackageState(packageName) - } - ?: return PackageManager.PERMISSION_DENIED + } ?: return PackageManager.PERMISSION_DENIED val isPermissionGranted = service.getState { isPermissionGranted(packageState, userId, permissionName, deviceId) } @@ -1164,8 +1163,7 @@ class PermissionService(private val service: AccessCheckingService) : val packageState = packageManagerLocal.withFilteredSnapshot(Binder.getCallingUid(), userId).use { it.getPackageState(packageName) - } - ?: return false + } ?: return false service.getState { if (isPermissionGranted(packageState, userId, permissionName, deviceId)) { @@ -1216,8 +1214,7 @@ class PermissionService(private val service: AccessCheckingService) : val packageState = packageManagerLocal.withFilteredSnapshot(callingUid, userId).use { it.getPackageState(packageName) - } - ?: return false + } ?: return false val appId = packageState.appId if (UserHandle.getAppId(callingUid) != appId) { return false @@ -1546,8 +1543,7 @@ class PermissionService(private val service: AccessCheckingService) : val packageState = packageManagerLocal.withFilteredSnapshot(callingUid, userId).use { it.getPackageState(packageName) - } - ?: return null + } ?: return null val androidPackage = packageState.androidPackage ?: return null val isCallerPrivileged = @@ -1710,8 +1706,7 @@ class PermissionService(private val service: AccessCheckingService) : PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER, userId ) - ?.let { ArraySet(permissionNames).apply { this += it }.toList() } - ?: permissionNames + ?.let { ArraySet(permissionNames).apply { this += it }.toList() } ?: permissionNames setAllowlistedRestrictedPermissionsUnchecked( androidPackage, @@ -2753,27 +2748,25 @@ class PermissionService(private val service: AccessCheckingService) : ) { return false } - return try { - val contentResolver = context.contentResolver - val userId = UserHandle.getUserId(uid) - val isInSetup = - Settings.Secure.getIntForUser( - contentResolver, - Settings.Secure.USER_SETUP_COMPLETE, - userId - ) == 0 - val isInDeferredSetup = - Settings.Secure.getIntForUser( - contentResolver, - Settings.Secure.USER_SETUP_PERSONALIZATION_STATE, - userId - ) == Settings.Secure.USER_SETUP_PERSONALIZATION_STARTED - isInSetup || isInDeferredSetup + + val userId = UserHandle.getUserId(uid) + + val isInSetup = getSecureInt(Settings.Secure.USER_SETUP_COMPLETE, userId) == 0 + if (isInSetup) return true + + val isInDeferredSetup = + getSecureInt(Settings.Secure.USER_SETUP_PERSONALIZATION_STATE, userId) == + Settings.Secure.USER_SETUP_PERSONALIZATION_STARTED + return isInDeferredSetup + } + + private fun getSecureInt(settingName: String, userId: Int): Int? = + try { + Settings.Secure.getIntForUser(context.contentResolver, settingName, userId) } catch (e: Settings.SettingNotFoundException) { - Slog.w(LOG_TAG, "Failed to check if the user is in restore: $e") - false + Slog.i(LOG_TAG, "Setting $settingName not found", e) + null } - } } private class OnPermissionsChangeListeners(looper: Looper) : Handler(looper) { diff --git a/services/tests/displayservicetests/src/com/android/server/display/AutomaticBrightnessControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/AutomaticBrightnessControllerTest.java index d3efcb6e50b3..2a458c42b841 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/AutomaticBrightnessControllerTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/AutomaticBrightnessControllerTest.java @@ -52,7 +52,6 @@ import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; -import com.android.server.display.brightness.clamper.BrightnessClamperController; import com.android.server.display.config.HysteresisLevels; import com.android.server.display.feature.DisplayManagerFlags; import com.android.server.testutils.OffsettableClock; @@ -102,8 +101,6 @@ public class AutomaticBrightnessControllerTest { @Mock Handler mNoOpHandler; @Mock BrightnessRangeController mBrightnessRangeController; @Mock - BrightnessClamperController mBrightnessClamperController; - @Mock DisplayManagerFlags mDisplayManagerFlags; @Mock BrightnessThrottler mBrightnessThrottler; @@ -181,7 +178,7 @@ public class AutomaticBrightnessControllerTest { mContext, mBrightnessRangeController, mBrightnessThrottler, useHorizon ? AMBIENT_LIGHT_HORIZON_SHORT : 1, useHorizon ? AMBIENT_LIGHT_HORIZON_LONG : 10000, userLux, userNits, - mBrightnessClamperController, mDisplayManagerFlags + mDisplayManagerFlags ); when(mBrightnessRangeController.getCurrentBrightnessMax()).thenReturn( 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 d0eb83aa986b..211ab03813b3 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java @@ -2569,6 +2569,63 @@ public class DisplayManagerServiceTest { } @Test + public void testPowerOnAndOffInternalDisplay() { + manageDisplaysPermission(/* granted= */ true); + DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector); + DisplayManagerService.BinderService bs = displayManager.new BinderService(); + LogicalDisplayMapper logicalDisplayMapper = displayManager.getLogicalDisplayMapper(); + FakeDisplayManagerCallback callback = new FakeDisplayManagerCallback(); + bs.registerCallbackWithEventMask(callback, STANDARD_AND_CONNECTION_DISPLAY_EVENTS); + + callback.expectsEvent(EVENT_DISPLAY_ADDED); + FakeDisplayDevice displayDevice = + createFakeDisplayDevice(displayManager, new float[]{60f}, Display.TYPE_INTERNAL); + callback.waitForExpectedEvent(); + + LogicalDisplay display = + logicalDisplayMapper.getDisplayLocked(displayDevice, /* includeDisabled= */ true); + + assertThat(displayDevice.getDisplayDeviceInfoLocked().committedState) + .isEqualTo(Display.STATE_ON); + + assertThat(displayManager.requestDisplayPower(display.getDisplayIdLocked(), false)) + .isTrue(); + + assertThat(displayDevice.getDisplayDeviceInfoLocked().committedState) + .isEqualTo(Display.STATE_OFF); + + assertThat(displayManager.requestDisplayPower(display.getDisplayIdLocked(), true)) + .isTrue(); + + assertThat(displayDevice.getDisplayDeviceInfoLocked().committedState) + .isEqualTo(Display.STATE_ON); + } + + @Test + public void testPowerOnAndOffInternalDisplay_withoutPermission_shouldThrowException() { + DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector); + DisplayManagerService.BinderService bs = displayManager.new BinderService(); + LogicalDisplayMapper logicalDisplayMapper = displayManager.getLogicalDisplayMapper(); + FakeDisplayManagerCallback callback = new FakeDisplayManagerCallback(); + bs.registerCallbackWithEventMask(callback, STANDARD_AND_CONNECTION_DISPLAY_EVENTS); + + callback.expectsEvent(EVENT_DISPLAY_ADDED); + FakeDisplayDevice displayDevice = + createFakeDisplayDevice(displayManager, new float[]{60f}, Display.TYPE_INTERNAL); + callback.waitForExpectedEvent(); + + LogicalDisplay display = + logicalDisplayMapper.getDisplayLocked(displayDevice, /* includeDisabled= */ true); + var displayId = display.getDisplayIdLocked(); + + assertThat(displayDevice.getDisplayDeviceInfoLocked().committedState) + .isEqualTo(Display.STATE_ON); + + assertThrows(SecurityException.class, () -> bs.requestDisplayPower(displayId, true)); + assertThrows(SecurityException.class, () -> bs.requestDisplayPower(displayId, false)); + } + + @Test public void testEnableExternalDisplay_withDisplayManagement_shouldSignalDisplayAdded() { when(mMockFlags.isConnectedDisplayManagementEnabled()).thenReturn(true); manageDisplaysPermission(/* granted= */ true); @@ -3529,6 +3586,7 @@ public class DisplayManagerServiceTest { public void setDisplayDeviceInfo(DisplayDeviceInfo displayDeviceInfo) { mDisplayDeviceInfo = displayDeviceInfo; + mDisplayDeviceInfo.committedState = Display.STATE_ON; } @Override @@ -3558,5 +3616,14 @@ public class DisplayManagerServiceTest { public Display.Mode getUserPreferredDisplayModeLocked() { return mPreferredMode; } + + @Override + public Runnable requestDisplayStateLocked( + final int state, + final float brightnessState, + final float sdrBrightnessState, + @Nullable DisplayOffloadSessionImpl displayOffloadSession) { + return () -> mDisplayDeviceInfo.committedState = state; + } } } diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java index efa224f28c8c..95f0b65ec35c 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayPowerControllerTest.java @@ -1415,7 +1415,7 @@ public final class DisplayPowerControllerTest { when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(.2f); when(mHolder.displayPowerState.getSdrScreenBrightness()).thenReturn(.1f); - when(mHolder.clamperController.clamp(any(), anyFloat(), anyBoolean())).thenAnswer( + when(mHolder.clamperController.clamp(any(), anyFloat(), anyBoolean(), anyInt())).thenAnswer( invocation -> DisplayBrightnessState.builder() .setIsSlowChange(invocation.getArgument(2)) .setBrightness(invocation.getArgument(1)) @@ -1439,7 +1439,7 @@ public final class DisplayPowerControllerTest { when(mHolder.displayPowerState.getColorFadeLevel()).thenReturn(1.0f); when(mHolder.displayPowerState.getScreenBrightness()).thenReturn(.2f); when(mHolder.displayPowerState.getSdrScreenBrightness()).thenReturn(.1f); - when(mHolder.clamperController.clamp(any(), anyFloat(), anyBoolean())).thenAnswer( + when(mHolder.clamperController.clamp(any(), anyFloat(), anyBoolean(), anyInt())).thenAnswer( invocation -> DisplayBrightnessState.builder() .setIsSlowChange(invocation.getArgument(2)) .setBrightness(invocation.getArgument(1)) @@ -2094,7 +2094,7 @@ public final class DisplayPowerControllerTest { BrightnessClamperController clamperController = mock(BrightnessClamperController.class); when(hbmController.getCurrentBrightnessMax()).thenReturn(PowerManager.BRIGHTNESS_MAX); - when(clamperController.clamp(any(), anyFloat(), anyBoolean())).thenAnswer( + when(clamperController.clamp(any(), anyFloat(), anyBoolean(), anyInt())).thenAnswer( invocation -> DisplayBrightnessState.builder() .setIsSlowChange(invocation.getArgument(2)) .setBrightness(invocation.getArgument(1)) @@ -2328,7 +2328,7 @@ public final class DisplayPowerControllerTest { BrightnessClamperController getBrightnessClamperController(Handler handler, BrightnessClamperController.ClamperChangeListener clamperChangeListener, BrightnessClamperController.DisplayDeviceData data, Context context, - DisplayManagerFlags flags) { + DisplayManagerFlags flags, SensorManager sensorManager) { return mClamperController; } diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessClamperControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessClamperControllerTest.java index 5487bc53ffce..69043f5704de 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessClamperControllerTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessClamperControllerTest.java @@ -16,14 +16,20 @@ package com.android.server.display.brightness.clamper; +import static android.view.Display.STATE_ON; + import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; 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.Sensor; +import android.hardware.SensorEventListener; +import android.hardware.SensorManager; import android.hardware.display.BrightnessInfo; import android.hardware.display.DisplayManagerInternal; import android.os.Handler; @@ -34,11 +40,15 @@ import android.testing.TestableContext; import androidx.test.filters.SmallTest; import androidx.test.platform.app.InstrumentationRegistry; +import com.android.internal.util.test.FakeSettingsProvider; +import com.android.internal.util.test.FakeSettingsProviderRule; import com.android.server.display.DisplayBrightnessState; import com.android.server.display.DisplayDeviceConfig; +import com.android.server.display.TestUtils; import com.android.server.display.brightness.BrightnessReason; import com.android.server.display.feature.DeviceConfigParameterProvider; import com.android.server.display.feature.DisplayManagerFlags; +import com.android.server.testutils.OffsettableClock; import com.android.server.testutils.TestHandler; import org.junit.Before; @@ -54,13 +64,16 @@ import java.util.List; public class BrightnessClamperControllerTest { private static final float FLOAT_TOLERANCE = 0.001f; - private final TestHandler mTestHandler = new TestHandler(null); + private final OffsettableClock mClock = new OffsettableClock(); + private final TestHandler mTestHandler = new TestHandler(null, mClock); @Rule public final TestableContext mMockContext = new TestableContext( InstrumentationRegistry.getInstrumentation().getContext()); @Mock private BrightnessClamperController.ClamperChangeListener mMockExternalListener; + @Mock + private SensorManager mSensorManager; @Mock private BrightnessClamperController.DisplayDeviceData mMockDisplayDeviceData; @@ -74,15 +87,25 @@ public class BrightnessClamperControllerTest { private BrightnessModifier mMockModifier; @Mock private DisplayManagerInternal.DisplayPowerRequest mMockRequest; + + Sensor mLightSensor; + @Mock private DeviceConfig.Properties mMockProperties; private BrightnessClamperController mClamperController; private TestInjector mTestInjector; + @Rule + public FakeSettingsProviderRule mSettingsProviderRule = FakeSettingsProvider.rule(); + @Before - public void setUp() { + public void setUp() throws Exception { MockitoAnnotations.initMocks(this); + mLightSensor = TestUtils.createSensor(Sensor.TYPE_LIGHT, "Light Sensor"); mTestInjector = new TestInjector(List.of(mMockClamper), List.of(mMockModifier)); + when(mSensorManager.getDefaultSensor(anyInt())).thenReturn(mLightSensor); + when(mMockModifier.shouldListenToLightSensor()).thenReturn(true); + mClamperController = createBrightnessClamperController(); } @@ -132,7 +155,7 @@ public class BrightnessClamperControllerTest { public void testClamp_AppliesModifier() { float initialBrightness = 0.2f; boolean initialSlowChange = true; - mClamperController.clamp(mMockRequest, initialBrightness, initialSlowChange); + mClamperController.clamp(mMockRequest, initialBrightness, initialSlowChange, STATE_ON); verify(mMockModifier).apply(eq(mMockRequest), any()); } @@ -151,7 +174,7 @@ public class BrightnessClamperControllerTest { mTestHandler.flush(); DisplayBrightnessState state = mClamperController.clamp(mMockRequest, initialBrightness, - initialSlowChange); + initialSlowChange, STATE_ON); assertEquals(initialBrightness, state.getBrightness(), FLOAT_TOLERANCE); assertEquals(PowerManager.BRIGHTNESS_MAX, state.getMaxBrightness(), FLOAT_TOLERANCE); @@ -175,7 +198,7 @@ public class BrightnessClamperControllerTest { mTestHandler.flush(); DisplayBrightnessState state = mClamperController.clamp(mMockRequest, initialBrightness, - initialSlowChange); + initialSlowChange, STATE_ON); assertEquals(clampedBrightness, state.getBrightness(), FLOAT_TOLERANCE); assertEquals(clampedBrightness, state.getMaxBrightness(), FLOAT_TOLERANCE); @@ -199,7 +222,7 @@ public class BrightnessClamperControllerTest { mTestHandler.flush(); DisplayBrightnessState state = mClamperController.clamp(mMockRequest, initialBrightness, - initialSlowChange); + initialSlowChange, STATE_ON); assertEquals(initialBrightness, state.getBrightness(), FLOAT_TOLERANCE); assertEquals(clampedBrightness, state.getMaxBrightness(), FLOAT_TOLERANCE); @@ -223,10 +246,10 @@ public class BrightnessClamperControllerTest { mTestHandler.flush(); // first call of clamp method mClamperController.clamp(mMockRequest, initialBrightness, - initialSlowChange); + initialSlowChange, STATE_ON); // immediately second call of clamp method DisplayBrightnessState state = mClamperController.clamp(mMockRequest, initialBrightness, - initialSlowChange); + initialSlowChange, STATE_ON); assertEquals(clampedBrightness, state.getBrightness(), FLOAT_TOLERANCE); assertEquals(clampedBrightness, state.getMaxBrightness(), FLOAT_TOLERANCE); @@ -237,6 +260,31 @@ public class BrightnessClamperControllerTest { } @Test + public void testAmbientLuxChanges() throws Exception { + ArgumentCaptor<SensorEventListener> listenerCaptor = ArgumentCaptor.forClass( + SensorEventListener.class); + + verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(mLightSensor), + anyInt(), any(Handler.class)); + SensorEventListener listener = listenerCaptor.getValue(); + + when(mSensorManager.getSensorList(eq(Sensor.TYPE_ALL))).thenReturn(List.of(mLightSensor)); + + float initialBrightness = 0.8f; + boolean initialSlowChange = true; + + DisplayBrightnessState state = mClamperController.clamp(mMockRequest, initialBrightness, + initialSlowChange, STATE_ON); + assertEquals(initialBrightness, state.getBrightness(), FLOAT_TOLERANCE); + + listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 50, mClock.now())); + verify(mMockModifier).setAmbientLux(50); + + listener.onSensorChanged(TestUtils.createSensorEvent(mLightSensor, 300, mClock.now())); + verify(mMockModifier).setAmbientLux(300); + } + + @Test public void testStop() { mClamperController.stop(); verify(mMockModifier).stop(); @@ -245,7 +293,7 @@ public class BrightnessClamperControllerTest { private BrightnessClamperController createBrightnessClamperController() { return new BrightnessClamperController(mTestInjector, mTestHandler, mMockExternalListener, - mMockDisplayDeviceData, mMockContext, mFlags); + mMockDisplayDeviceData, mMockContext, mFlags, mSensorManager); } private class TestInjector extends BrightnessClamperController.Injector { @@ -282,7 +330,7 @@ public class BrightnessClamperControllerTest { @Override List<BrightnessStateModifier> getModifiers(DisplayManagerFlags flags, Context context, Handler handler, BrightnessClamperController.ClamperChangeListener listener, - DisplayDeviceConfig displayDeviceConfig) { + DisplayDeviceConfig displayDeviceConfig, SensorManager sensorManager) { return mModifiers; } } diff --git a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessLowLuxModifierTest.kt b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessLowLuxModifierTest.kt index 21e066dd0922..d672435096b9 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessLowLuxModifierTest.kt +++ b/services/tests/displayservicetests/src/com/android/server/display/brightness/clamper/BrightnessLowLuxModifierTest.kt @@ -20,8 +20,6 @@ import android.platform.test.annotations.RequiresFlagsEnabled import android.provider.Settings import android.testing.TestableContext import androidx.test.platform.app.InstrumentationRegistry -import com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED -import com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED import com.android.server.display.DisplayDeviceConfig import com.android.server.display.brightness.BrightnessReason import com.android.server.display.feature.flags.Flags @@ -54,10 +52,12 @@ class BrightnessLowLuxModifierTest { @Before fun setUp() { modifier = - BrightnessLowLuxModifier(testHandler, + BrightnessLowLuxModifier( + testHandler, mockClamperChangeListener, context, - mockDisplayDeviceConfig) + mockDisplayDeviceConfig + ) // values below transition point (even dimmer range) // nits: 0.1 -> backlight 0.02 -> brightness -> 0.1 @@ -66,7 +66,7 @@ class BrightnessLowLuxModifierTest { whenever(mockDisplayDeviceConfig.getBrightnessFromBacklight(/* backlight = */ 0.02f)) .thenReturn(LOW_LUX_BRIGHTNESS) - // values above transition point (noraml range) + // values above transition point (normal range) // nits: 10 -> backlight 0.2 -> brightness -> 0.3 whenever(mockDisplayDeviceConfig.getBacklightFromNits(/* nits= */ 2f)) .thenReturn(0.15f) @@ -95,12 +95,12 @@ class BrightnessLowLuxModifierTest { // test transition point ensures brightness doesn't drop when setting is off. Settings.Secure.putIntForUser(context.contentResolver, Settings.Secure.EVEN_DIMMER_ACTIVATED, 0, USER_ID) - modifier.setAutoBrightnessState(AUTO_BRIGHTNESS_ENABLED) modifier.recalculateLowerBound() testHandler.flush() assertThat(modifier.brightnessLowerBound).isEqualTo(TRANSITION_POINT) assertThat(modifier.brightnessReason).isEqualTo(0) // no reason - ie off - modifier.onAmbientLuxChange(3000.0f) + modifier.setAmbientLux(3000f) + testHandler.flush() assertThat(modifier.isActive).isFalse() assertThat(modifier.brightnessLowerBound).isEqualTo(TRANSITION_POINT) @@ -115,8 +115,8 @@ class BrightnessLowLuxModifierTest { Settings.Secure.EVEN_DIMMER_ACTIVATED, 1, USER_ID) Settings.Secure.putFloatForUser(context.contentResolver, Settings.Secure.EVEN_DIMMER_MIN_NITS, 0.1f, USER_ID) - modifier.setAutoBrightnessState(AUTO_BRIGHTNESS_ENABLED) - modifier.onAmbientLuxChange(400.0f) + modifier.setAmbientLux(400f) + testHandler.flush() assertThat(modifier.isActive).isTrue() @@ -133,7 +133,6 @@ class BrightnessLowLuxModifierTest { Settings.Secure.EVEN_DIMMER_ACTIVATED, 1, USER_ID) Settings.Secure.putFloatForUser(context.contentResolver, Settings.Secure.EVEN_DIMMER_MIN_NITS, 10.0f, USER_ID) - modifier.setAutoBrightnessState(AUTO_BRIGHTNESS_ENABLED) modifier.recalculateLowerBound() testHandler.flush() @@ -152,8 +151,8 @@ class BrightnessLowLuxModifierTest { Settings.Secure.EVEN_DIMMER_ACTIVATED, 1, USER_ID) // on Settings.Secure.putFloatForUser(context.contentResolver, Settings.Secure.EVEN_DIMMER_MIN_NITS, 1.0f, USER_ID) - modifier.setAutoBrightnessState(AUTO_BRIGHTNESS_ENABLED) - modifier.onAmbientLuxChange(400.0f) + modifier.setAmbientLux(400f) + testHandler.flush() assertThat(modifier.isActive).isTrue() @@ -180,8 +179,8 @@ class BrightnessLowLuxModifierTest { Settings.Secure.EVEN_DIMMER_ACTIVATED, 0, USER_ID) // off Settings.Secure.putFloatForUser(context.contentResolver, Settings.Secure.EVEN_DIMMER_MIN_NITS, 1.0f, USER_ID) - modifier.setAutoBrightnessState(AUTO_BRIGHTNESS_ENABLED) - modifier.onAmbientLuxChange(400.0f) + modifier.setAmbientLux(400f) + testHandler.flush() assertThat(modifier.isActive).isFalse() @@ -203,15 +202,14 @@ class BrightnessLowLuxModifierTest { @Test @RequiresFlagsEnabled(Flags.FLAG_EVEN_DIMMER) - fun testDisabledWhenAutobrightnessIsOff() { + fun testEnabledEvenWhenAutobrightnessIsOff() { // test that high lux prevents low brightness range. Settings.Secure.putIntForUser(context.contentResolver, Settings.Secure.EVEN_DIMMER_ACTIVATED, 1, USER_ID) // on Settings.Secure.putFloatForUser(context.contentResolver, Settings.Secure.EVEN_DIMMER_MIN_NITS, 1.0f, USER_ID) - modifier.setAutoBrightnessState(AUTO_BRIGHTNESS_ENABLED) - modifier.onAmbientLuxChange(400.0f) + modifier.setAmbientLux(400f) testHandler.flush() assertThat(modifier.isActive).isTrue() @@ -219,15 +217,13 @@ class BrightnessLowLuxModifierTest { assertThat(modifier.brightnessReason).isEqualTo(BrightnessReason.MODIFIER_MIN_LUX) assertThat(modifier.brightnessLowerBound).isEqualTo(LOW_LUX_BRIGHTNESS) - - modifier.setAutoBrightnessState(AUTO_BRIGHTNESS_DISABLED) - modifier.onAmbientLuxChange(400.0f) + modifier.setAmbientLux(400f) testHandler.flush() - assertThat(modifier.isActive).isFalse() + assertThat(modifier.isActive).isTrue() // Test restriction from lux setting - assertThat(modifier.brightnessReason).isEqualTo(0) - assertThat(modifier.brightnessLowerBound).isEqualTo(TRANSITION_POINT) + assertThat(modifier.brightnessReason).isEqualTo(BrightnessReason.MODIFIER_MIN_LUX) + assertThat(modifier.brightnessLowerBound).isEqualTo(LOW_LUX_BRIGHTNESS) } } 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 28c7fb2396dd..488ce66ca99b 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java @@ -44,6 +44,7 @@ import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_SHORT_SERVI import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; +import static com.android.server.am.ActivityManagerService.FOLLOW_UP_OOMADJUSTER_UPDATE_MSG; import static com.android.server.am.ProcessList.BACKUP_APP_ADJ; import static com.android.server.am.ProcessList.CACHED_APP_MAX_ADJ; import static com.android.server.am.ProcessList.CACHED_APP_MIN_ADJ; @@ -77,6 +78,7 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyBoolean; import static org.mockito.Mockito.anyInt; +import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doCallRealMethod; import static org.mockito.Mockito.doNothing; @@ -113,11 +115,10 @@ import com.android.server.wm.ActivityTaskManagerService; import com.android.server.wm.WindowProcessController; import org.junit.After; -import org.junit.AfterClass; import org.junit.Before; -import org.junit.BeforeClass; import org.junit.Rule; import org.junit.Test; +import org.mockito.ArgumentCaptor; import java.io.File; import java.lang.reflect.Field; @@ -164,92 +165,86 @@ public class MockingOomAdjusterTests { private static int sFirstCachedAdj = ProcessList.CACHED_APP_MIN_ADJ + ProcessList.CACHED_APP_IMPORTANCE_LEVELS; - private static Context sContext; - private static PackageManagerInternal sPackageManagerInternal; - private static ActivityManagerService sService; + private Context mContext; + private PackageManagerInternal mPackageManagerInternal; + private ActivityManagerService mService; + private OomAdjusterInjector mInjector = new OomAdjusterInjector(); @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); @SuppressWarnings("GuardedBy") - @BeforeClass - public static void setUpOnce() { - sContext = getInstrumentation().getTargetContext(); + @Before + public void setUp() { + mContext = getInstrumentation().getTargetContext(); System.setProperty("dexmaker.share_classloader", "true"); - sPackageManagerInternal = mock(PackageManagerInternal.class); - doReturn(new ComponentName("", "")).when(sPackageManagerInternal) + mPackageManagerInternal = mock(PackageManagerInternal.class); + doReturn(new ComponentName("", "")).when(mPackageManagerInternal) .getSystemUiServiceComponent(); // Remove stale instance of PackageManagerInternal if there is any LocalServices.removeServiceForTest(PackageManagerInternal.class); - LocalServices.addService(PackageManagerInternal.class, sPackageManagerInternal); - - sService = mock(ActivityManagerService.class); - sService.mActivityTaskManager = new ActivityTaskManagerService(sContext); - sService.mActivityTaskManager.initialize(null, null, sContext.getMainLooper()); - sService.mPackageManagerInt = sPackageManagerInternal; - sService.mAtmInternal = spy(sService.mActivityTaskManager.getAtmInternal()); - - sService.mConstants = new ActivityManagerConstants(sContext, sService, - sContext.getMainThreadHandler()); - setFieldValue(ActivityManagerService.class, sService, "mContext", - sContext); + LocalServices.addService(PackageManagerInternal.class, mPackageManagerInternal); + + mService = mock(ActivityManagerService.class); + mService.mActivityTaskManager = new ActivityTaskManagerService(mContext); + mService.mActivityTaskManager.initialize(null, null, mContext.getMainLooper()); + mService.mPackageManagerInt = mPackageManagerInternal; + mService.mAtmInternal = spy(mService.mActivityTaskManager.getAtmInternal()); + + mService.mConstants = new ActivityManagerConstants(mContext, mService, + mContext.getMainThreadHandler()); + setFieldValue(ActivityManagerService.class, mService, "mContext", + mContext); ProcessList pr = spy(new ProcessList()); - pr.mService = sService; + pr.mService = mService; AppProfiler profiler = mock(AppProfiler.class); - setFieldValue(ActivityManagerService.class, sService, "mProcessList", + setFieldValue(ActivityManagerService.class, mService, "mProcessList", pr); - setFieldValue(ActivityManagerService.class, sService, "mHandler", + setFieldValue(ActivityManagerService.class, mService, "mHandler", mock(ActivityManagerService.MainHandler.class)); - setFieldValue(ActivityManagerService.class, sService, "mProcessStats", - new ProcessStatsService(sService, new File(sContext.getFilesDir(), "procstats"))); - setFieldValue(ActivityManagerService.class, sService, "mBackupTargets", + setFieldValue(ActivityManagerService.class, mService, "mProcessStats", + new ProcessStatsService(mService, new File(mContext.getFilesDir(), "procstats"))); + setFieldValue(ActivityManagerService.class, mService, "mBackupTargets", mock(SparseArray.class)); - setFieldValue(ActivityManagerService.class, sService, "mUserController", + setFieldValue(ActivityManagerService.class, mService, "mUserController", mock(UserController.class)); - setFieldValue(ActivityManagerService.class, sService, "mAppProfiler", profiler); - setFieldValue(ActivityManagerService.class, sService, "mProcLock", + setFieldValue(ActivityManagerService.class, mService, "mAppProfiler", profiler); + setFieldValue(ActivityManagerService.class, mService, "mProcLock", new ActivityManagerProcLock()); - setFieldValue(ActivityManagerService.class, sService, "mServices", - spy(new ActiveServices(sService))); - setFieldValue(ActivityManagerService.class, sService, "mInternal", + setFieldValue(ActivityManagerService.class, mService, "mServices", + spy(new ActiveServices(mService))); + setFieldValue(ActivityManagerService.class, mService, "mInternal", mock(ActivityManagerService.LocalService.class)); - setFieldValue(ActivityManagerService.class, sService, "mBatteryStatsService", + setFieldValue(ActivityManagerService.class, mService, "mBatteryStatsService", mock(BatteryStatsService.class)); - setFieldValue(ActivityManagerService.class, sService, "mInjector", - new ActivityManagerService.Injector(sContext)); - doReturn(mock(AppOpsManager.class)).when(sService).getAppOpsManager(); - doCallRealMethod().when(sService).enqueueOomAdjTargetLocked(any(ProcessRecord.class)); - doCallRealMethod().when(sService).updateOomAdjPendingTargetsLocked(OOM_ADJ_REASON_ACTIVITY); + setFieldValue(ActivityManagerService.class, mService, "mInjector", + new ActivityManagerService.Injector(mContext)); + doReturn(mock(AppOpsManager.class)).when(mService).getAppOpsManager(); + doCallRealMethod().when(mService).enqueueOomAdjTargetLocked(any(ProcessRecord.class)); + doCallRealMethod().when(mService).updateOomAdjPendingTargetsLocked(OOM_ADJ_REASON_ACTIVITY); setFieldValue(AppProfiler.class, profiler, "mProfilerLock", new Object()); doReturn(new ActivityManagerService.ProcessChangeItem()).when(pr) .enqueueProcessChangeItemLocked(anyInt(), anyInt()); - sService.mOomAdjuster = sService.mConstants.ENABLE_NEW_OOMADJ - ? new OomAdjusterModernImpl(sService, sService.mProcessList, - new ActiveUids(sService, false)) - : new OomAdjuster(sService, sService.mProcessList, new ActiveUids(sService, false)); - sService.mOomAdjuster.mAdjSeq = 10000; - sService.mWakefulness = new AtomicInteger(PowerManagerInternal.WAKEFULNESS_AWAKE); - if (sService.mConstants.USE_TIERED_CACHED_ADJ) { + mService.mOomAdjuster = mService.mConstants.ENABLE_NEW_OOMADJ + ? new OomAdjusterModernImpl(mService, mService.mProcessList, + new ActiveUids(mService, false), mInjector) + : new OomAdjuster(mService, mService.mProcessList, new ActiveUids(mService, false), + mInjector); + mService.mOomAdjuster.mAdjSeq = 10000; + mService.mWakefulness = new AtomicInteger(PowerManagerInternal.WAKEFULNESS_AWAKE); + if (mService.mConstants.USE_TIERED_CACHED_ADJ) { sFirstCachedAdj = ProcessList.CACHED_APP_MIN_ADJ + 10; } - } - - @Before - public void setUp() { mSetFlagsRule.enableFlags(Flags.FLAG_NEW_FGS_RESTRICTION_LOGIC); } - @AfterClass - public static void tearDownOnce() { - LocalServices.removeServiceForTest(PackageManagerInternal.class); - } - @SuppressWarnings("GuardedBy") @After public void tearDown() { - sService.mOomAdjuster.resetInternal(); - sService.mOomAdjuster.mActiveUids.clear(); + mService.mOomAdjuster.resetInternal(); + mService.mOomAdjuster.mActiveUids.clear(); + LocalServices.removeServiceForTest(PackageManagerInternal.class); } private static <T> void setFieldValue(Class clazz, Object obj, String fieldName, T val) { @@ -278,7 +273,7 @@ public class MockingOomAdjusterTests { */ @SuppressWarnings("GuardedBy") private void setProcessesToLru(ProcessRecord... apps) { - ArrayList<ProcessRecord> lru = sService.mProcessList.getLruProcessesLOSP(); + ArrayList<ProcessRecord> lru = mService.mProcessList.getLruProcessesLOSP(); lru.clear(); Collections.addAll(lru, apps); } @@ -292,20 +287,20 @@ public class MockingOomAdjusterTests { @SuppressWarnings("GuardedBy") private void updateOomAdj(ProcessRecord... apps) { if (apps.length == 0) { - updateProcessRecordNodes(sService.mProcessList.getLruProcessesLOSP()); - sService.mOomAdjuster.updateOomAdjLocked(OOM_ADJ_REASON_NONE); + updateProcessRecordNodes(mService.mProcessList.getLruProcessesLOSP()); + mService.mOomAdjuster.updateOomAdjLocked(OOM_ADJ_REASON_NONE); } else { updateProcessRecordNodes(Arrays.asList(apps)); if (apps.length == 1) { final ProcessRecord app = apps[0]; - if (!sService.mProcessList.getLruProcessesLOSP().contains(app)) { - sService.mProcessList.getLruProcessesLOSP().add(app); + if (!mService.mProcessList.getLruProcessesLOSP().contains(app)) { + mService.mProcessList.getLruProcessesLOSP().add(app); } - sService.mOomAdjuster.updateOomAdjLocked(apps[0], OOM_ADJ_REASON_NONE); + mService.mOomAdjuster.updateOomAdjLocked(apps[0], OOM_ADJ_REASON_NONE); } else { setProcessesToLru(apps); - sService.mOomAdjuster.updateOomAdjLocked(OOM_ADJ_REASON_NONE); - sService.mProcessList.getLruProcessesLOSP().clear(); + mService.mOomAdjuster.updateOomAdjLocked(OOM_ADJ_REASON_NONE); + mService.mProcessList.getLruProcessesLOSP().clear(); } } } @@ -318,10 +313,10 @@ public class MockingOomAdjusterTests { private void updateOomAdjPending(ProcessRecord... apps) { setProcessesToLru(apps); for (ProcessRecord app : apps) { - sService.mOomAdjuster.enqueueOomAdjTargetLocked(app); + mService.mOomAdjuster.enqueueOomAdjTargetLocked(app); } - sService.mOomAdjuster.updateOomAdjPendingTargetsLocked(OOM_ADJ_REASON_NONE); - sService.mProcessList.getLruProcessesLOSP().clear(); + mService.mOomAdjuster.updateOomAdjPendingTargetsLocked(OOM_ADJ_REASON_NONE); + mService.mProcessList.getLruProcessesLOSP().clear(); } /** @@ -343,9 +338,9 @@ public class MockingOomAdjusterTests { MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); app.mState.setMaxAdj(PERSISTENT_PROC_ADJ); app.mState.setHasTopUi(true); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_ASLEEP); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_ASLEEP); updateOomAdj(app); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); assertProcStates(app, PROCESS_STATE_BOUND_FOREGROUND_SERVICE, PERSISTENT_PROC_ADJ, SCHED_GROUP_RESTRICTED); @@ -359,7 +354,7 @@ public class MockingOomAdjusterTests { MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); app.mState.setMaxAdj(PERSISTENT_PROC_ADJ); app.mState.setHasTopUi(true); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); assertProcStates(app, PROCESS_STATE_PERSISTENT_UI, PERSISTENT_PROC_ADJ, @@ -372,10 +367,10 @@ public class MockingOomAdjusterTests { ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); app.mState.setMaxAdj(PERSISTENT_PROC_ADJ); - doReturn(app).when(sService).getTopApp(); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + doReturn(app).when(mService).getTopApp(); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); - doReturn(null).when(sService).getTopApp(); + doReturn(null).when(mService).getTopApp(); assertProcStates(app, PROCESS_STATE_PERSISTENT_UI, PERSISTENT_PROC_ADJ, SCHED_GROUP_TOP_APP); @@ -386,11 +381,11 @@ public class MockingOomAdjusterTests { public void testUpdateOomAdj_DoOne_TopApp_Awake() { ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); - doReturn(PROCESS_STATE_TOP).when(sService.mAtmInternal).getTopProcessState(); - doReturn(app).when(sService).getTopApp(); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + doReturn(PROCESS_STATE_TOP).when(mService.mAtmInternal).getTopProcessState(); + doReturn(app).when(mService).getTopApp(); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); - doReturn(null).when(sService).getTopApp(); + doReturn(null).when(mService).getTopApp(); assertProcStates(app, PROCESS_STATE_TOP, FOREGROUND_APP_ADJ, SCHED_GROUP_TOP_APP); } @@ -400,11 +395,11 @@ public class MockingOomAdjusterTests { public void testUpdateOomAdj_DoOne_RunningAnimations() { ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); - doReturn(PROCESS_STATE_TOP_SLEEPING).when(sService.mAtmInternal).getTopProcessState(); + doReturn(PROCESS_STATE_TOP_SLEEPING).when(mService.mAtmInternal).getTopProcessState(); app.mState.setRunningRemoteAnimation(true); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); - doReturn(PROCESS_STATE_TOP).when(sService.mAtmInternal).getTopProcessState(); + doReturn(PROCESS_STATE_TOP).when(mService.mAtmInternal).getTopProcessState(); assertProcStates(app, PROCESS_STATE_TOP_SLEEPING, VISIBLE_APP_ADJ, SCHED_GROUP_TOP_APP); } @@ -415,7 +410,7 @@ public class MockingOomAdjusterTests { ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); doReturn(mock(ActiveInstrumentation.class)).when(app).getActiveInstrumentation(); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); doCallRealMethod().when(app).getActiveInstrumentation(); @@ -429,11 +424,11 @@ public class MockingOomAdjusterTests { public void testUpdateOomAdj_DoOne_ReceivingBroadcast() { ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); - doReturn(true).when(sService).isReceivingBroadcastLocked(any(ProcessRecord.class), + doReturn(true).when(mService).isReceivingBroadcastLocked(any(ProcessRecord.class), any(int[].class)); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); - doReturn(false).when(sService).isReceivingBroadcastLocked(any(ProcessRecord.class), + doReturn(false).when(mService).isReceivingBroadcastLocked(any(ProcessRecord.class), any(int[].class)); assertProcStates(app, PROCESS_STATE_RECEIVER, FOREGROUND_APP_ADJ, SCHED_GROUP_BACKGROUND); @@ -445,7 +440,7 @@ public class MockingOomAdjusterTests { ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); app.mServices.startExecutingService(mock(ServiceRecord.class)); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); assertProcStates(app, PROCESS_STATE_SERVICE, FOREGROUND_APP_ADJ, SCHED_GROUP_BACKGROUND); @@ -456,13 +451,13 @@ public class MockingOomAdjusterTests { public void testUpdateOomAdj_DoOne_TopApp_Sleeping() { ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); - doReturn(PROCESS_STATE_TOP_SLEEPING).when(sService.mAtmInternal).getTopProcessState(); - doReturn(app).when(sService).getTopApp(); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_ASLEEP); + doReturn(PROCESS_STATE_TOP_SLEEPING).when(mService.mAtmInternal).getTopProcessState(); + doReturn(app).when(mService).getTopApp(); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_ASLEEP); updateOomAdj(app); - doReturn(null).when(sService).getTopApp(); - doReturn(PROCESS_STATE_TOP).when(sService.mAtmInternal).getTopProcessState(); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + doReturn(null).when(mService).getTopApp(); + doReturn(PROCESS_STATE_TOP).when(mService.mAtmInternal).getTopProcessState(); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); assertProcStates(app, PROCESS_STATE_TOP_SLEEPING, FOREGROUND_APP_ADJ, SCHED_GROUP_BACKGROUND); @@ -475,8 +470,8 @@ public class MockingOomAdjusterTests { MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); app.mState.setCurRawAdj(CACHED_APP_MIN_ADJ); app.mState.setCurAdj(CACHED_APP_MIN_ADJ); - doReturn(null).when(sService).getTopApp(); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + doReturn(null).when(mService).getTopApp(); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); final int expectedAdj = sFirstCachedAdj; @@ -505,7 +500,7 @@ public class MockingOomAdjusterTests { return 0; })).when(wpc).computeOomAdjFromActivities( any(WindowProcessController.ComputeOomAdjCallback.class)); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); assertProcStates(app, PROCESS_STATE_TOP, VISIBLE_APP_ADJ, SCHED_GROUP_TOP_APP); @@ -522,7 +517,7 @@ public class MockingOomAdjusterTests { WindowProcessController wpc = app.getWindowProcessController(); doReturn(true).when(wpc).hasRecentTasks(); app.mState.setLastTopTime(SystemClock.uptimeMillis()); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); doCallRealMethod().when(wpc).hasRecentTasks(); @@ -536,7 +531,7 @@ public class MockingOomAdjusterTests { MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); app.mServices.setHasForegroundServices(true, ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION, /* hasNoneType=*/false); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, @@ -550,7 +545,7 @@ public class MockingOomAdjusterTests { ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); app.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, @@ -561,10 +556,10 @@ public class MockingOomAdjusterTests { @SuppressWarnings("GuardedBy") @Test public void testUpdateOomAdj_DoOne_FgService_ShortFgs() { - sService.mConstants.TOP_TO_FGS_GRACE_DURATION = 100_000; - sService.mConstants.mShortFgsProcStateExtraWaitDuration = 200_000; + mService.mConstants.TOP_TO_FGS_GRACE_DURATION = 100_000; + mService.mConstants.mShortFgsProcStateExtraWaitDuration = 200_000; - ServiceRecord s = ServiceRecord.newEmptyInstanceForTest(sService); + ServiceRecord s = ServiceRecord.newEmptyInstanceForTest(mService); s.appInfo = new ApplicationInfo(); s.startRequested = true; s.isForeground = true; @@ -579,7 +574,7 @@ public class MockingOomAdjusterTests { app.mServices.setHasForegroundServices(true, FOREGROUND_SERVICE_TYPE_SHORT_SERVICE, /* hasNoneType=*/false); app.mState.setLastTopTime(SystemClock.uptimeMillis()); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); @@ -596,8 +591,8 @@ public class MockingOomAdjusterTests { FOREGROUND_SERVICE_TYPE_SHORT_SERVICE, /* hasNoneType=*/false); app.mServices.startService(s); app.mState.setLastTopTime(SystemClock.uptimeMillis() - - sService.mConstants.TOP_TO_FGS_GRACE_DURATION); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + - mService.mConstants.TOP_TO_FGS_GRACE_DURATION); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); @@ -607,14 +602,14 @@ public class MockingOomAdjusterTests { } // SHORT_SERVICE, timed out already. - s = ServiceRecord.newEmptyInstanceForTest(sService); + s = ServiceRecord.newEmptyInstanceForTest(mService); s.appInfo = new ApplicationInfo(); s.startRequested = true; s.isForeground = true; s.foregroundServiceType = FOREGROUND_SERVICE_TYPE_SHORT_SERVICE; s.setShortFgsInfo(SystemClock.uptimeMillis() - - sService.mConstants.mShortFgsTimeoutDuration - - sService.mConstants.mShortFgsProcStateExtraWaitDuration); + - mService.mConstants.mShortFgsTimeoutDuration + - mService.mConstants.mShortFgsProcStateExtraWaitDuration); { ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); @@ -622,8 +617,8 @@ public class MockingOomAdjusterTests { FOREGROUND_SERVICE_TYPE_SHORT_SERVICE, /* hasNoneType=*/false); app.mServices.startService(s); app.mState.setLastTopTime(SystemClock.uptimeMillis() - - sService.mConstants.TOP_TO_FGS_GRACE_DURATION); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + - mService.mConstants.TOP_TO_FGS_GRACE_DURATION); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); @@ -639,7 +634,7 @@ public class MockingOomAdjusterTests { ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); app.mState.setHasOverlayUi(true); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); assertProcStates(app, PROCESS_STATE_IMPORTANT_FOREGROUND, PERCEPTIBLE_APP_ADJ, @@ -653,12 +648,26 @@ public class MockingOomAdjusterTests { MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); app.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); app.mState.setLastTopTime(SystemClock.uptimeMillis()); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, - PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ, SCHED_GROUP_DEFAULT); + PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ, SCHED_GROUP_DEFAULT, "fg-service-act"); assertBfsl(app); + + if (!Flags.followUpOomadjUpdates()) return; + + final ArgumentCaptor<Long> followUpTimeCaptor = ArgumentCaptor.forClass(Long.class); + verify(mService.mHandler).sendEmptyMessageAtTime( + eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG), followUpTimeCaptor.capture()); + mInjector.jumpUptimeAheadTo(followUpTimeCaptor.getValue()); + mService.mOomAdjuster.updateOomAdjFollowUpTargetsLocked(); + + assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, + SCHED_GROUP_DEFAULT, "fg-service"); + // Follow up should not have been called again. + verify(mService.mHandler).sendEmptyMessageAtTime(eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG), + followUpTimeCaptor.capture()); } @SuppressWarnings("GuardedBy") @@ -678,12 +687,24 @@ public class MockingOomAdjusterTests { s.lastTopAlmostPerceptibleBindRequestUptimeMs = nowUptime; s.getConnections().clear(); app.mServices.updateHasTopStartedAlmostPerceptibleServices(); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); assertEquals(PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ + 2, app.mState.getSetAdj()); - sService.mOomAdjuster.resetInternal(); + if (!Flags.followUpOomadjUpdates()) return; + + final ArgumentCaptor<Long> followUpTimeCaptor = ArgumentCaptor.forClass(Long.class); + verify(mService.mHandler).sendEmptyMessageAtTime( + eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG), followUpTimeCaptor.capture()); + mInjector.jumpUptimeAheadTo(followUpTimeCaptor.getValue()); + mService.mOomAdjuster.updateOomAdjFollowUpTargetsLocked(); + + assertEquals(sFirstCachedAdj, app.mState.getSetAdj()); + // Follow up should not have been called again. + verify(mService.mHandler).sendEmptyMessageAtTime(eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG), + followUpTimeCaptor.capture()); + } // Out of grace period but valid binding allows the adjustment. @@ -698,14 +719,14 @@ public class MockingOomAdjusterTests { ServiceRecord s = bindService(app, system, null, null, Context.BIND_ALMOST_PERCEPTIBLE + 2, mock(IBinder.class)); s.lastTopAlmostPerceptibleBindRequestUptimeMs = - nowUptime - 2 * sService.mConstants.mServiceBindAlmostPerceptibleTimeoutMs; + nowUptime - 2 * mService.mConstants.mServiceBindAlmostPerceptibleTimeoutMs; app.mServices.updateHasTopStartedAlmostPerceptibleServices(); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); assertEquals(PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ + 2, app.mState.getSetAdj()); - sService.mOomAdjuster.resetInternal(); + mService.mOomAdjuster.resetInternal(); } // Out of grace period and no valid binding so no adjustment. @@ -720,15 +741,15 @@ public class MockingOomAdjusterTests { ServiceRecord s = bindService(app, system, null, null, Context.BIND_ALMOST_PERCEPTIBLE, mock(IBinder.class)); s.lastTopAlmostPerceptibleBindRequestUptimeMs = - nowUptime - 2 * sService.mConstants.mServiceBindAlmostPerceptibleTimeoutMs; + nowUptime - 2 * mService.mConstants.mServiceBindAlmostPerceptibleTimeoutMs; s.getConnections().clear(); app.mServices.updateHasTopStartedAlmostPerceptibleServices(); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); assertNotEquals(PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ + 2, app.mState.getSetAdj()); - sService.mOomAdjuster.resetInternal(); + mService.mOomAdjuster.resetInternal(); } } @@ -744,7 +765,7 @@ public class MockingOomAdjusterTests { // Simulate the system starting and binding to a service in the app. ServiceRecord s = bindService(app, system, null, null, Context.BIND_ALMOST_PERCEPTIBLE, mock(IBinder.class)); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(system, app); assertProcStates(app, PROCESS_STATE_IMPORTANT_FOREGROUND, @@ -757,7 +778,7 @@ public class MockingOomAdjusterTests { ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); app.mState.setForcingToImportant(new Object()); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); assertProcStates(app, PROCESS_STATE_TRANSIENT_BACKGROUND, PERCEPTIBLE_APP_ADJ, @@ -771,7 +792,7 @@ public class MockingOomAdjusterTests { MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); WindowProcessController wpc = app.getWindowProcessController(); doReturn(true).when(wpc).isHeavyWeightProcess(); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); doReturn(false).when(wpc).isHeavyWeightProcess(); @@ -786,7 +807,7 @@ public class MockingOomAdjusterTests { MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); WindowProcessController wpc = app.getWindowProcessController(); doReturn(true).when(wpc).isHomeProcess(); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); assertProcStates(app, PROCESS_STATE_HOME, HOME_APP_ADJ, SCHED_GROUP_BACKGROUND); @@ -800,11 +821,25 @@ public class MockingOomAdjusterTests { WindowProcessController wpc = app.getWindowProcessController(); doReturn(true).when(wpc).isPreviousProcess(); doReturn(true).when(wpc).hasActivities(); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); assertProcStates(app, PROCESS_STATE_LAST_ACTIVITY, PREVIOUS_APP_ADJ, - SCHED_GROUP_BACKGROUND); + SCHED_GROUP_BACKGROUND, "previous"); + + if (!Flags.followUpOomadjUpdates()) return; + + final ArgumentCaptor<Long> followUpTimeCaptor = ArgumentCaptor.forClass(Long.class); + verify(mService.mHandler).sendEmptyMessageAtTime(eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG), + followUpTimeCaptor.capture()); + mInjector.jumpUptimeAheadTo(followUpTimeCaptor.getValue()); + mService.mOomAdjuster.updateOomAdjFollowUpTargetsLocked(); + + assertProcStates(app, PROCESS_STATE_LAST_ACTIVITY, CACHED_APP_MIN_ADJ, + SCHED_GROUP_BACKGROUND, "previous-expired"); + // Follow up should not have been called again. + verify(mService.mHandler).sendEmptyMessageAtTime(eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG), + followUpTimeCaptor.capture()); } @SuppressWarnings("GuardedBy") @@ -814,10 +849,10 @@ public class MockingOomAdjusterTests { MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); BackupRecord backupTarget = new BackupRecord(null, 0, 0, 0); backupTarget.app = app; - doReturn(backupTarget).when(sService.mBackupTargets).get(anyInt()); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + doReturn(backupTarget).when(mService.mBackupTargets).get(anyInt()); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); - doReturn(null).when(sService.mBackupTargets).get(anyInt()); + doReturn(null).when(mService.mBackupTargets).get(anyInt()); assertProcStates(app, PROCESS_STATE_TRANSIENT_BACKGROUND, BACKUP_APP_ADJ, SCHED_GROUP_BACKGROUND); @@ -829,7 +864,7 @@ public class MockingOomAdjusterTests { ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); app.mServices.setHasClientActivities(true); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); assertEquals(PROCESS_STATE_CACHED_ACTIVITY_CLIENT, app.mState.getSetProcState()); @@ -841,7 +876,7 @@ public class MockingOomAdjusterTests { ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); app.mServices.setTreatLikeActivity(true); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); assertEquals(PROCESS_STATE_CACHED_ACTIVITY, app.mState.getSetProcState()); @@ -858,7 +893,7 @@ public class MockingOomAdjusterTests { s.startRequested = true; s.lastActivity = SystemClock.uptimeMillis(); app.mServices.startService(s); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); assertProcStates(app, PROCESS_STATE_SERVICE, SERVICE_B_ADJ, SCHED_GROUP_BACKGROUND); @@ -870,7 +905,7 @@ public class MockingOomAdjusterTests { ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false)); app.mState.setMaxAdj(PERCEPTIBLE_LOW_APP_ADJ); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, PERCEPTIBLE_LOW_APP_ADJ, @@ -884,8 +919,8 @@ public class MockingOomAdjusterTests { MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false)); app.mState.setCurRawAdj(SERVICE_ADJ); app.mState.setCurAdj(SERVICE_ADJ); - doReturn(null).when(sService).getTopApp(); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + doReturn(null).when(mService).getTopApp(); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); assertTrue(ProcessList.CACHED_APP_MIN_ADJ <= app.mState.getSetAdj()); @@ -902,7 +937,7 @@ public class MockingOomAdjusterTests { s.startRequested = true; s.lastActivity = SystemClock.uptimeMillis(); app.mServices.startService(s); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); assertProcStates(app, PROCESS_STATE_SERVICE, SERVICE_ADJ, SCHED_GROUP_BACKGROUND); @@ -918,11 +953,11 @@ public class MockingOomAdjusterTests { ServiceRecord s = bindService(app, client, null, null, Context.BIND_WAIVE_PRIORITY, mock(IBinder.class)); s.startRequested = true; - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); - doReturn(PROCESS_STATE_TOP).when(sService.mAtmInternal).getTopProcessState(); - doReturn(client).when(sService).getTopApp(); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + doReturn(PROCESS_STATE_TOP).when(mService.mAtmInternal).getTopProcessState(); + doReturn(client).when(mService).getTopApp(); updateOomAdj(client, app); - doReturn(null).when(sService).getTopApp(); + doReturn(null).when(mService).getTopApp(); assertProcStates(app, PROCESS_STATE_SERVICE, sFirstCachedAdj, SCHED_GROUP_BACKGROUND); } @@ -937,7 +972,7 @@ public class MockingOomAdjusterTests { client.mServices.setTreatLikeActivity(true); bindService(app, client, null, null, Context.BIND_WAIVE_PRIORITY | Context.BIND_TREAT_LIKE_ACTIVITY, mock(IBinder.class)); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, app); assertEquals(PROCESS_STATE_CACHED_ACTIVITY, app.mState.getSetProcState()); @@ -956,9 +991,9 @@ public class MockingOomAdjusterTests { ConnectionRecord cr = s.getConnections().get(binder).get(0); setFieldValue(ConnectionRecord.class, cr, "activity", mock(ActivityServiceConnectionsHolder.class)); - doReturn(client).when(sService).getTopApp(); + doReturn(client).when(mService).getTopApp(); doReturn(true).when(cr.activity).isActivityVisible(); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, app); assertEquals(FOREGROUND_APP_ADJ, app.mState.getSetAdj()); @@ -971,7 +1006,7 @@ public class MockingOomAdjusterTests { ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false)); bindService(app, app, null, null, 0, mock(IBinder.class)); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, sFirstCachedAdj, SCHED_GROUP_BACKGROUND); @@ -986,7 +1021,7 @@ public class MockingOomAdjusterTests { MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); client.mServices.setTreatLikeActivity(true); bindService(app, client, null, null, 0, mock(IBinder.class)); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, app); assertEquals(PROCESS_STATE_CACHED_EMPTY, app.mState.getSetProcState()); @@ -1005,11 +1040,11 @@ public class MockingOomAdjusterTests { MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); bindService(app, client, null, null, Context.BIND_ALLOW_OOM_MANAGEMENT, mock(IBinder.class)); - doReturn(PROCESS_STATE_TOP).when(sService.mAtmInternal).getTopProcessState(); - doReturn(client).when(sService).getTopApp(); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + doReturn(PROCESS_STATE_TOP).when(mService.mAtmInternal).getTopProcessState(); + doReturn(client).when(mService).getTopApp(); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, app); - doReturn(null).when(sService).getTopApp(); + doReturn(null).when(mService).getTopApp(); assertEquals(PREVIOUS_APP_ADJ, app.mState.getSetAdj()); } @@ -1024,7 +1059,7 @@ public class MockingOomAdjusterTests { bindService(app, client, null, null, Context.BIND_FOREGROUND_SERVICE, mock(IBinder.class)); client.mState.setMaxAdj(PERSISTENT_PROC_ADJ); client.mState.setHasTopUi(true); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, app); assertProcStates(app, PROCESS_STATE_BOUND_FOREGROUND_SERVICE, VISIBLE_APP_ADJ, @@ -1041,7 +1076,7 @@ public class MockingOomAdjusterTests { MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); bindService(app, client, null, null, Context.BIND_IMPORTANT, mock(IBinder.class)); client.mServices.startExecutingService(mock(ServiceRecord.class)); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, app); assertEquals(FOREGROUND_APP_ADJ, app.mState.getSetAdj()); @@ -1056,11 +1091,11 @@ public class MockingOomAdjusterTests { ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID, MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); bindService(app, client, null, null, 0, mock(IBinder.class)); - doReturn(PROCESS_STATE_TOP).when(sService.mAtmInternal).getTopProcessState(); - doReturn(client).when(sService).getTopApp(); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + doReturn(PROCESS_STATE_TOP).when(mService.mAtmInternal).getTopProcessState(); + doReturn(client).when(mService).getTopApp(); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, app); - doReturn(null).when(sService).getTopApp(); + doReturn(null).when(mService).getTopApp(); assertProcStates(app, PROCESS_STATE_BOUND_TOP, VISIBLE_APP_ADJ, SCHED_GROUP_DEFAULT); } @@ -1074,7 +1109,7 @@ public class MockingOomAdjusterTests { MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); bindService(app, client, null, null, Context.BIND_FOREGROUND_SERVICE, mock(IBinder.class)); client.mState.setMaxAdj(PERSISTENT_PROC_ADJ); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, app); assertEquals(PROCESS_STATE_BOUND_FOREGROUND_SERVICE, app.mState.getSetProcState()); @@ -1092,7 +1127,7 @@ public class MockingOomAdjusterTests { MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); bindService(app, client, null, null, Context.BIND_NOT_FOREGROUND, mock(IBinder.class)); client.mState.setMaxAdj(PERSISTENT_PROC_ADJ); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, app); assertEquals(PROCESS_STATE_TRANSIENT_BACKGROUND, app.mState.getSetProcState()); @@ -1108,7 +1143,7 @@ public class MockingOomAdjusterTests { MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); bindService(app, client, null, null, 0, mock(IBinder.class)); client.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, app); assertEquals(PROCESS_STATE_FOREGROUND_SERVICE, client.mState.getSetProcState()); @@ -1128,7 +1163,7 @@ public class MockingOomAdjusterTests { bindService(app, client, null, null, 0, mock(IBinder.class)); // In order to trick OomAdjuster to think it has a short-service, we need this logic. - ServiceRecord s = ServiceRecord.newEmptyInstanceForTest(sService); + ServiceRecord s = ServiceRecord.newEmptyInstanceForTest(mService); s.appInfo = new ApplicationInfo(); s.startRequested = true; s.isForeground = true; @@ -1139,7 +1174,7 @@ public class MockingOomAdjusterTests { client.mServices.setHasForegroundServices(true, FOREGROUND_SERVICE_TYPE_SHORT_SERVICE, /* hasNoneType=*/false); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, app); // Client only has a SHORT_FGS, so it doesn't have BFSL, and that's propagated. @@ -1159,7 +1194,7 @@ public class MockingOomAdjusterTests { MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false)); // In order to trick OomAdjuster to think it has a short-service, we need this logic. - ServiceRecord s = ServiceRecord.newEmptyInstanceForTest(sService); + ServiceRecord s = ServiceRecord.newEmptyInstanceForTest(mService); s.appInfo = new ApplicationInfo(); s.startRequested = true; s.isForeground = true; @@ -1170,7 +1205,7 @@ public class MockingOomAdjusterTests { app2.mServices.setHasForegroundServices(true, FOREGROUND_SERVICE_TYPE_SHORT_SERVICE, /* hasNoneType=*/false); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app2); // Client only has a SHORT_FGS, so it doesn't have BFSL, and that's propagated. @@ -1211,11 +1246,11 @@ public class MockingOomAdjusterTests { bindService(app, client, null, null, Context.BIND_ABOVE_CLIENT, mock(IBinder.class)); BackupRecord backupTarget = new BackupRecord(null, 0, 0, 0); backupTarget.app = client; - doReturn(backupTarget).when(sService.mBackupTargets).get(anyInt()); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + doReturn(backupTarget).when(mService.mBackupTargets).get(anyInt()); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, app); - doReturn(null).when(sService.mBackupTargets).get(anyInt()); + doReturn(null).when(mService.mBackupTargets).get(anyInt()); assertEquals(BACKUP_APP_ADJ, app.mState.getSetAdj()); assertNoBfsl(app); @@ -1236,7 +1271,7 @@ public class MockingOomAdjusterTests { MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); bindService(app, client, null, null, Context.BIND_NOT_PERCEPTIBLE, mock(IBinder.class)); client.mState.setRunningRemoteAnimation(true); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, app); assertEquals(PERCEPTIBLE_LOW_APP_ADJ, app.mState.getSetAdj()); @@ -1251,7 +1286,7 @@ public class MockingOomAdjusterTests { MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); bindService(app, client, null, null, Context.BIND_NOT_VISIBLE, mock(IBinder.class)); client.mState.setRunningRemoteAnimation(true); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, app); assertEquals(PERCEPTIBLE_APP_ADJ, app.mState.getSetAdj()); @@ -1266,7 +1301,7 @@ public class MockingOomAdjusterTests { MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); bindService(app, client, null, null, 0, mock(IBinder.class)); client.mState.setHasOverlayUi(true); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, app); assertEquals(PERCEPTIBLE_APP_ADJ, app.mState.getSetAdj()); @@ -1284,12 +1319,12 @@ public class MockingOomAdjusterTests { Context.BIND_ALMOST_PERCEPTIBLE | Context.BIND_NOT_FOREGROUND, mock(IBinder.class)); client.mState.setMaxAdj(PERSISTENT_PROC_ADJ); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, app); assertEquals(PERCEPTIBLE_MEDIUM_APP_ADJ + 2, app.mState.getSetAdj()); - sService.mOomAdjuster.resetInternal(); + mService.mOomAdjuster.resetInternal(); } { @@ -1303,13 +1338,13 @@ public class MockingOomAdjusterTests { Context.BIND_ALMOST_PERCEPTIBLE | Context.BIND_NOT_FOREGROUND, mock(IBinder.class)); client.mState.setMaxAdj(PERSISTENT_PROC_ADJ); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, app); doReturn(false).when(wpc).isHeavyWeightProcess(); assertEquals(PERCEPTIBLE_MEDIUM_APP_ADJ + 2, app.mState.getSetAdj()); - sService.mOomAdjuster.resetInternal(); + mService.mOomAdjuster.resetInternal(); } { @@ -1321,12 +1356,12 @@ public class MockingOomAdjusterTests { Context.BIND_ALMOST_PERCEPTIBLE, mock(IBinder.class)); client.mState.setMaxAdj(PERSISTENT_PROC_ADJ); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, app); assertEquals(PERCEPTIBLE_APP_ADJ + 1, app.mState.getSetAdj()); - sService.mOomAdjuster.resetInternal(); + mService.mOomAdjuster.resetInternal(); } { @@ -1340,13 +1375,13 @@ public class MockingOomAdjusterTests { Context.BIND_ALMOST_PERCEPTIBLE, mock(IBinder.class)); client.mState.setMaxAdj(PERSISTENT_PROC_ADJ); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, app); doReturn(false).when(wpc).isHeavyWeightProcess(); assertEquals(PERCEPTIBLE_APP_ADJ + 1, app.mState.getSetAdj()); - sService.mOomAdjuster.resetInternal(); + mService.mOomAdjuster.resetInternal(); } } @@ -1359,7 +1394,7 @@ public class MockingOomAdjusterTests { MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); bindService(app, client, null, null, 0, mock(IBinder.class)); client.mState.setRunningRemoteAnimation(true); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, app); assertEquals(VISIBLE_APP_ADJ, app.mState.getSetAdj()); @@ -1375,7 +1410,7 @@ public class MockingOomAdjusterTests { bindService(app, client, null, null, Context.BIND_IMPORTANT_BACKGROUND, mock(IBinder.class)); client.mState.setHasOverlayUi(true); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, app); assertEquals(PROCESS_STATE_IMPORTANT_BACKGROUND, app.mState.getSetProcState()); @@ -1402,7 +1437,7 @@ public class MockingOomAdjusterTests { MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); bindProvider(app, client, null, null, false); client.mServices.setTreatLikeActivity(true); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app, client); assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, sFirstCachedAdj, SCHED_GROUP_BACKGROUND); @@ -1416,11 +1451,11 @@ public class MockingOomAdjusterTests { ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID, MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); bindProvider(app, client, null, null, false); - doReturn(PROCESS_STATE_TOP).when(sService.mAtmInternal).getTopProcessState(); - doReturn(client).when(sService).getTopApp(); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + doReturn(PROCESS_STATE_TOP).when(mService.mAtmInternal).getTopProcessState(); + doReturn(client).when(mService).getTopApp(); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, app); - doReturn(null).when(sService).getTopApp(); + doReturn(null).when(mService).getTopApp(); assertProcStates(app, PROCESS_STATE_BOUND_TOP, FOREGROUND_APP_ADJ, SCHED_GROUP_DEFAULT); } @@ -1434,7 +1469,7 @@ public class MockingOomAdjusterTests { MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); client.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); bindProvider(app, client, null, null, false); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, app); assertProcStates(app, PROCESS_STATE_BOUND_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, @@ -1452,7 +1487,7 @@ public class MockingOomAdjusterTests { MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); // In order to trick OomAdjuster to think it has a short-service, we need this logic. - ServiceRecord s = ServiceRecord.newEmptyInstanceForTest(sService); + ServiceRecord s = ServiceRecord.newEmptyInstanceForTest(mService); s.appInfo = new ApplicationInfo(); s.startRequested = true; s.isForeground = true; @@ -1464,7 +1499,7 @@ public class MockingOomAdjusterTests { client.mServices.setHasForegroundServices(true, FOREGROUND_SERVICE_TYPE_SHORT_SERVICE, /* hasNoneType=*/false); bindProvider(app, client, null, null, false); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, app); // Client only has a SHORT_FGS, so it doesn't have BFSL, and that's propagated. @@ -1486,7 +1521,7 @@ public class MockingOomAdjusterTests { ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID, MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); bindProvider(app, client, null, null, true); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, app); assertProcStates(app, PROCESS_STATE_IMPORTANT_FOREGROUND, FOREGROUND_APP_ADJ, @@ -1499,11 +1534,25 @@ public class MockingOomAdjusterTests { ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false)); app.mProviders.setLastProviderTime(SystemClock.uptimeMillis()); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); assertProcStates(app, PROCESS_STATE_LAST_ACTIVITY, PREVIOUS_APP_ADJ, - SCHED_GROUP_BACKGROUND); + SCHED_GROUP_BACKGROUND, "recent-provider"); + + if (!Flags.followUpOomadjUpdates()) return; + + final ArgumentCaptor<Long> followUpTimeCaptor = ArgumentCaptor.forClass(Long.class); + verify(mService.mHandler).sendEmptyMessageAtTime(eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG), + followUpTimeCaptor.capture()); + mInjector.jumpUptimeAheadTo(followUpTimeCaptor.getValue()); + mService.mOomAdjuster.updateOomAdjFollowUpTargetsLocked(); + + assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, sFirstCachedAdj, SCHED_GROUP_BACKGROUND, + "cch-empty"); + // Follow up should not have been called again. + verify(mService.mHandler).sendEmptyMessageAtTime(eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG), + followUpTimeCaptor.capture()); } @SuppressWarnings("GuardedBy") @@ -1517,11 +1566,11 @@ public class MockingOomAdjusterTests { ProcessRecord client2 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID, MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false)); bindService(client, client2, null, null, 0, mock(IBinder.class)); - doReturn(PROCESS_STATE_TOP).when(sService.mAtmInternal).getTopProcessState(); - doReturn(client2).when(sService).getTopApp(); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + doReturn(PROCESS_STATE_TOP).when(mService.mAtmInternal).getTopProcessState(); + doReturn(client2).when(mService).getTopApp(); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, client2, app); - doReturn(null).when(sService).getTopApp(); + doReturn(null).when(mService).getTopApp(); assertProcStates(app, PROCESS_STATE_BOUND_TOP, VISIBLE_APP_ADJ, SCHED_GROUP_DEFAULT); @@ -1539,7 +1588,7 @@ public class MockingOomAdjusterTests { MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false)); bindService(app, client2, null, null, 0, mock(IBinder.class)); client2.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, client2, app); assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, @@ -1559,7 +1608,7 @@ public class MockingOomAdjusterTests { MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false)); bindService(client, client2, null, null, 0, mock(IBinder.class)); client2.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, client2, app); assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, @@ -1584,7 +1633,7 @@ public class MockingOomAdjusterTests { // Note: We add processes to LRU but still call updateOomAdjLocked() with a specific // processes. setProcessesToLru(app, client, client2); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, @@ -1598,7 +1647,7 @@ public class MockingOomAdjusterTests { assertBfsl(client2); client2.mServices.setHasForegroundServices(false, 0, /* hasNoneType=*/false); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client2); assertEquals(PROCESS_STATE_CACHED_EMPTY, client2.mState.getSetProcState()); @@ -1622,7 +1671,7 @@ public class MockingOomAdjusterTests { MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false)); bindService(client2, client, null, null, 0, mock(IBinder.class)); client.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app, client, client2); assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, @@ -1649,7 +1698,7 @@ public class MockingOomAdjusterTests { bindService(client, client2, null, null, 0, mock(IBinder.class)); bindService(client2, client, null, null, 0, mock(IBinder.class)); client.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app, client, client2); assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, @@ -1683,7 +1732,7 @@ public class MockingOomAdjusterTests { bindService(client3, client4, null, null, 0, mock(IBinder.class)); bindService(client4, client3, null, null, 0, mock(IBinder.class)); client.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app, client, client2, client3, client4); assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, @@ -1720,7 +1769,7 @@ public class MockingOomAdjusterTests { MOCKAPP4_PROCESSNAME, MOCKAPP4_PACKAGENAME, false)); client3.mState.setForcingToImportant(new Object()); bindService(app, client3, null, null, 0, mock(IBinder.class)); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app, client, client2, client3); assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, @@ -1746,7 +1795,7 @@ public class MockingOomAdjusterTests { MOCKAPP4_PROCESSNAME, MOCKAPP4_PACKAGENAME, false)); client3.mState.setForcingToImportant(new Object()); bindService(app, client3, null, null, 0, mock(IBinder.class)); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app, client, client2, client3); assertProcStates(app, PROCESS_STATE_TRANSIENT_BACKGROUND, PERCEPTIBLE_APP_ADJ, @@ -1773,7 +1822,7 @@ public class MockingOomAdjusterTests { MOCKAPP5_PROCESSNAME, MOCKAPP5_PACKAGENAME, false)); client4.mState.setForcingToImportant(new Object()); bindService(app, client4, null, null, 0, mock(IBinder.class)); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app, client, client2, client3, client4); assertProcStates(app, PROCESS_STATE_TRANSIENT_BACKGROUND, PERCEPTIBLE_APP_ADJ, @@ -1802,7 +1851,7 @@ public class MockingOomAdjusterTests { MOCKAPP5_PROCESSNAME, MOCKAPP5_PACKAGENAME, false)); client4.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); bindService(app, client4, null, null, 0, mock(IBinder.class)); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app, client, client2, client3, client4); assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, @@ -1828,7 +1877,7 @@ public class MockingOomAdjusterTests { MOCKAPP4_PROCESSNAME, MOCKAPP4_PACKAGENAME, false)); client3.mState.setForcingToImportant(new Object()); bindService(app, client3, null, null, 0, mock(IBinder.class)); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, client2, client3, app); assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, @@ -1848,7 +1897,7 @@ public class MockingOomAdjusterTests { MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false)); bindProvider(client, client2, null, null, false); client2.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, client2, app); assertProcStates(app, PROCESS_STATE_BOUND_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, @@ -1869,7 +1918,7 @@ public class MockingOomAdjusterTests { bindProvider(client, client2, null, null, false); client2.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); bindService(client2, app, null, null, 0, mock(IBinder.class)); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app, client, client2); assertProcStates(app, PROCESS_STATE_BOUND_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, @@ -1889,7 +1938,7 @@ public class MockingOomAdjusterTests { MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false)); bindProvider(client, client2, null, null, false); client2.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, client2, app); assertProcStates(app, PROCESS_STATE_BOUND_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, @@ -1910,7 +1959,7 @@ public class MockingOomAdjusterTests { bindProvider(client, client2, null, null, false); client2.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); bindProvider(client2, app, null, null, false); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app, client, client2); assertProcStates(app, PROCESS_STATE_BOUND_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, @@ -1936,7 +1985,7 @@ public class MockingOomAdjusterTests { client1.mState.setMaxAdj(PERSISTENT_PROC_ADJ); client2.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client1, client2, app1, app2); assertProcStates(app1, PROCESS_STATE_BOUND_FOREGROUND_SERVICE, VISIBLE_APP_ADJ, @@ -1957,7 +2006,7 @@ public class MockingOomAdjusterTests { assertProcStates(app2, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, SCHED_GROUP_DEFAULT); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_ASLEEP); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_ASLEEP); updateOomAdj(client1, client2, app1, app2); assertProcStates(app1, PROCESS_STATE_IMPORTANT_FOREGROUND, VISIBLE_APP_ADJ, SCHED_GROUP_TOP_APP); @@ -2028,9 +2077,9 @@ public class MockingOomAdjusterTests { SCHED_GROUP_DEFAULT); client2.mState.setHasOverlayUi(false); - doReturn(PROCESS_STATE_TOP).when(sService.mAtmInternal).getTopProcessState(); - doReturn(client2).when(sService).getTopApp(); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + doReturn(PROCESS_STATE_TOP).when(mService.mAtmInternal).getTopProcessState(); + doReturn(client2).when(mService).getTopApp(); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client2, app2); assertProcStates(app2, PROCESS_STATE_BOUND_TOP, VISIBLE_APP_ADJ, @@ -2047,7 +2096,7 @@ public class MockingOomAdjusterTests { client1.mState.setMaxAdj(PERSISTENT_PROC_ADJ); app1.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); bindService(app1, client1, null, null, Context.BIND_NOT_PERCEPTIBLE, mock(IBinder.class)); @@ -2068,7 +2117,7 @@ public class MockingOomAdjusterTests { client1.mState.setMaxAdj(PERSISTENT_PROC_ADJ); app1.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); bindService(app1, client1, null, null, Context.BIND_ALMOST_PERCEPTIBLE, mock(IBinder.class)); @@ -2088,7 +2137,7 @@ public class MockingOomAdjusterTests { app.setPendingFinishAttach(true); app.mState.setHasForegroundActivities(false); - sService.mOomAdjuster.setAttachingProcessStatesLSP(app); + mService.mOomAdjuster.setAttachingProcessStatesLSP(app); updateOomAdj(app); assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, FOREGROUND_APP_ADJ, @@ -2102,9 +2151,9 @@ public class MockingOomAdjusterTests { MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false)); app.setPendingFinishAttach(true); app.mState.setHasForegroundActivities(true); - doReturn(app).when(sService).getTopApp(); + doReturn(app).when(mService).getTopApp(); - sService.mOomAdjuster.setAttachingProcessStatesLSP(app); + mService.mOomAdjuster.setAttachingProcessStatesLSP(app); updateOomAdj(app); assertProcStates(app, PROCESS_STATE_TOP, FOREGROUND_APP_ADJ, @@ -2124,10 +2173,10 @@ public class MockingOomAdjusterTests { MOCKAPP4_PROCESSNAME, MOCKAPP3_PACKAGENAME, false)); final ProcessRecord app3 = spy(makeDefaultProcessRecord(MOCKAPP5_PID, MOCKAPP5_UID, MOCKAPP5_PROCESSNAME, MOCKAPP5_PACKAGENAME, false)); - final UidRecord app1UidRecord = new UidRecord(MOCKAPP_UID, sService); - final UidRecord app2UidRecord = new UidRecord(MOCKAPP2_UID, sService); - final UidRecord app3UidRecord = new UidRecord(MOCKAPP5_UID, sService); - final UidRecord clientUidRecord = new UidRecord(MOCKAPP3_UID, sService); + final UidRecord app1UidRecord = new UidRecord(MOCKAPP_UID, mService); + final UidRecord app2UidRecord = new UidRecord(MOCKAPP2_UID, mService); + final UidRecord app3UidRecord = new UidRecord(MOCKAPP5_UID, mService); + final UidRecord clientUidRecord = new UidRecord(MOCKAPP3_UID, mService); app1.setUidRecord(app1UidRecord); app2.setUidRecord(app2UidRecord); app3.setUidRecord(app3UidRecord); @@ -2137,7 +2186,7 @@ public class MockingOomAdjusterTests { client1.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); client2.mState.setForcingToImportant(new Object()); setProcessesToLru(app1, app2, app3, client1, client2); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); final ComponentName cn1 = ComponentName.unflattenFromString( MOCKAPP_PACKAGENAME + "/.TestService"); @@ -2164,10 +2213,10 @@ public class MockingOomAdjusterTests { c2s.startRequested = true; try { - sService.mOomAdjuster.mActiveUids.put(MOCKAPP_UID, app1UidRecord); - sService.mOomAdjuster.mActiveUids.put(MOCKAPP2_UID, app2UidRecord); - sService.mOomAdjuster.mActiveUids.put(MOCKAPP5_UID, app3UidRecord); - sService.mOomAdjuster.mActiveUids.put(MOCKAPP3_UID, clientUidRecord); + mService.mOomAdjuster.mActiveUids.put(MOCKAPP_UID, app1UidRecord); + mService.mOomAdjuster.mActiveUids.put(MOCKAPP2_UID, app2UidRecord); + mService.mOomAdjuster.mActiveUids.put(MOCKAPP5_UID, app3UidRecord); + mService.mOomAdjuster.mActiveUids.put(MOCKAPP3_UID, clientUidRecord); setServiceMap(s1, MOCKAPP_UID, cn1); setServiceMap(s2, MOCKAPP2_UID, cn2); @@ -2195,10 +2244,10 @@ public class MockingOomAdjusterTests { app2UidRecord.setIdle(true); app3UidRecord.setIdle(true); clientUidRecord.setIdle(true); - doReturn(ActivityManager.APP_START_MODE_DELAYED).when(sService) + doReturn(ActivityManager.APP_START_MODE_DELAYED).when(mService) .getAppStartModeLOSP(anyInt(), any(String.class), anyInt(), anyInt(), anyBoolean(), anyBoolean(), anyBoolean()); - doNothing().when(sService.mServices) + doNothing().when(mService.mServices) .scheduleServiceTimeoutLocked(any(ProcessRecord.class)); updateOomAdj(client1, client2, app1, app2, app3); @@ -2206,11 +2255,11 @@ public class MockingOomAdjusterTests { assertEquals(PROCESS_STATE_SERVICE, app1.mState.getSetProcState()); assertEquals(PROCESS_STATE_SERVICE, client2.mState.getSetProcState()); } finally { - doCallRealMethod().when(sService) + doCallRealMethod().when(mService) .getAppStartModeLOSP(anyInt(), any(String.class), anyInt(), anyInt(), anyBoolean(), anyBoolean(), anyBoolean()); - sService.mServices.mServiceMap.clear(); - sService.mOomAdjuster.mActiveUids.clear(); + mService.mServices.mServiceMap.clear(); + mService.mOomAdjuster.mActiveUids.clear(); } } @@ -2224,7 +2273,7 @@ public class MockingOomAdjusterTests { MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); app2.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app, app2); assertProcStates(app, PROCESS_STATE_TRANSIENT_BACKGROUND, PERCEPTIBLE_APP_ADJ, @@ -2244,7 +2293,7 @@ public class MockingOomAdjusterTests { MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); app2.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); bindService(app, app2, null, null, 0, mock(IBinder.class)); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app, app2); assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, @@ -2268,7 +2317,7 @@ public class MockingOomAdjusterTests { bindService(app2, app3, null, null, 0, mock(IBinder.class)); app3.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); bindService(app3, app, null, null, 0, mock(IBinder.class)); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app, app2, app3); assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, @@ -2313,7 +2362,7 @@ public class MockingOomAdjusterTests { MOCKAPP5_PROCESSNAME, MOCKAPP5_PACKAGENAME, false)); app5.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); bindService(app, app5, null, s, 0, mock(IBinder.class)); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app, app2, app3, app4, app5); assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, @@ -2355,7 +2404,7 @@ public class MockingOomAdjusterTests { MOCKAPP5_PROCESSNAME, MOCKAPP5_PACKAGENAME, false)); app5.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); bindService(app, app5, null, s, 0, mock(IBinder.class)); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app5, app4, app3, app2, app); assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, @@ -2397,7 +2446,7 @@ public class MockingOomAdjusterTests { MOCKAPP5_PROCESSNAME, MOCKAPP5_PACKAGENAME, false)); app5.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); bindService(app, app5, null, s, 0, mock(IBinder.class)); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app3, app4, app2, app, app5); assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, @@ -2437,7 +2486,7 @@ public class MockingOomAdjusterTests { client3.mState.setMaxAdj(PERSISTENT_PROC_ADJ); bindService(app, client3, null, null, Context.BIND_INCLUDE_CAPABILITIES, mock(IBinder.class)); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app, client, client2, client3); final int expected = PROCESS_CAPABILITY_ALL & ~PROCESS_CAPABILITY_BFSL; @@ -2468,7 +2517,7 @@ public class MockingOomAdjusterTests { MOCKAPP5_PROCESSNAME, MOCKAPP5_PACKAGENAME, false)); app5.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); bindProvider(app, app5, cr, null, false); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app, app2, app3, app4, app5); assertProcStates(app, PROCESS_STATE_BOUND_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, @@ -2512,8 +2561,8 @@ public class MockingOomAdjusterTests { doCallRealMethod().when(s).getConnections(); s.startRequested = true; s.lastActivity = now; - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); - sService.mOomAdjuster.mNumServiceProcs = 3; + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mOomAdjuster.mNumServiceProcs = 3; updateOomAdj(app3, app2, app); assertEquals(SERVICE_B_ADJ, app3.mState.getSetAdj()); @@ -2530,15 +2579,15 @@ public class MockingOomAdjusterTests { MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); final int userOwner = 0; final int userOther = 1; - final int cachedAdj1 = sService.mConstants.USE_TIERED_CACHED_ADJ + final int cachedAdj1 = mService.mConstants.USE_TIERED_CACHED_ADJ ? CACHED_APP_MIN_ADJ + 10 : CACHED_APP_MIN_ADJ + ProcessList.CACHED_APP_IMPORTANCE_LEVELS; - final int cachedAdj2 = sService.mConstants.USE_TIERED_CACHED_ADJ + final int cachedAdj2 = mService.mConstants.USE_TIERED_CACHED_ADJ ? CACHED_APP_MIN_ADJ + 10 : cachedAdj1 + ProcessList.CACHED_APP_IMPORTANCE_LEVELS * 2; - doReturn(userOwner).when(sService.mUserController).getCurrentUserId(); + doReturn(userOwner).when(mService.mUserController).getCurrentUserId(); - final ArrayList<ProcessRecord> lru = sService.mProcessList.getLruProcessesLOSP(); + final ArrayList<ProcessRecord> lru = mService.mProcessList.getLruProcessesLOSP(); lru.clear(); lru.add(app2); lru.add(app); @@ -2549,10 +2598,10 @@ public class MockingOomAdjusterTests { MOCKAPP2_PACKAGENAME + "/.TestService"); final long now = SystemClock.uptimeMillis(); - sService.mConstants.KEEP_WARMING_SERVICES.clear(); + mService.mConstants.KEEP_WARMING_SERVICES.clear(); final ServiceInfo si = mock(ServiceInfo.class); si.applicationInfo = mock(ApplicationInfo.class); - ServiceRecord s = spy(new ServiceRecord(sService, cn, cn, null, 0, null, + ServiceRecord s = spy(new ServiceRecord(mService, cn, cn, null, 0, null, si, false, null)); doReturn(new ArrayMap<IBinder, ArrayList<ConnectionRecord>>()).when(s).getConnections(); s.startRequested = true; @@ -2564,16 +2613,16 @@ public class MockingOomAdjusterTests { final ServiceInfo si2 = mock(ServiceInfo.class); si2.applicationInfo = mock(ApplicationInfo.class); si2.applicationInfo.uid = MOCKAPP2_UID_OTHER; - ServiceRecord s2 = spy(new ServiceRecord(sService, cn2, cn2, null, 0, null, + ServiceRecord s2 = spy(new ServiceRecord(mService, cn2, cn2, null, 0, null, si2, false, null)); doReturn(new ArrayMap<IBinder, ArrayList<ConnectionRecord>>()).when(s2).getConnections(); s2.startRequested = true; - s2.lastActivity = now - sService.mConstants.MAX_SERVICE_INACTIVITY - 1; + s2.lastActivity = now - mService.mConstants.MAX_SERVICE_INACTIVITY - 1; app2.mServices.startService(s2); app2.mState.setHasShownUi(false); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(); assertProcStates(app, true, PROCESS_STATE_SERVICE, cachedAdj1, "cch-started-ui-services"); @@ -2590,7 +2639,7 @@ public class MockingOomAdjusterTests { app.mState.setSetProcState(PROCESS_STATE_NONEXISTENT); app.mState.setAdjType(null); app.mState.setSetAdj(UNKNOWN_ADJ); - s.lastActivity = now - sService.mConstants.MAX_SERVICE_INACTIVITY - 1; + s.lastActivity = now - mService.mConstants.MAX_SERVICE_INACTIVITY - 1; updateOomAdj(); assertProcStates(app, true, PROCESS_STATE_SERVICE, cachedAdj1, "cch-started-services"); @@ -2600,9 +2649,9 @@ public class MockingOomAdjusterTests { app.mState.setAdjType(null); app.mState.setSetAdj(UNKNOWN_ADJ); app.mState.setHasShownUi(true); - sService.mConstants.KEEP_WARMING_SERVICES.add(cn); - sService.mConstants.KEEP_WARMING_SERVICES.add(cn2); - s = spy(new ServiceRecord(sService, cn, cn, null, 0, null, + mService.mConstants.KEEP_WARMING_SERVICES.add(cn); + mService.mConstants.KEEP_WARMING_SERVICES.add(cn2); + s = spy(new ServiceRecord(mService, cn, cn, null, 0, null, si, false, null)); doReturn(new ArrayMap<IBinder, ArrayList<ConnectionRecord>>()).when(s).getConnections(); s.startRequested = true; @@ -2618,14 +2667,14 @@ public class MockingOomAdjusterTests { app.mState.setAdjType(null); app.mState.setSetAdj(UNKNOWN_ADJ); app.mState.setHasShownUi(false); - s.lastActivity = now - sService.mConstants.MAX_SERVICE_INACTIVITY - 1; + s.lastActivity = now - mService.mConstants.MAX_SERVICE_INACTIVITY - 1; updateOomAdj(); assertProcStates(app, false, PROCESS_STATE_SERVICE, SERVICE_ADJ, "started-services"); assertProcStates(app2, true, PROCESS_STATE_SERVICE, cachedAdj1, "cch-started-services"); - doReturn(userOther).when(sService.mUserController).getCurrentUserId(); - sService.mOomAdjuster.handleUserSwitchedLocked(); + doReturn(userOther).when(mService.mUserController).getCurrentUserId(); + mService.mOomAdjuster.handleUserSwitchedLocked(); updateOomAdj(); assertProcStates(app, true, PROCESS_STATE_SERVICE, cachedAdj1, "cch-started-services"); @@ -2637,9 +2686,9 @@ public class MockingOomAdjusterTests { public void testUpdateOomAdj_DoOne_AboveClient_SameProcess() { ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); - doReturn(PROCESS_STATE_TOP).when(sService.mAtmInternal).getTopProcessState(); - doReturn(app).when(sService).getTopApp(); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + doReturn(PROCESS_STATE_TOP).when(mService.mAtmInternal).getTopProcessState(); + doReturn(app).when(mService).getTopApp(); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); assertEquals(FOREGROUND_APP_ADJ, app.mState.getSetAdj()); @@ -2672,8 +2721,8 @@ public class MockingOomAdjusterTests { s = bindService(app3, app2, null, null, 0, mock(IBinder.class)); s.lastActivity = now; - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); - sService.mOomAdjuster.mNumServiceProcs = 3; + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mOomAdjuster.mNumServiceProcs = 3; updateOomAdj(app, app2, app3); assertEquals(SERVICE_ADJ, app.mState.getSetAdj()); @@ -2688,9 +2737,9 @@ public class MockingOomAdjusterTests { public void testUpdateOomAdj_DoOne_AboveClient_NotStarted() { ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); - doReturn(PROCESS_STATE_TOP).when(sService.mAtmInternal).getTopProcessState(); - doReturn(app).when(sService).getTopApp(); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + doReturn(PROCESS_STATE_TOP).when(mService.mAtmInternal).getTopProcessState(); + doReturn(app).when(mService).getTopApp(); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); assertEquals(FOREGROUND_APP_ADJ, app.mState.getSetAdj()); @@ -2718,7 +2767,7 @@ public class MockingOomAdjusterTests { ServiceRecord s = makeServiceRecord(app); s.startRequested = true; s.lastActivity = SystemClock.uptimeMillis(); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(); assertProcStates(app, PROCESS_STATE_SERVICE, SERVICE_ADJ, SCHED_GROUP_BACKGROUND); @@ -2738,7 +2787,7 @@ public class MockingOomAdjusterTests { ServiceRecord s = makeServiceRecord(app); s.startRequested = true; s.lastActivity = SystemClock.uptimeMillis(); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdjPending(app); assertProcStates(app, PROCESS_STATE_SERVICE, SERVICE_ADJ, SCHED_GROUP_BACKGROUND); @@ -2760,7 +2809,7 @@ public class MockingOomAdjusterTests { ServiceRecord s = makeServiceRecord(app); s.startRequested = true; s.lastActivity = SystemClock.uptimeMillis(); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(); assertProcStates(app, PROCESS_STATE_SERVICE, SERVICE_ADJ, SCHED_GROUP_BACKGROUND); @@ -2781,7 +2830,7 @@ public class MockingOomAdjusterTests { ServiceRecord s = makeServiceRecord(app); s.startRequested = true; s.lastActivity = SystemClock.uptimeMillis(); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdjPending(app); assertProcStates(app, PROCESS_STATE_SERVICE, SERVICE_ADJ, SCHED_GROUP_BACKGROUND); @@ -2809,7 +2858,7 @@ public class MockingOomAdjusterTests { client.mState.setMaxAdj(PERSISTENT_PROC_ADJ); attributedClient.mServices.setHasForegroundServices(true, 0, true); bindService(sandboxService, client, attributedClient, null, 0, mock(IBinder.class)); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(); assertProcStates(client, PROCESS_STATE_PERSISTENT, PERSISTENT_PROC_ADJ, SCHED_GROUP_DEFAULT); @@ -2830,13 +2879,13 @@ public class MockingOomAdjusterTests { // App1 binds to app2 and gets temp allowlisted. bindService(app2, app, null, null, 0, mock(IBinder.class)); - sService.mOomAdjuster.setUidTempAllowlistStateLSP(MOCKAPP_UID, true); + mService.mOomAdjuster.setUidTempAllowlistStateLSP(MOCKAPP_UID, true); assertEquals(true, app.getUidRecord().isSetAllowListed()); assertEquals(true, app.mOptRecord.shouldNotFreeze()); assertEquals(true, app2.mOptRecord.shouldNotFreeze()); - sService.mOomAdjuster.setUidTempAllowlistStateLSP(MOCKAPP_UID, false); + mService.mOomAdjuster.setUidTempAllowlistStateLSP(MOCKAPP_UID, false); assertEquals(false, app.getUidRecord().isSetAllowListed()); assertEquals(false, app.mOptRecord.shouldNotFreeze()); assertEquals(false, app2.mOptRecord.shouldNotFreeze()); @@ -2856,8 +2905,8 @@ public class MockingOomAdjusterTests { // App1 and app2 both bind to app3 and get temp allowlisted. bindService(app3, app, null, null, 0, mock(IBinder.class)); bindService(app3, app2, null, null, 0, mock(IBinder.class)); - sService.mOomAdjuster.setUidTempAllowlistStateLSP(MOCKAPP_UID, true); - sService.mOomAdjuster.setUidTempAllowlistStateLSP(MOCKAPP2_UID, true); + mService.mOomAdjuster.setUidTempAllowlistStateLSP(MOCKAPP_UID, true); + mService.mOomAdjuster.setUidTempAllowlistStateLSP(MOCKAPP2_UID, true); assertEquals(true, app.getUidRecord().isSetAllowListed()); assertEquals(true, app2.getUidRecord().isSetAllowListed()); @@ -2866,7 +2915,7 @@ public class MockingOomAdjusterTests { assertEquals(true, app3.mOptRecord.shouldNotFreeze()); // Remove app1 from allowlist. - sService.mOomAdjuster.setUidTempAllowlistStateLSP(MOCKAPP_UID, false); + mService.mOomAdjuster.setUidTempAllowlistStateLSP(MOCKAPP_UID, false); assertEquals(false, app.getUidRecord().isSetAllowListed()); assertEquals(true, app2.getUidRecord().isSetAllowListed()); assertEquals(false, app.mOptRecord.shouldNotFreeze()); @@ -2874,7 +2923,7 @@ public class MockingOomAdjusterTests { assertEquals(true, app3.mOptRecord.shouldNotFreeze()); // Now remove app2 from allowlist. - sService.mOomAdjuster.setUidTempAllowlistStateLSP(MOCKAPP2_UID, false); + mService.mOomAdjuster.setUidTempAllowlistStateLSP(MOCKAPP2_UID, false); assertEquals(false, app.getUidRecord().isSetAllowListed()); assertEquals(false, app2.getUidRecord().isSetAllowListed()); assertEquals(false, app.mOptRecord.shouldNotFreeze()); @@ -2882,6 +2931,73 @@ public class MockingOomAdjusterTests { assertEquals(false, app3.mOptRecord.shouldNotFreeze()); } + @SuppressWarnings("GuardedBy") + @Test + public void testUpdateOomAdj_DoAll_ClientlessService() { + ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, + MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false)); + + setProcessesToLru(app); + ServiceRecord s = makeServiceRecord(app); + s.startRequested = true; + s.lastActivity = SystemClock.uptimeMillis(); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + updateOomAdj(); + assertProcStates(app, PROCESS_STATE_SERVICE, SERVICE_ADJ, SCHED_GROUP_BACKGROUND, + "started-services"); + + if (!Flags.followUpOomadjUpdates()) return; + + final ArgumentCaptor<Long> followUpTimeCaptor = ArgumentCaptor.forClass(Long.class); + verify(mService.mHandler).sendEmptyMessageAtTime( + eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG), followUpTimeCaptor.capture()); + mInjector.jumpUptimeAheadTo(followUpTimeCaptor.getValue()); + mService.mOomAdjuster.updateOomAdjFollowUpTargetsLocked(); + + assertProcStates(app, PROCESS_STATE_SERVICE, sFirstCachedAdj, SCHED_GROUP_BACKGROUND, + "cch-started-services"); + // Follow up should not have been called again. + verify(mService.mHandler).sendEmptyMessageAtTime(eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG), + followUpTimeCaptor.capture()); + } + + @SuppressWarnings("GuardedBy") + @Test + public void testUpdateOomAdj_DoAll_Multiple_Provider_Retention() { + ProcessRecord app1 = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, + MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false)); + ProcessRecord app2 = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID, + MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); + app1.mProviders.setLastProviderTime(SystemClock.uptimeMillis()); + app2.mProviders.setLastProviderTime(SystemClock.uptimeMillis() + 2000); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + setProcessesToLru(app1, app2); + mService.mOomAdjuster.updateOomAdjLocked(OOM_ADJ_REASON_NONE); + + assertProcStates(app1, PROCESS_STATE_LAST_ACTIVITY, PREVIOUS_APP_ADJ, + SCHED_GROUP_BACKGROUND, "recent-provider"); + assertProcStates(app2, PROCESS_STATE_LAST_ACTIVITY, PREVIOUS_APP_ADJ, + SCHED_GROUP_BACKGROUND, "recent-provider"); + + if (!Flags.followUpOomadjUpdates()) return; + + final ArgumentCaptor<Long> followUpTimeCaptor = ArgumentCaptor.forClass(Long.class); + verify(mService.mHandler, atLeastOnce()).sendEmptyMessageAtTime( + eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG), followUpTimeCaptor.capture()); + mInjector.jumpUptimeAheadTo(followUpTimeCaptor.getValue()); + mService.mOomAdjuster.updateOomAdjFollowUpTargetsLocked(); + + assertProcStates(app1, PROCESS_STATE_CACHED_EMPTY, sFirstCachedAdj, SCHED_GROUP_BACKGROUND, + "cch-empty"); + + verify(mService.mHandler, atLeastOnce()).sendEmptyMessageAtTime( + eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG), followUpTimeCaptor.capture()); + mInjector.jumpUptimeAheadTo(followUpTimeCaptor.getValue()); + mService.mOomAdjuster.updateOomAdjFollowUpTargetsLocked(); + assertProcStates(app2, PROCESS_STATE_CACHED_EMPTY, sFirstCachedAdj, SCHED_GROUP_BACKGROUND, + "cch-empty"); + } + private ProcessRecord makeDefaultProcessRecord(int pid, int uid, String processName, String packageName, boolean hasShownUi) { return new ProcessRecordBuilder(pid, uid, processName, packageName).setHasShownUi( @@ -2904,7 +3020,7 @@ public class MockingOomAdjusterTests { } private void setServiceMap(ServiceRecord s, int uid, ComponentName cn) { - ActiveServices.ServiceMap serviceMap = sService.mServices.mServiceMap.get( + ActiveServices.ServiceMap serviceMap = mService.mServices.mServiceMap.get( UserHandle.getUserId(uid)); if (serviceMap == null) { serviceMap = mock(ActiveServices.ServiceMap.class); @@ -2916,7 +3032,7 @@ public class MockingOomAdjusterTests { new ArrayMap<>()); setFieldValue(ActiveServices.ServiceMap.class, serviceMap, "mDelayedStartList", new ArrayList<>()); - sService.mServices.mServiceMap.put(UserHandle.getUserId(uid), serviceMap); + mService.mServices.mServiceMap.put(UserHandle.getUserId(uid), serviceMap); } serviceMap.mServicesByInstanceName.put(cn, s); } @@ -2957,6 +3073,7 @@ public class MockingOomAdjusterTests { return record; } + @SuppressWarnings("GuardedBy") private void assertProcStates(ProcessRecord app, int expectedProcState, int expectedAdj, int expectedSchedGroup) { final ProcessStateRecord state = app.mState; @@ -2974,6 +3091,7 @@ public class MockingOomAdjusterTests { } } + @SuppressWarnings("GuardedBy") private void assertProcStates(ProcessRecord app, boolean expectedCached, int expectedProcState, int expectedAdj, String expectedAdjType) { final ProcessStateRecord state = app.mState; @@ -2992,7 +3110,26 @@ public class MockingOomAdjusterTests { } } - private static class ProcessRecordBuilder { + @SuppressWarnings("GuardedBy") + private void assertProcStates(ProcessRecord app, int expectedProcState, int expectedAdj, + int expectedSchedGroup, String expectedAdjType) { + final ProcessStateRecord state = app.mState; + assertEquals(expectedAdjType, state.getAdjType()); + assertEquals(expectedProcState, state.getSetProcState()); + assertEquals(expectedAdj, state.getSetAdj()); + assertEquals(expectedSchedGroup, state.getSetSchedGroup()); + + // Below BFGS should never have BFSL. + if (expectedProcState > PROCESS_STATE_BOUND_FOREGROUND_SERVICE) { + assertNoBfsl(app); + } + // Above FGS should always have BFSL. + if (expectedProcState < PROCESS_STATE_FOREGROUND_SERVICE) { + assertBfsl(app); + } + } + + private class ProcessRecordBuilder { @SuppressWarnings("UnusedVariable") int mPid; int mUid; @@ -3069,17 +3206,17 @@ public class MockingOomAdjusterTests { ai.packageName = mPackageName; ai.longVersionCode = mVersionCode; ai.targetSdkVersion = mTargetSdkVersion; - doCallRealMethod().when(sService).getPackageManagerInternal(); - doReturn(null).when(sPackageManagerInternal).getApplicationInfo( + doCallRealMethod().when(mService).getPackageManagerInternal(); + doReturn(null).when(mPackageManagerInternal).getApplicationInfo( eq(mSdkSandboxClientAppPackage), anyLong(), anyInt(), anyInt()); - ProcessRecord app = new ProcessRecord(sService, ai, mProcessName, mUid, + ProcessRecord app = new ProcessRecord(mService, ai, mProcessName, mUid, mSdkSandboxClientAppPackage, -1, null); final ProcessStateRecord state = app.mState; final ProcessServiceRecord services = app.mServices; final ProcessReceiverRecord receivers = app.mReceivers; final ProcessProfileRecord profile = app.mProfile; final ProcessProviderRecord providers = app.mProviders; - app.makeActive(mock(IApplicationThread.class), sService.mProcessStats); + app.makeActive(mock(IApplicationThread.class), mService.mProcessStats); app.setLastActivityTime(mLastActivityTime); app.setKilledByAm(mKilledByAm); app.setIsolatedEntryPoint(mIsolatedEntryPoint); @@ -3124,14 +3261,35 @@ public class MockingOomAdjusterTests { } providers.setLastProviderTime(mLastProviderTime); - UidRecord uidRec = sService.mOomAdjuster.mActiveUids.get(mUid); + UidRecord uidRec = mService.mOomAdjuster.mActiveUids.get(mUid); if (uidRec == null) { - uidRec = new UidRecord(mUid, sService); - sService.mOomAdjuster.mActiveUids.put(mUid, uidRec); + uidRec = new UidRecord(mUid, mService); + mService.mOomAdjuster.mActiveUids.put(mUid, uidRec); } uidRec.addProcess(app); app.setUidRecord(uidRec); return app; } } + + static class OomAdjusterInjector extends OomAdjuster.Injector { + // Jump ahead in time by this offset amount. + long mTimeOffsetMillis = 0; + + void jumpUptimeAheadTo(long uptimeMillis) { + final long jumpMs = uptimeMillis - getUptimeMillis(); + if (jumpMs <= 0) return; + mTimeOffsetMillis += jumpMs; + } + + @Override + long getUptimeMillis() { + return SystemClock.uptimeMillis() + mTimeOffsetMillis; + } + + @Override + long getElapsedRealtimeMillis() { + return SystemClock.elapsedRealtime() + mTimeOffsetMillis; + } + } } diff --git a/services/tests/servicestests/src/com/android/server/am/OomAdjusterTests.java b/services/tests/servicestests/src/com/android/server/am/OomAdjusterTests.java index 8cbed2c7ffb8..31bf5f046cff 100644 --- a/services/tests/servicestests/src/com/android/server/am/OomAdjusterTests.java +++ b/services/tests/servicestests/src/com/android/server/am/OomAdjusterTests.java @@ -61,20 +61,6 @@ public class OomAdjusterTests { private static final long USAGE_STATS_INTERACTION = 10 * 60 * 1000L; private static final long SERVICE_USAGE_INTERACTION = 60 * 1000; - static class MyOomAdjuster extends OomAdjuster { - - MyOomAdjuster(ActivityManagerService service, ProcessList processList, - ActiveUids activeUids) { - super(service, processList, activeUids); - } - - @Override - protected boolean isChangeEnabled(int changeId, ApplicationInfo app, - boolean defaultValue) { - return true; - } - } - @BeforeClass public static void setUpOnce() { sContext = getInstrumentation().getTargetContext(); @@ -99,7 +85,15 @@ public class OomAdjusterTests { final AppProfiler profiler = mock(AppProfiler.class); setFieldValue(AppProfiler.class, profiler, "mProfilerLock", new Object()); setFieldValue(ActivityManagerService.class, sService, "mAppProfiler", profiler); - sService.mOomAdjuster = new MyOomAdjuster(sService, sService.mProcessList, null); + final OomAdjuster.Injector injector = new OomAdjuster.Injector(){ + @Override + boolean isChangeEnabled(int changeId, ApplicationInfo app, + boolean defaultValue) { + return true; + } + }; + sService.mOomAdjuster = new OomAdjuster(sService, sService.mProcessList, null, + injector); LocalServices.addService(UsageStatsManagerInternal.class, mock(UsageStatsManagerInternal.class)); sService.mUsageStatsService = LocalServices.getService(UsageStatsManagerInternal.class); 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 3cab75b5d320..3d6884925098 100644 --- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java @@ -18,11 +18,13 @@ package com.android.server.net; import static android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS; import static android.Manifest.permission.NETWORK_STACK; +import static android.app.ActivityManager.MAX_PROCESS_STATE; import static android.app.ActivityManager.PROCESS_CAPABILITY_ALL; import static android.app.ActivityManager.PROCESS_CAPABILITY_NONE; import static android.app.ActivityManager.PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK; import static android.app.ActivityManager.PROCESS_CAPABILITY_USER_RESTRICTED_NETWORK; import static android.app.ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND; +import static android.app.ActivityManager.PROCESS_STATE_LAST_ACTIVITY; import static android.app.ActivityManager.PROCESS_STATE_SERVICE; import static android.app.ActivityManager.PROCESS_STATE_TOP; import static android.net.ConnectivityManager.BLOCKED_METERED_REASON_DATA_SAVER; @@ -165,9 +167,11 @@ import android.os.PowerManagerInternal; import android.os.PowerSaveState; import android.os.RemoteException; import android.os.SimpleClock; +import android.os.SystemClock; import android.os.UserHandle; import android.os.UserManager; import android.platform.test.annotations.Presubmit; +import android.platform.test.annotations.RequiresFlagsDisabled; import android.platform.test.annotations.RequiresFlagsEnabled; import android.platform.test.flag.junit.CheckFlagsRule; import android.platform.test.flag.junit.DeviceFlagsValueProvider; @@ -2158,7 +2162,8 @@ public class NetworkPolicyManagerServiceTest { @Test @RequiresFlagsEnabled(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE) - public void testBackgroundChainOnProcStateChange() throws Exception { + @RequiresFlagsDisabled(Flags.FLAG_USE_DIFFERENT_DELAYS_FOR_BACKGROUND_CHAIN) + public void testBackgroundChainOnProcStateChangeSameDelay() throws Exception { // initialization calls setFirewallChainEnabled, so we want to reset the invocations. clearInvocations(mNetworkManager); @@ -2186,6 +2191,59 @@ public class NetworkPolicyManagerServiceTest { } @Test + @RequiresFlagsEnabled({ + Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE, + 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)); + + // Tweak delays to avoid waiting too long in tests. + mService.mBackgroundRestrictionShortDelayMs = 50; + mService.mBackgroundRestrictionLongDelayMs = 1000; + + int procStateSeq = 231; // Any arbitrary starting sequence. + for (int ps = BACKGROUND_THRESHOLD_STATE; ps <= MAX_PROCESS_STATE; ps++) { + clearInvocations(mNetworkManager); + + // Make sure app is in correct process-state to access network. + callAndWaitOnUidStateChanged(UID_A, BACKGROUND_THRESHOLD_STATE - 1, procStateSeq++); + verify(mNetworkManager).setFirewallUidRule(FIREWALL_CHAIN_BACKGROUND, UID_A, + FIREWALL_RULE_ALLOW); + assertFalse(mService.isUidNetworkingBlocked(UID_A, false)); + + // Now put the app into the background and test that it eventually loses network. + callAndWaitOnUidStateChanged(UID_A, ps, procStateSeq++); + + final long uidStateChangeTime = SystemClock.uptimeMillis(); + if (ps <= PROCESS_STATE_LAST_ACTIVITY) { + // Verify that the app is blocked after long delay but not after short delay. + waitForDelayedMessageOnHandler(mService.mBackgroundRestrictionShortDelayMs + 1); + verify(mNetworkManager, never()).setFirewallUidRule(FIREWALL_CHAIN_BACKGROUND, + UID_A, FIREWALL_RULE_DEFAULT); + assertFalse(mService.isUidNetworkingBlocked(UID_A, false)); + + final long timeUntilLongDelay = uidStateChangeTime + + mService.mBackgroundRestrictionLongDelayMs - SystemClock.uptimeMillis(); + assertTrue("No time left to verify long delay in background transition", + timeUntilLongDelay >= 0); + + waitForDelayedMessageOnHandler(timeUntilLongDelay + 1); + verify(mNetworkManager).setFirewallUidRule(FIREWALL_CHAIN_BACKGROUND, UID_A, + FIREWALL_RULE_DEFAULT); + assertTrue(mService.isUidNetworkingBlocked(UID_A, false)); + } else { + // Verify that the app is blocked after short delay. + waitForDelayedMessageOnHandler(mService.mBackgroundRestrictionShortDelayMs + 1); + verify(mNetworkManager).setFirewallUidRule(FIREWALL_CHAIN_BACKGROUND, UID_A, + FIREWALL_RULE_DEFAULT); + assertTrue(mService.isUidNetworkingBlocked(UID_A, false)); + } + } + } + + @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. @@ -2881,6 +2939,11 @@ public class NetworkPolicyManagerServiceTest { } } + /** + * This posts a blocking message to the service handler with the given delayMs and waits for it + * to complete. This ensures that all messages posted before the given delayMs will also + * have been executed before this method returns and can be verified in subsequent code. + */ private void waitForDelayedMessageOnHandler(long delayMs) throws InterruptedException { final CountDownLatch latch = new CountDownLatch(1); mService.getHandlerForTesting().postDelayed(latch::countDown, delayMs); 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 15c9bfb74d92..f07e5bca4f70 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -511,7 +511,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { TestableNotificationManagerService.StrongAuthTrackerFake mStrongAuthTracker; TestableFlagResolver mTestFlagResolver = new TestableFlagResolver(); - @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT); + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); private InstanceIdSequence mNotificationInstanceIdSequence = new InstanceIdSequenceFake( 1 << 30); @Mock @@ -626,7 +627,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { when(mPackageManagerClient.getPackageUidAsUser(any(), anyInt())).thenReturn(mUid); when(mPackageManagerInternal.isSameApp(anyString(), anyInt(), anyInt())).thenAnswer( (Answer<Boolean>) invocation -> { - // TODO: b/317957802 - This is overly broad and basically makes ANY + // TODO: b/317957802 - This is overly broad and basically makes ANY // isSameApp() check pass, requiring Mockito.reset() for meaningful // tests! Make it more precise. Object[] args = invocation.getArguments(); @@ -6892,22 +6893,15 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { verify(visitor, times(1)).accept(eq(personIcon3.getUri())); } - private PendingIntent getPendingIntentWithUri(Uri uri) { - return PendingIntent.getActivity(mContext, 0, - new Intent("action", uri), - PendingIntent.FLAG_IMMUTABLE); - } - @Test - @EnableFlags({android.app.Flags.FLAG_VISIT_RISKY_URIS}) - public void testVisitUris_callStyle_ongoingCall() { + public void testVisitUris_callStyle() { Icon personIcon = Icon.createWithContentUri("content://media/person"); Icon verificationIcon = Icon.createWithContentUri("content://media/verification"); Person callingPerson = new Person.Builder().setName("Someone") .setIcon(personIcon) .build(); - Uri hangUpUri = Uri.parse("content://intent/hangup"); - PendingIntent hangUpIntent = getPendingIntentWithUri(hangUpUri); + PendingIntent hangUpIntent = PendingIntent.getActivity(mContext, 0, new Intent(), + PendingIntent.FLAG_IMMUTABLE); Notification n = new Notification.Builder(mContext, "a") .setStyle(Notification.CallStyle.forOngoingCall(callingPerson, hangUpIntent) .setVerificationIcon(verificationIcon)) @@ -6920,42 +6914,10 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { verify(visitor, times(1)).accept(eq(personIcon.getUri())); verify(visitor, times(1)).accept(eq(verificationIcon.getUri())); - verify(visitor, times(1)).accept(eq(hangUpUri)); hangUpIntent.cancel(); } @Test - @EnableFlags({android.app.Flags.FLAG_VISIT_RISKY_URIS}) - public void testVisitUris_callStyle_incomingCall() { - Icon personIcon = Icon.createWithContentUri("content://media/person"); - Icon verificationIcon = Icon.createWithContentUri("content://media/verification"); - Person callingPerson = new Person.Builder().setName("Someone") - .setIcon(personIcon) - .build(); - Uri answerUri = Uri.parse("content://intent/answer"); - PendingIntent answerIntent = getPendingIntentWithUri(answerUri); - Uri declineUri = Uri.parse("content://intent/decline"); - PendingIntent declineIntent = getPendingIntentWithUri(declineUri); - Notification n = new Notification.Builder(mContext, "a") - .setStyle(Notification.CallStyle.forIncomingCall(callingPerson, declineIntent, - answerIntent) - .setVerificationIcon(verificationIcon)) - .setContentTitle("Calling...") - .setSmallIcon(android.R.drawable.sym_def_app_icon) - .build(); - - Consumer<Uri> visitor = (Consumer<Uri>) spy(Consumer.class); - n.visitUris(visitor); - - verify(visitor, times(1)).accept(eq(personIcon.getUri())); - verify(visitor, times(1)).accept(eq(verificationIcon.getUri())); - verify(visitor, times(1)).accept(eq(answerIntent.getIntent().getData())); - verify(visitor, times(1)).accept(eq(declineUri)); - answerIntent.cancel(); - declineIntent.cancel(); - } - - @Test public void testVisitUris_styleExtrasWithoutStyle() { Notification.Builder notification = new Notification.Builder(mContext, "a") .setSmallIcon(android.R.drawable.sym_def_app_icon); @@ -7001,87 +6963,23 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @Test - @EnableFlags({android.app.Flags.FLAG_VISIT_RISKY_URIS}) public void testVisitUris_wearableExtender() { Icon actionIcon = Icon.createWithContentUri("content://media/action"); Icon wearActionIcon = Icon.createWithContentUri("content://media/wearAction"); - Uri displayIntentUri = Uri.parse("content://intent/display"); - PendingIntent displayIntent = getPendingIntentWithUri(displayIntentUri); - Uri actionIntentUri = Uri.parse("content://intent/action"); - PendingIntent actionIntent = getPendingIntentWithUri(actionIntentUri); - Uri wearActionIntentUri = Uri.parse("content://intent/wear"); - PendingIntent wearActionIntent = getPendingIntentWithUri(wearActionIntentUri); + PendingIntent intent = PendingIntent.getActivity(mContext, 0, new Intent(), + PendingIntent.FLAG_IMMUTABLE); Notification n = new Notification.Builder(mContext, "a") .setSmallIcon(android.R.drawable.sym_def_app_icon) - .addAction( - new Notification.Action.Builder(actionIcon, "Hey!", actionIntent).build()) - .extend(new Notification.WearableExtender() - .setDisplayIntent(displayIntent) - .addAction(new Notification.Action.Builder(wearActionIcon, "Wear!", - wearActionIntent) - .build())) + .addAction(new Notification.Action.Builder(actionIcon, "Hey!", intent).build()) + .extend(new Notification.WearableExtender().addAction( + new Notification.Action.Builder(wearActionIcon, "Wear!", intent).build())) .build(); Consumer<Uri> visitor = (Consumer<Uri>) spy(Consumer.class); n.visitUris(visitor); verify(visitor).accept(eq(actionIcon.getUri())); - verify(visitor, times(1)).accept(eq(actionIntentUri)); verify(visitor).accept(eq(wearActionIcon.getUri())); - verify(visitor, times(1)).accept(eq(wearActionIntentUri)); - displayIntent.cancel(); - actionIntent.cancel(); - wearActionIntent.cancel(); - } - - @Test - @EnableFlags({android.app.Flags.FLAG_VISIT_RISKY_URIS}) - public void testVisitUris_tvExtender() { - Uri contentIntentUri = Uri.parse("content://intent/content"); - PendingIntent contentIntent = getPendingIntentWithUri(contentIntentUri); - Uri deleteIntentUri = Uri.parse("content://intent/delete"); - PendingIntent deleteIntent = getPendingIntentWithUri(deleteIntentUri); - Notification n = new Notification.Builder(mContext, "a") - .extend( - new Notification.TvExtender() - .setContentIntent(contentIntent) - .setDeleteIntent(deleteIntent)) - .build(); - - Consumer<Uri> visitor = (Consumer<Uri>) spy(Consumer.class); - n.visitUris(visitor); - - verify(visitor, times(1)).accept(eq(contentIntentUri)); - verify(visitor, times(1)).accept(eq(deleteIntentUri)); - contentIntent.cancel(); - deleteIntent.cancel(); - } - - @Test - @EnableFlags({android.app.Flags.FLAG_VISIT_RISKY_URIS}) - public void testVisitUris_carExtender() { - final String testParticipant = "testParticipant"; - Uri readPendingIntentUri = Uri.parse("content://intent/read"); - PendingIntent readPendingIntent = getPendingIntentWithUri(readPendingIntentUri); - Uri replyPendingIntentUri = Uri.parse("content://intent/reply"); - PendingIntent replyPendingIntent = getPendingIntentWithUri(replyPendingIntentUri); - final RemoteInput testRemoteInput = new RemoteInput.Builder("key").build(); - - Notification n = new Notification.Builder(mContext, "a") - .extend(new Notification.CarExtender().setUnreadConversation( - new Notification.CarExtender.Builder(testParticipant) - .setReadPendingIntent(readPendingIntent) - .setReplyAction(replyPendingIntent, testRemoteInput) - .build())) - .build(); - - Consumer<Uri> visitor = (Consumer<Uri>) spy(Consumer.class); - n.visitUris(visitor); - - verify(visitor, times(1)).accept(eq(readPendingIntentUri)); - verify(visitor, times(1)).accept(eq(replyPendingIntentUri)); - readPendingIntent.cancel(); - replyPendingIntent.cancel(); } @Test 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 863cda4905f1..594d6f2ebb98 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationVisitUrisTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationVisitUrisTest.java @@ -34,6 +34,7 @@ import android.net.Uri; import android.os.Bundle; import android.os.IBinder; import android.os.Parcel; +import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.SetFlagsRule; import android.util.Log; import android.view.LayoutInflater; @@ -87,6 +88,7 @@ import java.util.stream.Stream; import javax.annotation.Nullable; @RunWith(AndroidJUnit4.class) +@EnableFlags(Flags.FLAG_VISIT_PERSON_URI) public class NotificationVisitUrisTest extends UiServiceTestCase { @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); @@ -153,10 +155,6 @@ public class NotificationVisitUrisTest extends UiServiceTestCase { .put(Notification.Action.Builder.class, "extend") // Overwrites icon supplied to constructor. .put(Notification.BubbleMetadata.Builder.class, "setIcon") - // Overwrites intent supplied to constructor. - .put(Notification.BubbleMetadata.Builder.class, "setIntent") - // Overwrites intent supplied to constructor. - .put(Notification.BubbleMetadata.Builder.class, "setDeleteIntent") // Discards previously-added actions. .put(RemoteViews.class, "mergeRemoteViews") .build(); @@ -172,7 +170,6 @@ public class NotificationVisitUrisTest extends UiServiceTestCase { @Before public void setUp() { mContext = InstrumentationRegistry.getInstrumentation().getContext(); - mSetFlagsRule.enableFlags(Flags.FLAG_VISIT_RISKY_URIS); } @After @@ -700,14 +697,13 @@ public class NotificationVisitUrisTest extends UiServiceTestCase { } if (clazz == Intent.class) { - return new Intent("action", generateUri(where.plus(Intent.class))); + return new Intent("action"); } if (clazz == PendingIntent.class) { - // PendingIntent can have an Intent with a Uri. - Uri intentUri = generateUri(where.plus(PendingIntent.class)); - return PendingIntent.getActivity(mContext, 0, - new Intent("action", intentUri), + // PendingIntent can have an Intent with a Uri but those are inaccessible and + // not inspected. + return PendingIntent.getActivity(mContext, 0, new Intent("action"), PendingIntent.FLAG_IMMUTABLE); } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java index abfb95c9431a..9352c1287ee1 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeConfigTest.java @@ -17,7 +17,16 @@ package com.android.server.notification; import static android.app.AutomaticZenRule.TYPE_BEDTIME; +import static android.app.Flags.FLAG_MODES_UI; +import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS; +import static android.provider.Settings.Global.ZEN_MODE_OFF; +import static android.service.notification.Condition.SOURCE_USER_ACTION; +import static android.service.notification.Condition.STATE_FALSE; +import static android.service.notification.Condition.STATE_TRUE; import static android.service.notification.ZenPolicy.CONVERSATION_SENDERS_IMPORTANT; +import static android.service.notification.ZenPolicy.CONVERSATION_SENDERS_NONE; +import static android.service.notification.ZenPolicy.PEOPLE_TYPE_ANYONE; +import static android.service.notification.ZenPolicy.STATE_ALLOW; import static com.google.common.truth.Truth.assertThat; @@ -33,6 +42,9 @@ import android.app.NotificationManager.Policy; import android.content.ComponentName; import android.net.Uri; import android.os.Parcel; +import android.platform.test.annotations.DisableFlags; +import android.platform.test.annotations.EnableFlags; +import android.platform.test.flag.junit.FlagsParameterization; import android.platform.test.flag.junit.SetFlagsRule; import android.provider.Settings; import android.service.notification.Condition; @@ -43,7 +55,6 @@ import android.service.notification.ZenPolicy; import android.util.Xml; import androidx.test.filters.SmallTest; -import androidx.test.runner.AndroidJUnit4; import com.android.modules.utils.TypedXmlPullParser; import com.android.modules.utils.TypedXmlSerializer; @@ -63,9 +74,13 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.time.Instant; +import java.util.List; + +import platform.test.runner.parameterized.ParameterizedAndroidJunit4; +import platform.test.runner.parameterized.Parameters; @SmallTest -@RunWith(AndroidJUnit4.class) +@RunWith(ParameterizedAndroidJunit4.class) public class ZenModeConfigTest extends UiServiceTestCase { private final String NAME = "name"; @@ -91,6 +106,16 @@ public class ZenModeConfigTest extends UiServiceTestCase { public final SetFlagsRule mSetFlagsRule = new SetFlagsRule( SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT); + @Parameters(name = "{0}") + public static List<FlagsParameterization> getParams() { + return FlagsParameterization.allCombinationsOf( + FLAG_MODES_UI); + } + + public ZenModeConfigTest(FlagsParameterization flags) { + mSetFlagsRule.setFlagsParameterization(flags); + } + @Before public final void setUp() { mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API); @@ -101,13 +126,44 @@ public class ZenModeConfigTest extends UiServiceTestCase { ZenModeConfig config = getMutedRingerConfig(); assertTrue(ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(config)); - config.allowReminders = true; + if (Flags.modesUi()) { + config.manualRule.zenPolicy = new ZenPolicy.Builder(config.manualRule.zenPolicy) + .allowReminders(true) + .build(); + } else { + config.setAllowReminders(true); + } assertFalse(ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(config)); - config.allowReminders = false; + if (Flags.modesUi()) { + config.manualRule.zenPolicy = new ZenPolicy.Builder(config.manualRule.zenPolicy) + .allowReminders(false) + .build(); + } else { + config.setAllowReminders(false); + } + assertTrue(ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(config)); config.areChannelsBypassingDnd = true; + assertTrue(ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(config)); + + if (Flags.modesUi()) { + config.manualRule.zenPolicy = new ZenPolicy.Builder(config.manualRule.zenPolicy) + .allowPriorityChannels(true) + .build(); + } else { + config.setAllowPriorityChannels(true); + } + assertFalse(ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(config)); + config.areChannelsBypassingDnd = false; + if (Flags.modesUi()) { + config.manualRule.zenPolicy = new ZenPolicy.Builder(config.manualRule.zenPolicy) + .allowPriorityChannels(false) + .build(); + } else { + config.setAllowPriorityChannels(false); + } assertTrue(ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(config)); } @@ -122,6 +178,8 @@ public class ZenModeConfigTest extends UiServiceTestCase { @Test public void testZenPolicyToNotificationPolicy_classic() { ZenModeConfig config = getMutedAllConfig(); + // this shouldn't usually be directly set, but since it's a test that involved the default + // policy, calling setNotificationPolicy as a precondition may obscure issues config.suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_BADGE; // Explicitly allow conversations from priority senders to make sure that goes through @@ -154,8 +212,9 @@ public class ZenModeConfigTest extends UiServiceTestCase { @Test public void testZenPolicyToNotificationPolicy() { - mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API); ZenModeConfig config = getMutedAllConfig(); + // this shouldn't usually be directly set, but since it's a test that involved the default + // policy, calling setNotificationPolicy as a precondition may obscure issues config.suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_BADGE; // Explicitly allow conversations from priority senders to make sure that goes through @@ -194,14 +253,16 @@ public class ZenModeConfigTest extends UiServiceTestCase { @Test public void testZenPolicyToNotificationPolicy_unsetChannelsTakesDefault() { - mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API); ZenModeConfig config = new ZenModeConfig(); ZenPolicy zenPolicy = new ZenPolicy.Builder().build(); // When allowChannels is not set to anything in the ZenPolicy builder, make sure it takes // the default value from the zen mode config. Policy policy = config.toNotificationPolicy(zenPolicy); - assertEquals(config.allowPriorityChannels, policy.allowPriorityChannels()); + assertEquals(Flags.modesUi() + ? config.manualRule.zenPolicy.getPriorityChannelsAllowed() == STATE_ALLOW + : config.isAllowPriorityChannels(), + policy.allowPriorityChannels()); } @Test @@ -219,18 +280,22 @@ public class ZenModeConfigTest extends UiServiceTestCase { .build(); ZenModeConfig config = getMutedAllConfig(); - config.allowAlarms = true; - config.allowReminders = true; - config.allowEvents = true; - config.allowCalls = true; - config.allowCallsFrom = Policy.PRIORITY_SENDERS_CONTACTS; - config.allowMessages = true; - config.allowMessagesFrom = Policy.PRIORITY_SENDERS_STARRED; - config.allowConversations = false; - config.suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_BADGE; - config.suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_LIGHTS; - config.suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_AMBIENT; - ZenPolicy actual = config.toZenPolicy(); + if (Flags.modesUi()) { + config.manualRule.zenPolicy = expected.copy(); + } else { + config.setAllowAlarms(true); + config.setAllowReminders(true); + config.setAllowEvents(true); + config.setAllowCalls(true); + config.setAllowCallsFrom(Policy.PRIORITY_SENDERS_CONTACTS); + config.setAllowMessages(true); + config.setAllowMessagesFrom(Policy.PRIORITY_SENDERS_STARRED); + config.setAllowConversationsFrom(CONVERSATION_SENDERS_NONE); + config.setSuppressedVisualEffects(config.getSuppressedVisualEffects() + | Policy.SUPPRESSED_EFFECT_BADGE | Policy.SUPPRESSED_EFFECT_LIGHTS + | Policy.SUPPRESSED_EFFECT_AMBIENT); + } + ZenPolicy actual = config.getZenPolicy(); assertEquals(expected.getVisualEffectBadge(), actual.getVisualEffectBadge()); assertEquals(expected.getPriorityCategoryAlarms(), actual.getPriorityCategoryAlarms()); @@ -247,7 +312,6 @@ public class ZenModeConfigTest extends UiServiceTestCase { @Test public void testZenConfigToZenPolicy() { - mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API); ZenPolicy expected = new ZenPolicy.Builder() .allowAlarms(true) .allowReminders(true) @@ -262,19 +326,24 @@ public class ZenModeConfigTest extends UiServiceTestCase { .build(); ZenModeConfig config = getMutedAllConfig(); - config.allowAlarms = true; - config.allowReminders = true; - config.allowEvents = true; - config.allowCalls = true; - config.allowCallsFrom = Policy.PRIORITY_SENDERS_CONTACTS; - config.allowMessages = true; - config.allowMessagesFrom = Policy.PRIORITY_SENDERS_STARRED; - config.allowConversations = false; - config.allowPriorityChannels = false; - config.suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_BADGE; - config.suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_LIGHTS; - config.suppressedVisualEffects |= Policy.SUPPRESSED_EFFECT_AMBIENT; - ZenPolicy actual = config.toZenPolicy(); + if (Flags.modesUi()) { + config.manualRule.zenPolicy = expected.copy(); + } else { + config.setAllowAlarms(true); + config.setAllowReminders(true); + + config.setAllowEvents(true); + config.setAllowCalls(true); + config.setAllowCallsFrom(Policy.PRIORITY_SENDERS_CONTACTS); + config.setAllowMessages(true); + config.setAllowMessagesFrom(Policy.PRIORITY_SENDERS_STARRED); + config.setAllowConversationsFrom(CONVERSATION_SENDERS_NONE); + config.setAllowPriorityChannels(false); + config.setSuppressedVisualEffects(config.getSuppressedVisualEffects() + | Policy.SUPPRESSED_EFFECT_BADGE | Policy.SUPPRESSED_EFFECT_LIGHTS + | Policy.SUPPRESSED_EFFECT_AMBIENT); + } + ZenPolicy actual = config.getZenPolicy(); assertEquals(expected.getVisualEffectBadge(), actual.getVisualEffectBadge()); assertEquals(expected.getPriorityCategoryAlarms(), actual.getPriorityCategoryAlarms()); @@ -296,20 +365,64 @@ public class ZenModeConfigTest extends UiServiceTestCase { assertTrue(ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(config)); assertTrue(ZenModeConfig.areAllZenBehaviorSoundsMuted(config)); - config.allowReminders = true; + if (Flags.modesUi()) { + config.manualRule.zenPolicy = new ZenPolicy.Builder(config.manualRule.zenPolicy) + .allowReminders(true) + .build(); + } else { + config.setAllowReminders(true); + } assertFalse(ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(config)); assertFalse(ZenModeConfig.areAllZenBehaviorSoundsMuted(config)); - config.allowReminders = false; + + if (Flags.modesUi()) { + config.manualRule.zenPolicy = new ZenPolicy.Builder(config.manualRule.zenPolicy) + .allowReminders(false) + .build(); + } else { + config.setAllowReminders(false); + } + + assertTrue(ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(config)); + assertTrue(ZenModeConfig.areAllZenBehaviorSoundsMuted(config)); config.areChannelsBypassingDnd = true; + if (Flags.modesUi()) { + config.manualRule.zenPolicy = new ZenPolicy.Builder(config.manualRule.zenPolicy) + .allowPriorityChannels(true) + .build(); + } else { + config.setAllowPriorityChannels(true); + } + assertFalse(ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(config)); assertFalse(ZenModeConfig.areAllZenBehaviorSoundsMuted(config)); - config.areChannelsBypassingDnd = false; - config.allowAlarms = true; + config.areChannelsBypassingDnd = false; + if (Flags.modesUi()) { + config.manualRule.zenPolicy = new ZenPolicy.Builder(config.manualRule.zenPolicy) + .allowPriorityChannels(false) + .build(); + } else { + config.setAllowPriorityChannels(false); + } + + if (Flags.modesUi()) { + config.manualRule.zenPolicy = new ZenPolicy.Builder(config.manualRule.zenPolicy) + .allowAlarms(true) + .build(); + } else { + config.setAllowAlarms(true); + } assertTrue(ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(config)); assertFalse(ZenModeConfig.areAllZenBehaviorSoundsMuted(config)); - config.allowAlarms = false; + if (Flags.modesUi()) { + config.manualRule.zenPolicy = new ZenPolicy.Builder(config.manualRule.zenPolicy) + .allowAlarms(false) + .build(); + } else { + config.setAllowAlarms(false); + } assertTrue(ZenModeConfig.areAllPriorityOnlyRingerSoundsMuted(config)); assertTrue(ZenModeConfig.areAllZenBehaviorSoundsMuted(config)); @@ -375,8 +488,6 @@ public class ZenModeConfigTest extends UiServiceTestCase { @Test public void testWriteToParcel() { - mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API); - ZenModeConfig.ZenRule rule = new ZenModeConfig.ZenRule(); rule.configurationActivity = CONFIG_ACTIVITY; rule.component = OWNER; @@ -473,8 +584,6 @@ public class ZenModeConfigTest extends UiServiceTestCase { @Test public void testRuleXml() throws Exception { - mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API); - ZenModeConfig.ZenRule rule = new ZenModeConfig.ZenRule(); rule.configurationActivity = CONFIG_ACTIVITY; rule.component = OWNER; @@ -719,8 +828,6 @@ public class ZenModeConfigTest extends UiServiceTestCase { @Test public void testZenPolicyXml() throws Exception { - mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API); - ZenPolicy policy = new ZenPolicy.Builder() .allowCalls(ZenPolicy.PEOPLE_TYPE_CONTACTS) .allowMessages(ZenPolicy.PEOPLE_TYPE_NONE) @@ -770,63 +877,173 @@ public class ZenModeConfigTest extends UiServiceTestCase { fromXml.getVisualEffectNotificationList()); } + @Test + @EnableFlags(Flags.FLAG_MODES_UI) + public void testisManualActive_stateTrue() { + ZenModeConfig config = getMutedAllConfig(); + final ZenModeConfig.ZenRule newRule = new ZenModeConfig.ZenRule(); + newRule.type = AutomaticZenRule.TYPE_OTHER; + newRule.enabled = true; + newRule.conditionId = Uri.EMPTY; + newRule.allowManualInvocation = true; + config.manualRule = newRule; + config.manualRule.pkg = "android"; + config.manualRule.zenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS; + config.manualRule.condition = new Condition(Uri.EMPTY, "", STATE_TRUE, SOURCE_USER_ACTION); + + assertThat(config.isManualActive()).isTrue(); + } + + @Test + @EnableFlags(Flags.FLAG_MODES_UI) + public void testisManualActive_stateFalse() { + ZenModeConfig config = getMutedAllConfig(); + final ZenModeConfig.ZenRule newRule = new ZenModeConfig.ZenRule(); + newRule.type = AutomaticZenRule.TYPE_OTHER; + newRule.enabled = true; + newRule.conditionId = Uri.EMPTY; + newRule.allowManualInvocation = true; + config.manualRule = newRule; + config.manualRule.pkg = "android"; + config.manualRule.zenMode = ZEN_MODE_OFF; + config.manualRule.condition = new Condition(Uri.EMPTY, "", STATE_FALSE, SOURCE_USER_ACTION); + + assertThat(config.isManualActive()).isFalse(); + } + + @Test + @DisableFlags(Flags.FLAG_MODES_UI) + public void testisManualActive_noState() { + ZenModeConfig config = getMutedAllConfig(); + final ZenModeConfig.ZenRule newRule = new ZenModeConfig.ZenRule(); + newRule.type = AutomaticZenRule.TYPE_OTHER; + newRule.enabled = true; + newRule.conditionId = Uri.EMPTY; + newRule.allowManualInvocation = true; + config.manualRule = newRule; + config.manualRule.pkg = "android"; + config.manualRule.zenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS; + + assertThat(config.isManualActive()).isTrue(); + } + + @Test + public void testisManualActive_noRule() { + ZenModeConfig config = getMutedAllConfig(); + + assertThat(config.isManualActive()).isFalse(); + } + + @Test + @EnableFlags(Flags.FLAG_MODES_UI) + public void testRuleXml_manual_upgrade() throws Exception { + ZenModeConfig config = getMutedAllConfig(); + final ZenModeConfig.ZenRule newRule = new ZenModeConfig.ZenRule(); + newRule.type = AutomaticZenRule.TYPE_OTHER; + newRule.enabled = true; + newRule.conditionId = Uri.EMPTY; + newRule.allowManualInvocation = true; + newRule.pkg = "android"; + newRule.zenMode = ZEN_MODE_OFF; + config.manualRule = newRule; + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + writeRuleXml(newRule, baos); + ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); + ZenModeConfig.ZenRule fromXml = readRuleXml(bais); + + assertThat(fromXml.zenPolicy).isEqualTo(config.getZenPolicy()); + } + private ZenModeConfig getMutedRingerConfig() { ZenModeConfig config = new ZenModeConfig(); - // Allow alarms, media - config.allowAlarms = true; - config.allowMedia = true; - - // All sounds that respect the ringer are not allowed - config.allowSystem = false; - config.allowCalls = false; - config.allowRepeatCallers = false; - config.allowMessages = false; - config.allowReminders = false; - config.allowEvents = false; - config.areChannelsBypassingDnd = false; - config.suppressedVisualEffects = 0; + if (Flags.modesUi()) { + config.manualRule.zenPolicy = new ZenPolicy.Builder() + .disallowAllSounds() + .allowAlarms(true) + .allowMedia(true) + .allowPriorityChannels(false) + .showAllVisualEffects() + .build(); + } else { + // Allow alarms, media + config.setAllowAlarms(true); + config.setAllowMedia(true); + + // All sounds that respect the ringer are not allowed + config.setAllowSystem(false); + config.setAllowCalls(false); + config.setAllowRepeatCallers(false); + config.setAllowMessages(false); + config.setAllowReminders(false); + config.setAllowEvents(false); + config.setSuppressedVisualEffects(0); + config.setAllowPriorityChannels(false); + } + config.areChannelsBypassingDnd = false; return config; } private ZenModeConfig getCustomConfig() { ZenModeConfig config = new ZenModeConfig(); - // Some sounds allowed - config.allowAlarms = true; - config.allowMedia = false; - config.allowSystem = false; - config.allowCalls = true; - config.allowRepeatCallers = true; - config.allowMessages = false; - config.allowReminders = false; - config.allowEvents = false; - config.areChannelsBypassingDnd = false; - config.allowCallsFrom = ZenModeConfig.SOURCE_ANYONE; - config.allowMessagesFrom = ZenModeConfig.SOURCE_ANYONE; - config.allowConversations = true; - config.allowConversationsFrom = CONVERSATION_SENDERS_IMPORTANT; - config.suppressedVisualEffects = 0; + if (Flags.modesUi()) { + config.manualRule.zenPolicy = new ZenPolicy.Builder() + .disallowAllSounds() + .allowAlarms(true) + .allowCalls(PEOPLE_TYPE_ANYONE) + .allowRepeatCallers(true) + .allowConversations(CONVERSATION_SENDERS_IMPORTANT) + .allowPriorityChannels(true) + .showAllVisualEffects() + .build(); + } else { + // Some sounds allowed + config.setAllowAlarms(true); + config.setAllowMedia(false); + config.setAllowSystem(false); + config.setAllowCalls(true); + config.setAllowRepeatCallers(true); + config.setAllowMessages(false); + config.setAllowReminders(false); + config.setAllowEvents(false); + config.setAllowCallsFrom(ZenModeConfig.SOURCE_ANYONE); + config.setAllowMessagesFrom(ZenModeConfig.SOURCE_ANYONE); + config.setAllowConversations(true); + config.setAllowConversationsFrom(CONVERSATION_SENDERS_IMPORTANT); + config.setSuppressedVisualEffects(0); + config.setAllowPriorityChannels(true); + } + config.areChannelsBypassingDnd = false; return config; } private ZenModeConfig getMutedAllConfig() { ZenModeConfig config = new ZenModeConfig(); - // No sounds allowed - config.allowAlarms = false; - config.allowMedia = false; - config.allowSystem = false; - config.allowCalls = false; - config.allowRepeatCallers = false; - config.allowMessages = false; - config.allowReminders = false; - config.allowEvents = false; - config.areChannelsBypassingDnd = false; - config.allowConversations = false; - config.allowConversationsFrom = ZenPolicy.CONVERSATION_SENDERS_NONE; - config.suppressedVisualEffects = 0; + if (Flags.modesUi()) { + config.manualRule.zenPolicy = new ZenPolicy.Builder() + .disallowAllSounds() + .showAllVisualEffects() + .allowPriorityChannels(false) + .build(); + } else { + // No sounds allowed + config.setAllowAlarms(false); + config.setAllowMedia(false); + config.setAllowSystem(false); + config.setAllowCalls(false); + config.setAllowRepeatCallers(false); + config.setAllowMessages(false); + config.setAllowReminders(false); + config.setAllowEvents(false); + config.setAllowConversations(false); + config.setAllowConversationsFrom(CONVERSATION_SENDERS_NONE); + config.setSuppressedVisualEffects(0); + } + config.areChannelsBypassingDnd = false; return config; } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java index 2e64645ecade..26a13cb47563 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeDiffTest.java @@ -16,6 +16,8 @@ package com.android.server.notification; +import static android.app.Flags.FLAG_MODES_UI; + import static com.google.common.truth.Truth.assertThat; import static junit.framework.Assert.assertEquals; @@ -27,8 +29,10 @@ import static junit.framework.Assert.fail; import android.app.AutomaticZenRule; import android.app.Flags; +import android.app.NotificationManager; import android.content.ComponentName; import android.net.Uri; +import android.platform.test.flag.junit.FlagsParameterization; import android.platform.test.flag.junit.SetFlagsRule; import android.provider.Settings; import android.service.notification.Condition; @@ -37,7 +41,6 @@ import android.service.notification.ZenModeConfig; import android.service.notification.ZenModeDiff; import android.service.notification.ZenModeDiff.RuleDiff; import android.service.notification.ZenPolicy; -import android.testing.AndroidTestingRunner; import android.testing.TestableLooper; import android.util.ArrayMap; @@ -60,8 +63,11 @@ import java.util.List; import java.util.Optional; import java.util.Set; +import platform.test.runner.parameterized.ParameterizedAndroidJunit4; +import platform.test.runner.parameterized.Parameters; + @SmallTest -@RunWith(AndroidTestingRunner.class) +@RunWith(ParameterizedAndroidJunit4.class) @TestableLooper.RunWithLooper public class ZenModeDiffTest extends UiServiceTestCase { // Base set of exempt fields independent of fields that are enabled/disabled via flags. @@ -91,6 +97,16 @@ public class ZenModeDiffTest extends UiServiceTestCase { @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + @Parameters(name = "{0}") + public static List<FlagsParameterization> getParams() { + return FlagsParameterization.allCombinationsOf( + FLAG_MODES_UI); + } + + public ZenModeDiffTest(FlagsParameterization flags) { + mSetFlagsRule.setFlagsParameterization(flags); + } + @Test public void testRuleDiff_addRemoveSame() { // Test add, remove, and both sides same @@ -220,21 +236,35 @@ public class ZenModeDiffTest extends UiServiceTestCase { ZenModeConfig c2 = new ZenModeConfig(); // set c1 and c2 to have some different senders - c1.allowMessagesFrom = ZenModeConfig.SOURCE_STAR; - c2.allowMessagesFrom = ZenModeConfig.SOURCE_CONTACT; - c1.allowConversationsFrom = ZenPolicy.CONVERSATION_SENDERS_IMPORTANT; - c2.allowConversationsFrom = ZenPolicy.CONVERSATION_SENDERS_NONE; + NotificationManager.Policy c1Policy = c1.toNotificationPolicy(); + c1.applyNotificationPolicy(new NotificationManager.Policy( + c1Policy.priorityCategories, c1Policy.priorityCallSenders, + c1Policy.PRIORITY_SENDERS_STARRED, c1Policy.suppressedVisualEffects, + c1Policy.state, ZenPolicy.CONVERSATION_SENDERS_IMPORTANT)); + + NotificationManager.Policy c2Policy = c1.toNotificationPolicy(); + c2.applyNotificationPolicy(new NotificationManager.Policy( + c2Policy.priorityCategories, c2Policy.priorityCallSenders, + c2Policy.PRIORITY_SENDERS_CONTACTS, c2Policy.suppressedVisualEffects, + c2Policy.state, ZenPolicy.CONVERSATION_SENDERS_NONE)); ZenModeDiff.ConfigDiff d = new ZenModeDiff.ConfigDiff(c1, c2); assertTrue(d.hasDiff()); - // Diff in top-level fields - assertTrue(d.getDiffForField("allowMessagesFrom").hasDiff()); - assertTrue(d.getDiffForField("allowConversationsFrom").hasDiff()); - - // Bonus testing of stringification of people senders and conversation senders - assertTrue(d.toString().contains("allowMessagesFrom:stars->contacts")); - assertTrue(d.toString().contains("allowConversationsFrom:important->none")); + if (!Flags.modesUi()) { + // Diff in top-level fields + assertTrue(d.getDiffForField("allowMessagesFrom").hasDiff()); + assertTrue(d.getDiffForField("allowConversationsFrom").hasDiff()); + + // Bonus testing of stringification of people senders and conversation senders + assertTrue(d.toString().contains("allowMessagesFrom:stars->contacts")); + assertTrue(d.toString().contains("allowConversationsFrom:important->none")); + } else { + RuleDiff r = d.getManualRuleDiff(); + assertNotNull(r); + ZenModeDiff.FieldDiff p = r.getDiffForField(RuleDiff.FIELD_ZEN_POLICY); + assertNotNull(p); + } } @Test 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 72ace84b855b..201b2868b3b8 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeHelperTest.java @@ -18,6 +18,8 @@ package com.android.server.notification; import static android.app.AutomaticZenRule.TYPE_BEDTIME; import static android.app.AutomaticZenRule.TYPE_IMMERSIVE; +import static android.app.Flags.FLAG_MODES_API; +import static android.app.Flags.FLAG_MODES_UI; import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_ACTIVATED; import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_DEACTIVATED; import static android.app.NotificationManager.AUTOMATIC_RULE_STATUS_DISABLED; @@ -41,11 +43,9 @@ import static android.app.NotificationManager.Policy.PRIORITY_CATEGORY_SYSTEM; import static android.app.NotificationManager.Policy.PRIORITY_SENDERS_ANY; import static android.app.NotificationManager.Policy.PRIORITY_SENDERS_CONTACTS; import static android.app.NotificationManager.Policy.PRIORITY_SENDERS_STARRED; -import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_AMBIENT; +import static android.app.NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND; +import static android.app.NotificationManager.Policy.STATE_PRIORITY_CHANNELS_BLOCKED; import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_BADGE; -import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_FULL_SCREEN_INTENT; -import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_LIGHTS; -import static android.app.NotificationManager.Policy.SUPPRESSED_EFFECT_PEEK; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.provider.Settings.Global.ZEN_MODE_ALARMS; import static android.provider.Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS; @@ -64,6 +64,11 @@ import static android.service.notification.ZenModeConfig.UPDATE_ORIGIN_USER; import static android.service.notification.ZenPolicy.PEOPLE_TYPE_CONTACTS; import static android.service.notification.ZenPolicy.PEOPLE_TYPE_NONE; import static android.service.notification.ZenPolicy.PEOPLE_TYPE_STARRED; +import static android.service.notification.ZenPolicy.VISUAL_EFFECT_AMBIENT; +import static android.service.notification.ZenPolicy.VISUAL_EFFECT_BADGE; +import static android.service.notification.ZenPolicy.VISUAL_EFFECT_FULL_SCREEN_INTENT; +import static android.service.notification.ZenPolicy.VISUAL_EFFECT_LIGHTS; +import static android.service.notification.ZenPolicy.VISUAL_EFFECT_PEEK; import static com.android.internal.config.sysui.SystemUiSystemPropertiesFlags.NotificationFlags.LOG_DND_STATE_EVENTS; import static com.android.os.dnd.DNDProtoEnums.PEOPLE_STARRED; @@ -75,6 +80,7 @@ import static com.android.server.notification.ZenModeHelper.RULE_LIMIT_PER_PACKA import static com.google.common.collect.Iterables.getOnlyElement; import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; @@ -131,6 +137,7 @@ import android.os.SimpleClock; import android.os.UserHandle; import android.platform.test.annotations.DisableFlags; import android.platform.test.annotations.EnableFlags; +import android.platform.test.flag.junit.FlagsParameterization; import android.platform.test.flag.junit.SetFlagsRule; import android.provider.Settings; import android.provider.Settings.Global; @@ -140,7 +147,6 @@ import android.service.notification.SystemZenRules; import android.service.notification.ZenAdapters; import android.service.notification.ZenDeviceEffects; import android.service.notification.ZenModeConfig; -import android.service.notification.ZenModeConfig.ConfigChangeOrigin; import android.service.notification.ZenModeConfig.ScheduleInfo; import android.service.notification.ZenModeConfig.ZenRule; import android.service.notification.ZenModeDiff; @@ -172,8 +178,6 @@ import com.google.common.collect.ImmutableSet; import com.google.common.truth.Correspondence; import com.google.common.util.concurrent.SettableFuture; import com.google.protobuf.InvalidProtocolBufferException; -import com.google.testing.junit.testparameterinjector.TestParameter; -import com.google.testing.junit.testparameterinjector.TestParameterInjector; import org.junit.Before; import org.junit.Rule; @@ -202,9 +206,12 @@ 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(TestParameterInjector.class) +@RunWith(ParameterizedAndroidJunit4.class) @TestableLooper.RunWithLooper public class ZenModeHelperTest extends UiServiceTestCase { @@ -268,6 +275,16 @@ public class ZenModeHelperTest extends UiServiceTestCase { ZenModeEventLoggerFake mZenModeEventLogger; private String mPkg; + @Parameters(name = "{0}") + public static List<FlagsParameterization> getParams() { + return FlagsParameterization.progressionOf(FLAG_MODES_API, + FLAG_MODES_UI); + } + + public ZenModeHelperTest(FlagsParameterization flags) { + mSetFlagsRule.setFlagsParameterization(flags); + } + @Before public void setUp() throws PackageManager.NameNotFoundException { MockitoAnnotations.initMocks(this); @@ -677,7 +694,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - @EnableFlags(Flags.FLAG_MODES_API) + @EnableFlags(FLAG_MODES_API) public void testTotalSilence_consolidatedPolicyDisallowsAll() { // Start with zen mode off just to make sure global/manual mode isn't doing anything. mZenModeHelper.mZenMode = ZEN_MODE_OFF; @@ -711,7 +728,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - @EnableFlags(Flags.FLAG_MODES_API) + @EnableFlags(FLAG_MODES_API) public void testAlarmsOnly_consolidatedPolicyOnlyAllowsAlarmsAndMedia() { // Start with zen mode off just to make sure global/manual mode isn't doing anything. mZenModeHelper.mZenMode = ZEN_MODE_OFF; @@ -802,15 +819,11 @@ public class ZenModeHelperTest extends UiServiceTestCase { // 1. Current ringer is normal when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL); // Set zen to priority-only with all notification sounds muted (so ringer will be muted) + Policy totalSilence = new Policy(0,0,0); + mZenModeHelper.setNotificationPolicy(totalSilence, UPDATE_ORIGIN_APP, 1); mZenModeHelper.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS; - mZenModeHelper.mConfig.allowReminders = false; - mZenModeHelper.mConfig.allowCalls = false; - mZenModeHelper.mConfig.allowMessages = false; - mZenModeHelper.mConfig.allowEvents = false; - mZenModeHelper.mConfig.allowRepeatCallers = false; - mZenModeHelper.mConfig.allowConversations = false; - - // 2. apply priority only zen - verify ringer is unchanged + + // 2. verify ringer is unchanged mZenModeHelper.applyZenToRingerMode(); verify(mAudioManager, never()).setRingerModeInternal(AudioManager.RINGER_MODE_SILENT, mZenModeHelper.TAG); @@ -844,9 +857,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { public void testRingerAffectedStreamsPriorityOnly() { // in priority only mode: // ringtone, notification and system streams are affected by ringer mode - mZenModeHelper.mConfig.allowAlarms = true; - mZenModeHelper.mConfig.allowReminders = true; - mZenModeHelper.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS; + mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, Uri.EMPTY, + UPDATE_ORIGIN_APP, "test", "caller", 1); ZenModeHelper.RingerModeDelegate ringerModeDelegateRingerMuted = mZenModeHelper.new RingerModeDelegate(); @@ -862,13 +874,9 @@ public class ZenModeHelperTest extends UiServiceTestCase { // even when ringer is muted (since all ringer sounds cannot bypass DND), // system stream is still affected by ringer mode - mZenModeHelper.mConfig.allowSystem = false; - mZenModeHelper.mConfig.allowReminders = false; - mZenModeHelper.mConfig.allowCalls = false; - mZenModeHelper.mConfig.allowMessages = false; - mZenModeHelper.mConfig.allowEvents = false; - mZenModeHelper.mConfig.allowRepeatCallers = false; - mZenModeHelper.mConfig.allowConversations = false; + mZenModeHelper.setNotificationPolicy(new Policy(0,0,0), UPDATE_ORIGIN_APP, 1); + mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, Uri.EMPTY, + UPDATE_ORIGIN_APP, "test", "caller", 1); ZenModeHelper.RingerModeDelegate ringerModeDelegateRingerNotMuted = mZenModeHelper.new RingerModeDelegate(); @@ -885,7 +893,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - public void testZenSetInternalRinger_NotAllPriorityNotificationSoundsMuted_StartNormal() { + public void applyZenToRingerMode_ZEN_MODE_IMPORTANT_INTERRUPTIONS() { AudioManagerInternal mAudioManager = mock(AudioManagerInternal.class); mZenModeHelper.mAudioManager = mAudioManager; Global.putString(mContext.getContentResolver(), Global.ZEN_MODE_RINGER_LEVEL, @@ -894,7 +902,6 @@ public class ZenModeHelperTest extends UiServiceTestCase { // 1. Current ringer is normal when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL); mZenModeHelper.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS; - mZenModeHelper.mConfig.allowReminders = true; // 2. apply priority only zen - verify ringer is normal mZenModeHelper.applyZenToRingerMode(); @@ -919,7 +926,6 @@ public class ZenModeHelperTest extends UiServiceTestCase { // 1. Current ringer is silent when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_SILENT); mZenModeHelper.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS; - mZenModeHelper.mConfig.allowReminders = true; // 2. apply priority only zen - verify ringer is silent mZenModeHelper.applyZenToRingerMode(); @@ -945,7 +951,6 @@ public class ZenModeHelperTest extends UiServiceTestCase { when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL); // Set zen to priority-only with all notification sounds muted (so ringer will be muted) mZenModeHelper.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS; - mZenModeHelper.mConfig.allowReminders = true; // 2. apply priority only zen - verify zen will still be normal mZenModeHelper.applyZenToRingerMode(); @@ -977,11 +982,9 @@ public class ZenModeHelperTest extends UiServiceTestCase { // apply zen off multiple times - verify ringer is not set to normal when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_SILENT); - mZenModeHelper.mZenMode = ZEN_MODE_OFF; - mZenModeHelper.mConfig = null; // will evaluate config to zen mode off for (int i = 0; i < 3; i++) { - // if zen doesn't change, zen should not reapply itself to the ringer - mZenModeHelper.evaluateZenModeLocked(UPDATE_ORIGIN_UNKNOWN, "test", true); + mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, Uri.EMPTY, + UPDATE_ORIGIN_APP, "test", "caller", 1); } verify(mAudioManager, never()).setRingerModeInternal(AudioManager.RINGER_MODE_NORMAL, mZenModeHelper.TAG); @@ -991,8 +994,6 @@ public class ZenModeHelperTest extends UiServiceTestCase { public void testSilentRingerSavedOnZenOff_startsZenOn() { AudioManagerInternal mAudioManager = mock(AudioManagerInternal.class); mZenModeHelper.mAudioManager = mAudioManager; - mZenModeHelper.mZenMode = ZEN_MODE_OFF; - mZenModeHelper.mConfig = new ZenModeConfig(); // previously set silent ringer ZenModeHelper.RingerModeDelegate ringerModeDelegate = @@ -1003,12 +1004,12 @@ public class ZenModeHelperTest extends UiServiceTestCase { assertEquals(AudioManager.RINGER_MODE_SILENT, Global.getInt(mContext.getContentResolver(), Global.ZEN_MODE_RINGER_LEVEL, AudioManager.RINGER_MODE_NORMAL)); - // apply zen off multiple times - verify ringer is not set to normal + // apply zen on multiple times - verify ringer is not set to normal when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_SILENT); - mZenModeHelper.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS; for (int i = 0; i < 3; i++) { // if zen doesn't change, zen should not reapply itself to the ringer - mZenModeHelper.evaluateZenModeLocked(UPDATE_ORIGIN_UNKNOWN, "test", true); + mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, Uri.EMPTY, + UPDATE_ORIGIN_APP, "test", "caller", 1); } verify(mAudioManager, never()).setRingerModeInternal(AudioManager.RINGER_MODE_NORMAL, mZenModeHelper.TAG); @@ -1018,8 +1019,6 @@ public class ZenModeHelperTest extends UiServiceTestCase { public void testVibrateRingerSavedOnZenOff_startsZenOn() { AudioManagerInternal mAudioManager = mock(AudioManagerInternal.class); mZenModeHelper.mAudioManager = mAudioManager; - mZenModeHelper.mZenMode = ZEN_MODE_OFF; - mZenModeHelper.mConfig = new ZenModeConfig(); // previously set silent ringer ZenModeHelper.RingerModeDelegate ringerModeDelegate = @@ -1032,10 +1031,10 @@ public class ZenModeHelperTest extends UiServiceTestCase { // apply zen off multiple times - verify ringer is not set to normal when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_VIBRATE); - mZenModeHelper.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS; for (int i = 0; i < 3; i++) { // if zen doesn't change, zen should not reapply itself to the ringer - mZenModeHelper.evaluateZenModeLocked(UPDATE_ORIGIN_UNKNOWN, "test", true); + mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, Uri.EMPTY, + UPDATE_ORIGIN_APP, "test", "caller", 1); } verify(mAudioManager, never()).setRingerModeInternal(AudioManager.RINGER_MODE_NORMAL, mZenModeHelper.TAG); @@ -1066,22 +1065,18 @@ public class ZenModeHelperTest extends UiServiceTestCase { @Test public void testParcelConfig() { - mZenModeHelper.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS; - mZenModeHelper.mConfig.allowAlarms = false; - mZenModeHelper.mConfig.allowMedia = false; - mZenModeHelper.mConfig.allowSystem = false; - mZenModeHelper.mConfig.allowReminders = true; - mZenModeHelper.mConfig.allowCalls = true; - mZenModeHelper.mConfig.allowMessages = true; - mZenModeHelper.mConfig.allowEvents = true; - mZenModeHelper.mConfig.allowRepeatCallers = true; - mZenModeHelper.mConfig.allowConversations = true; - mZenModeHelper.mConfig.allowConversationsFrom = ZenPolicy.CONVERSATION_SENDERS_ANYONE; - mZenModeHelper.mConfig.suppressedVisualEffects = SUPPRESSED_EFFECT_BADGE; - mZenModeHelper.mConfig.manualRule = new ZenModeConfig.ZenRule(); - mZenModeHelper.mConfig.manualRule.component = new ComponentName("a", "a"); - mZenModeHelper.mConfig.manualRule.enabled = true; - mZenModeHelper.mConfig.manualRule.snoozing = true; + mZenModeHelper.setNotificationPolicy(new Policy(PRIORITY_CATEGORY_EVENTS + | PRIORITY_CATEGORY_MESSAGES | PRIORITY_CATEGORY_REPEAT_CALLERS + | PRIORITY_CATEGORY_CONVERSATIONS, PRIORITY_SENDERS_STARRED, + PRIORITY_SENDERS_STARRED, 0, CONVERSATION_SENDERS_ANYONE), UPDATE_ORIGIN_UNKNOWN, + 1); + mZenModeHelper.setManualZenRuleDeviceEffects(new ZenDeviceEffects.Builder() + .setShouldDimWallpaper(true) + .setShouldDisplayGrayscale(true) + .setShouldUseNightMode(true) + .build(), UPDATE_ORIGIN_UNKNOWN, "test", 1); + mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, Uri.EMPTY, + UPDATE_ORIGIN_UNKNOWN, "test", "me", 1); ZenModeConfig actual = mZenModeHelper.mConfig.copy(); @@ -1090,24 +1085,17 @@ public class ZenModeHelperTest extends UiServiceTestCase { @Test public void testWriteXml() throws Exception { - mZenModeHelper.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS; - mZenModeHelper.mConfig.allowAlarms = false; - mZenModeHelper.mConfig.allowMedia = false; - mZenModeHelper.mConfig.allowSystem = false; - mZenModeHelper.mConfig.allowReminders = true; - mZenModeHelper.mConfig.allowCalls = true; - mZenModeHelper.mConfig.allowMessages = true; - mZenModeHelper.mConfig.allowEvents = true; - mZenModeHelper.mConfig.allowRepeatCallers = true; - mZenModeHelper.mConfig.allowConversations = true; - mZenModeHelper.mConfig.allowConversationsFrom = ZenPolicy.CONVERSATION_SENDERS_ANYONE; - mZenModeHelper.mConfig.suppressedVisualEffects = SUPPRESSED_EFFECT_BADGE; - mZenModeHelper.mConfig.manualRule = new ZenModeConfig.ZenRule(); - mZenModeHelper.mConfig.manualRule.zenMode = - ZEN_MODE_IMPORTANT_INTERRUPTIONS; - mZenModeHelper.mConfig.manualRule.component = new ComponentName("a", "a"); - mZenModeHelper.mConfig.manualRule.pkg = "a"; - mZenModeHelper.mConfig.manualRule.enabled = true; + mZenModeHelper.setNotificationPolicy(new Policy(PRIORITY_CATEGORY_EVENTS + | PRIORITY_CATEGORY_MESSAGES | PRIORITY_CATEGORY_REPEAT_CALLERS + | PRIORITY_CATEGORY_CONVERSATIONS, PRIORITY_SENDERS_STARRED, + PRIORITY_SENDERS_STARRED, SUPPRESSED_EFFECT_BADGE, CONVERSATION_SENDERS_ANYONE), + UPDATE_ORIGIN_UNKNOWN, 1); + mZenModeHelper.setManualZenRuleDeviceEffects(new ZenDeviceEffects.Builder() + .setShouldDimWallpaper(true) + .setShouldDisplayGrayscale(true) + .build(), UPDATE_ORIGIN_UNKNOWN, "test", 1); + mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, Uri.EMPTY, + UPDATE_ORIGIN_UNKNOWN, "test", "me", 1); ZenModeConfig expected = mZenModeHelper.mConfig.copy(); if (Flags.modesUi()) { @@ -1127,10 +1115,10 @@ public class ZenModeHelperTest extends UiServiceTestCase { @Test public void testProto() throws InvalidProtocolBufferException { - mZenModeHelper.mZenMode = ZEN_MODE_IMPORTANT_INTERRUPTIONS; - // existence of manual rule means it should be in output - mZenModeHelper.mConfig.manualRule = new ZenModeConfig.ZenRule(); - mZenModeHelper.mConfig.manualRule.pkg = "android"; // system + mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, + Flags.modesApi() ? UPDATE_ORIGIN_USER : UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, + null, "test", CUSTOM_PKG_UID); + mZenModeHelper.mConfig.automaticRules = new ArrayMap<>(); // no automatic rules List<String> ids = new ArrayList<>(); @@ -1151,7 +1139,6 @@ public class ZenModeHelperTest extends UiServiceTestCase { assertTrue(cfg.getEnabled()); assertFalse(cfg.getChannelsBypassing()); } - assertEquals(Process.SYSTEM_UID, cfg.getUid()); String name = cfg.getId(); assertTrue("unexpected rule id", ids.contains(name)); ids.remove(name); @@ -1255,10 +1242,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test + @EnableFlags(FLAG_MODES_API) public void testProtoWithAutoRuleCustomPolicy() throws Exception { - // allowChannels is only valid under modes_api. - mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API); - setupZenConfig(); // clear any automatic rules just to make sure mZenModeHelper.mConfig.automaticRules = new ArrayMap<>(); @@ -1299,7 +1284,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - @EnableFlags(Flags.FLAG_MODES_API) + @EnableFlags(FLAG_MODES_API) public void testProtoWithAutoRuleWithModifiedFields() throws Exception { setupZenConfig(); mZenModeHelper.mConfig.automaticRules = new ArrayMap<>(); @@ -1384,8 +1369,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { public void testProtoWithManualRule() throws Exception { setupZenConfig(); mZenModeHelper.mConfig.automaticRules = getCustomAutomaticRules(); - mZenModeHelper.mConfig.manualRule = new ZenModeConfig.ZenRule(); - mZenModeHelper.mConfig.manualRule.enabled = true; + mZenModeHelper.setManualZenMode(INTERRUPTION_FILTER_PRIORITY, Uri.EMPTY, UPDATE_ORIGIN_APP, + "test", "me", 1); List<StatsEvent> events = new LinkedList<>(); mZenModeHelper.pullRules(events); @@ -1408,15 +1393,15 @@ public class ZenModeHelperTest extends UiServiceTestCase { // Setup configs for user 10 and 11. setupZenConfig(); ZenModeConfig config10 = mZenModeHelper.mConfig.copy(); + Policy policy = new Policy(PRIORITY_CATEGORY_MEDIA | PRIORITY_CATEGORY_ALARMS, 0, 0); + config10.applyNotificationPolicy(policy); config10.user = 10; - config10.allowAlarms = true; - config10.allowMedia = true; mZenModeHelper.setConfig(config10, null, UPDATE_ORIGIN_INIT, "writeXml", Process.SYSTEM_UID); ZenModeConfig config11 = mZenModeHelper.mConfig.copy(); config11.user = 11; - config11.allowAlarms = false; - config11.allowMedia = false; + policy = new Policy(0, 0, 0); + config11.applyNotificationPolicy(policy); mZenModeHelper.setConfig(config11, null, UPDATE_ORIGIN_INIT, "writeXml", Process.SYSTEM_UID); @@ -1583,8 +1568,9 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - public void testReadXmlRulesNotOverriden() throws Exception { + public void testReadXmlRulesNotOverridden() throws Exception { setupZenConfig(); + Policy originalPolicy = mZenModeHelper.getNotificationPolicy(); // automatic zen rule is enabled on upgrade so rules should not be overriden to default ArrayMap<String, ZenModeConfig.ZenRule> enabledAutoRule = new ArrayMap<>(); @@ -1607,7 +1593,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL); assertTrue(mZenModeHelper.mConfig.automaticRules.containsKey("customRule")); - setupZenConfigMaintained(); + assertEquals(originalPolicy, mZenModeHelper.getNotificationPolicy()); } @Test @@ -1626,7 +1612,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { parser.nextTag(); mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL); - assertEquals(0, mZenModeHelper.mConfig.suppressedVisualEffects); + assertTrue(mZenModeHelper.mConfig.getZenPolicy().shouldShowAllVisualEffects()); xml = "<zen version=\"6\" user=\"0\">\n" + "<allow calls=\"false\" repeatCallers=\"false\" messages=\"true\" " @@ -1642,7 +1628,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { parser.nextTag(); mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL); - assertEquals(0, mZenModeHelper.mConfig.suppressedVisualEffects); + assertTrue(mZenModeHelper.mConfig.getZenPolicy().shouldShowAllVisualEffects()); } @Test @@ -1661,7 +1647,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { parser.nextTag(); mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL); - assertEquals(0, mZenModeHelper.mConfig.suppressedVisualEffects); + assertTrue(mZenModeHelper.mConfig.getZenPolicy().shouldShowAllVisualEffects()); } @Test @@ -1680,11 +1666,16 @@ public class ZenModeHelperTest extends UiServiceTestCase { parser.nextTag(); mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL); - assertEquals(SUPPRESSED_EFFECT_FULL_SCREEN_INTENT - | SUPPRESSED_EFFECT_LIGHTS - | SUPPRESSED_EFFECT_AMBIENT - | SUPPRESSED_EFFECT_PEEK, - mZenModeHelper.mConfig.suppressedVisualEffects); + assertThat(mZenModeHelper.mConfig.getZenPolicy() + .isVisualEffectAllowed(VISUAL_EFFECT_FULL_SCREEN_INTENT, true)).isFalse(); + assertThat(mZenModeHelper.mConfig.getZenPolicy() + .isVisualEffectAllowed(VISUAL_EFFECT_LIGHTS, true)).isFalse(); + assertThat(mZenModeHelper.mConfig.getZenPolicy() + .isVisualEffectAllowed(VISUAL_EFFECT_PEEK, true)).isFalse(); + assertThat(mZenModeHelper.mConfig.getZenPolicy() + .isVisualEffectAllowed(VISUAL_EFFECT_AMBIENT, true)).isFalse(); + assertThat(mZenModeHelper.mConfig.getZenPolicy() + .isVisualEffectAllowed(VISUAL_EFFECT_BADGE, true)).isTrue(); xml = "<zen version=\"6\" user=\"0\">\n" + "<allow calls=\"false\" repeatCallers=\"false\" messages=\"true\" " @@ -1700,7 +1691,10 @@ public class ZenModeHelperTest extends UiServiceTestCase { parser.nextTag(); mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL); - assertEquals(SUPPRESSED_EFFECT_PEEK, mZenModeHelper.mConfig.suppressedVisualEffects); + assertThat(mZenModeHelper.mConfig.getZenPolicy() + .isVisualEffectAllowed(VISUAL_EFFECT_PEEK, true)).isFalse(); + assertThat(mZenModeHelper.mConfig.getZenPolicy() + .isVisualEffectAllowed(VISUAL_EFFECT_AMBIENT, true)).isTrue(); xml = "<zen version=\"6\" user=\"0\">\n" + "<allow calls=\"false\" repeatCallers=\"false\" messages=\"true\" " @@ -1716,18 +1710,23 @@ public class ZenModeHelperTest extends UiServiceTestCase { parser.nextTag(); mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL); - assertEquals(SUPPRESSED_EFFECT_FULL_SCREEN_INTENT - | SUPPRESSED_EFFECT_LIGHTS - | SUPPRESSED_EFFECT_AMBIENT, - mZenModeHelper.mConfig.suppressedVisualEffects); + assertThat(mZenModeHelper.mConfig.getZenPolicy() + .isVisualEffectAllowed(VISUAL_EFFECT_FULL_SCREEN_INTENT, true)).isFalse(); + assertThat(mZenModeHelper.mConfig.getZenPolicy() + .isVisualEffectAllowed(VISUAL_EFFECT_LIGHTS, true)).isFalse(); + assertThat(mZenModeHelper.mConfig.getZenPolicy() + .isVisualEffectAllowed(VISUAL_EFFECT_AMBIENT, true)).isFalse(); + assertThat(mZenModeHelper.mConfig.getZenPolicy() + .isVisualEffectAllowed(VISUAL_EFFECT_BADGE, true)).isTrue(); } @Test public void testReadXmlResetDefaultRules() throws Exception { setupZenConfig(); + Policy originalPolicy = mZenModeHelper.getNotificationPolicy(); // no enabled automatic zen rules and no default rules - // so rules should be overriden by default rules + // so rules should be overridden by default rules mZenModeHelper.mConfig.automaticRules = new ArrayMap<>(); // set previous version @@ -1745,13 +1744,14 @@ public class ZenModeHelperTest extends UiServiceTestCase { assertTrue(rules.containsKey(defaultId)); } - setupZenConfigMaintained(); + assertEquals(originalPolicy, mZenModeHelper.getNotificationPolicy()); } @Test public void testReadXmlAllDisabledRulesResetDefaultRules() throws Exception { setupZenConfig(); + Policy originalPolicy = mZenModeHelper.getNotificationPolicy(); // all automatic zen rules are disabled on upgrade (and default rules don't already exist) // so rules should be overriden by default rules @@ -1782,12 +1782,13 @@ public class ZenModeHelperTest extends UiServiceTestCase { } assertFalse(rules.containsKey("customRule")); - setupZenConfigMaintained(); + assertEquals(originalPolicy, mZenModeHelper.getNotificationPolicy()); } @Test public void testReadXmlOnlyOneDefaultRuleExists() throws Exception { setupZenConfig(); + Policy originalPolicy = mZenModeHelper.getNotificationPolicy(); // all automatic zen rules are disabled on upgrade and only one default rule exists // so rules should be overriden to the default rules @@ -1834,12 +1835,13 @@ public class ZenModeHelperTest extends UiServiceTestCase { } assertFalse(rules.containsKey("customRule")); - setupZenConfigMaintained(); + assertEquals(originalPolicy, mZenModeHelper.getNotificationPolicy()); } @Test public void testReadXmlDefaultRulesExist() throws Exception { setupZenConfig(); + Policy originalPolicy = mZenModeHelper.getNotificationPolicy(); // Default rules exist so rules should not be overridden by defaults ArrayMap<String, ZenModeConfig.ZenRule> automaticRules = new ArrayMap<>(); @@ -1897,13 +1899,13 @@ public class ZenModeHelperTest extends UiServiceTestCase { // check default rules ArrayMap<String, ZenModeConfig.ZenRule> rules = mZenModeHelper.mConfig.automaticRules; - assertTrue(rules.size() != 0); + assertEquals(3, rules.size()); for (String defaultId : ZenModeConfig.DEFAULT_RULE_IDS) { assertTrue(rules.containsKey(defaultId)); } assertTrue(rules.containsKey("customRule")); - setupZenConfigMaintained(); + assertEquals(originalPolicy, mZenModeHelper.getNotificationPolicy()); List<StatsEvent> events = new LinkedList<>(); mZenModeHelper.pullRules(events); @@ -1911,11 +1913,12 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - @EnableFlags(Flags.FLAG_MODES_API) + @EnableFlags(FLAG_MODES_API) public void testReadXml_onModesApi_noUpgrade() throws Exception { // When reading XML for something that is already on the modes API system, make sure no // rules' policies get changed. setupZenConfig(); + Policy originalPolicy = mZenModeHelper.getNotificationPolicy(); // Shared for rules ArrayMap<String, ZenModeConfig.ZenRule> enabledAutoRules = new ArrayMap<>(); @@ -1947,7 +1950,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL); // basic check: global config maintained - setupZenConfigMaintained(); + assertEquals(originalPolicy, mZenModeHelper.getNotificationPolicy()); // Find our automatic rules. ArrayMap<String, ZenModeConfig.ZenRule> rules = mZenModeHelper.mConfig.automaticRules; @@ -1958,12 +1961,13 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - @EnableFlags(Flags.FLAG_MODES_API) + @EnableFlags(FLAG_MODES_API) public void testReadXml_upgradeToModesApi_makesCustomPolicies() throws Exception { // When reading in an XML file written from a pre-modes-API version, confirm that we create // a custom policy matching the global config for any automatic rule with no specified // policy. setupZenConfig(); + Policy originalPolicy = mZenModeHelper.getNotificationPolicy(); ArrayMap<String, ZenModeConfig.ZenRule> enabledAutoRule = new ArrayMap<>(); ZenModeConfig.ZenRule customRule = new ZenModeConfig.ZenRule(); @@ -1985,7 +1989,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL); // basic check: global config maintained - setupZenConfigMaintained(); + assertEquals(originalPolicy, mZenModeHelper.getNotificationPolicy()); // Find our automatic rule and check that it has a policy set now ArrayMap<String, ZenModeConfig.ZenRule> rules = mZenModeHelper.mConfig.automaticRules; @@ -2009,12 +2013,13 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - @EnableFlags(Flags.FLAG_MODES_API) + @EnableFlags(FLAG_MODES_API) public void testReadXml_upgradeToModesApi_fillsInCustomPolicies() throws Exception { // When reading in an XML file written from a pre-modes-API version, confirm that for an // underspecified ZenPolicy, we fill in all of the gaps with things from the global config // in order to maintain consistency of behavior. setupZenConfig(); + Policy originalPolicy = mZenModeHelper.getNotificationPolicy(); ArrayMap<String, ZenModeConfig.ZenRule> enabledAutoRule = new ArrayMap<>(); ZenModeConfig.ZenRule customRule = new ZenModeConfig.ZenRule(); @@ -2041,7 +2046,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { mZenModeHelper.readXml(parser, false, UserHandle.USER_ALL); // basic check: global config maintained - setupZenConfigMaintained(); + assertEquals(originalPolicy, mZenModeHelper.getNotificationPolicy()); // Find our automatic rule and check that it has a policy set now ArrayMap<String, ZenModeConfig.ZenRule> rules = mZenModeHelper.mConfig.automaticRules; @@ -2068,7 +2073,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - @EnableFlags(Flags.FLAG_MODES_API) + @EnableFlags(FLAG_MODES_API) public void testReadXml_upgradeToModesApi_existingDefaultRulesGetCustomPolicy() throws Exception { setupZenConfig(); @@ -2230,7 +2235,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - @EnableFlags(Flags.FLAG_MODES_API) + @EnableFlags(FLAG_MODES_API) public void testDefaultRulesFromConfig_modesApi_getPolicies() { // After mZenModeHelper was created, set some things in the policy so it's changed from // default. @@ -2389,7 +2394,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - @EnableFlags(Flags.FLAG_MODES_API) + @EnableFlags(FLAG_MODES_API) public void testAddAutomaticZenRule_modesApi_fillsInDefaultValues() { // When a new automatic zen rule is added with only some fields filled in, ensure that // all unset fields are filled in with device defaults. @@ -2436,19 +2441,19 @@ public class ZenModeHelperTest extends UiServiceTestCase { assertThat(rule2InConfig.zenPolicy.getPriorityMessageSenders()) .isEqualTo(PEOPLE_TYPE_CONTACTS); assertThat(rule2InConfig.zenPolicy.getVisualEffectFullScreenIntent()) - .isEqualTo(ZenPolicy.STATE_ALLOW); + .isEqualTo(STATE_ALLOW); // the rest of rule 2's settings should be the device defaults assertThat(rule2InConfig.zenPolicy.getPriorityConversationSenders()) .isEqualTo(CONVERSATION_SENDERS_IMPORTANT); assertThat(rule2InConfig.zenPolicy.getPriorityCategorySystem()) - .isEqualTo(ZenPolicy.STATE_DISALLOW); + .isEqualTo(STATE_DISALLOW); assertThat(rule2InConfig.zenPolicy.getPriorityCategoryAlarms()) - .isEqualTo(ZenPolicy.STATE_ALLOW); + .isEqualTo(STATE_ALLOW); assertThat(rule2InConfig.zenPolicy.getVisualEffectPeek()) - .isEqualTo(ZenPolicy.STATE_DISALLOW); + .isEqualTo(STATE_DISALLOW); assertThat(rule2InConfig.zenPolicy.getVisualEffectNotificationList()) - .isEqualTo(ZenPolicy.STATE_ALLOW); + .isEqualTo(STATE_ALLOW); } @Test @@ -2590,9 +2595,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test + @EnableFlags(FLAG_MODES_API) public void addAutomaticZenRule_fromApp_ignoresHiddenEffects() { - mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API); - ZenDeviceEffects zde = new ZenDeviceEffects.Builder() .setShouldDisplayGrayscale(true) .setShouldSuppressAmbientDisplay(true) @@ -2624,9 +2628,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test + @EnableFlags(FLAG_MODES_API) public void addAutomaticZenRule_fromSystem_respectsHiddenEffects() { - mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API); - ZenDeviceEffects zde = new ZenDeviceEffects.Builder() .setShouldDisplayGrayscale(true) .setShouldSuppressAmbientDisplay(true) @@ -2652,9 +2655,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test + @EnableFlags(FLAG_MODES_API) public void addAutomaticZenRule_fromUser_respectsHiddenEffects() throws Exception { - mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API); - ZenDeviceEffects zde = new ZenDeviceEffects.Builder() .setShouldDisplayGrayscale(true) .setShouldSuppressAmbientDisplay(true) @@ -2682,8 +2684,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test + @EnableFlags(FLAG_MODES_API) public void updateAutomaticZenRule_fromApp_preservesPreviousHiddenEffects() { - mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API); ZenDeviceEffects original = new ZenDeviceEffects.Builder() .setShouldDisableTapToWake(true) .addExtraEffect("extra") @@ -2717,8 +2719,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test + @EnableFlags(FLAG_MODES_API) public void updateAutomaticZenRule_fromSystem_updatesHiddenEffects() { - mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API); ZenDeviceEffects original = new ZenDeviceEffects.Builder() .setShouldDisableTapToWake(true) .build(); @@ -2744,8 +2746,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test + @EnableFlags(FLAG_MODES_API) public void updateAutomaticZenRule_fromUser_updatesHiddenEffects() { - mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API); ZenDeviceEffects original = new ZenDeviceEffects.Builder() .setShouldDisableTapToWake(true) .build(); @@ -2775,7 +2777,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - @EnableFlags(Flags.FLAG_MODES_API) + @EnableFlags(FLAG_MODES_API) public void updateAutomaticZenRule_nullPolicy_doesNothing() { // Test that when updateAutomaticZenRule is called with a null policy, nothing changes // about the existing policy. @@ -2796,11 +2798,11 @@ public class ZenModeHelperTest extends UiServiceTestCase { AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(ruleId); assertThat(savedRule.getZenPolicy().getPriorityCategoryCalls()) - .isEqualTo(ZenPolicy.STATE_DISALLOW); + .isEqualTo(STATE_DISALLOW); } @Test - @EnableFlags(Flags.FLAG_MODES_API) + @EnableFlags(FLAG_MODES_API) public void updateAutomaticZenRule_overwritesExistingPolicy() { // Test that when updating an automatic zen rule with an existing policy, the newly set // fields overwrite those from the previous policy, but unset fields in the new policy @@ -2826,18 +2828,18 @@ public class ZenModeHelperTest extends UiServiceTestCase { AutomaticZenRule savedRule = mZenModeHelper.getAutomaticZenRule(ruleId); assertThat(savedRule.getZenPolicy().getPriorityCategoryCalls()) - .isEqualTo(ZenPolicy.STATE_ALLOW); // from update + .isEqualTo(STATE_ALLOW); // from update assertThat(savedRule.getZenPolicy().getPriorityCallSenders()) .isEqualTo(ZenPolicy.PEOPLE_TYPE_CONTACTS); // from update assertThat(savedRule.getZenPolicy().getPriorityCategoryAlarms()) - .isEqualTo(ZenPolicy.STATE_DISALLOW); // from original + .isEqualTo(STATE_DISALLOW); // from original assertThat(savedRule.getZenPolicy().getPriorityCategoryReminders()) - .isEqualTo(ZenPolicy.STATE_ALLOW); // from original + .isEqualTo(STATE_ALLOW); // from original } @Test - @EnableFlags(Flags.FLAG_MODES_API) + @EnableFlags(FLAG_MODES_API) public void addAutomaticZenRule_withTypeBedtime_replacesDisabledSleeping() { ZenRule sleepingRule = createCustomAutomaticRule(ZEN_MODE_IMPORTANT_INTERRUPTIONS, ZenModeConfig.EVERY_NIGHT_DEFAULT_RULE_ID); @@ -2857,7 +2859,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - @EnableFlags(Flags.FLAG_MODES_API) + @EnableFlags(FLAG_MODES_API) public void addAutomaticZenRule_withTypeBedtime_keepsEnabledSleeping() { ZenRule sleepingRule = createCustomAutomaticRule(ZEN_MODE_IMPORTANT_INTERRUPTIONS, ZenModeConfig.EVERY_NIGHT_DEFAULT_RULE_ID); @@ -2878,7 +2880,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - @EnableFlags(Flags.FLAG_MODES_API) + @EnableFlags(FLAG_MODES_API) public void addAutomaticZenRule_withTypeBedtime_keepsCustomizedSleeping() { ZenRule sleepingRule = createCustomAutomaticRule(ZEN_MODE_IMPORTANT_INTERRUPTIONS, ZenModeConfig.EVERY_NIGHT_DEFAULT_RULE_ID); @@ -2899,8 +2901,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test + @EnableFlags(FLAG_MODES_API) public void testSetManualZenMode() { - mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API); setupZenConfig(); // note that caller=null because that's how it comes in from NMS.setZenMode @@ -2919,68 +2921,83 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - @EnableFlags(Flags.FLAG_MODES_API) - @DisableFlags(Flags.FLAG_MODES_UI) - public void setManualZenMode_off_snoozesActiveRules(@TestParameter ChangeOrigin setZenOrigin) { - // Start with an active rule and an inactive rule. - mZenModeHelper.mConfig.automaticRules.clear(); - AutomaticZenRule activeRule = new AutomaticZenRule.Builder("Test", CONDITION_ID) - .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) - .build(); - String activeRuleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), - activeRule, UPDATE_ORIGIN_APP, "add it", CUSTOM_PKG_UID); - mZenModeHelper.setAutomaticZenRuleState(activeRuleId, CONDITION_TRUE, UPDATE_ORIGIN_APP, - CUSTOM_PKG_UID); - AutomaticZenRule inactiveRule = new AutomaticZenRule.Builder("Test", CONDITION_ID) - .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) - .build(); - String inactiveRuleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), - inactiveRule, UPDATE_ORIGIN_APP, "add it", CUSTOM_PKG_UID); - - assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_IMPORTANT_INTERRUPTIONS); - - // User turns DND off. - mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, null, setZenOrigin.value(), - "snoozing", "systemui", Process.SYSTEM_UID); - assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_OFF); - assertThat(mZenModeHelper.mConfig.automaticRules.get(activeRuleId).snoozing).isTrue(); - assertThat(mZenModeHelper.mConfig.automaticRules.get(inactiveRuleId).snoozing).isFalse(); + @EnableFlags(FLAG_MODES_API) + @DisableFlags(FLAG_MODES_UI) + public void setManualZenMode_off_snoozesActiveRules() { + for (ChangeOrigin origin : ChangeOrigin.values()) { + // Start with an active rule and an inactive rule. + mZenModeHelper.mConfig.automaticRules.clear(); + AutomaticZenRule activeRule = new AutomaticZenRule.Builder("Test", CONDITION_ID) + .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) + .build(); + String activeRuleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), + activeRule, UPDATE_ORIGIN_APP, "add it", CUSTOM_PKG_UID); + mZenModeHelper.setAutomaticZenRuleState(activeRuleId, CONDITION_TRUE, UPDATE_ORIGIN_APP, + CUSTOM_PKG_UID); + AutomaticZenRule inactiveRule = new AutomaticZenRule.Builder("Test", CONDITION_ID) + .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) + .build(); + String inactiveRuleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), + inactiveRule, UPDATE_ORIGIN_APP, "add it", CUSTOM_PKG_UID); + + assertWithMessage("Failure for origin " + origin.name()) + .that(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_IMPORTANT_INTERRUPTIONS); + + // User turns DND off. + mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, null, origin.value(), + "snoozing", "systemui", Process.SYSTEM_UID); + assertWithMessage("Failure for origin " + origin.name()) + .that(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_OFF); + assertWithMessage("Failure for origin " + origin.name()) + .that(mZenModeHelper.mConfig.automaticRules.get(activeRuleId).snoozing) + .isTrue(); + assertWithMessage("Failure for origin " + origin.name()) + .that(mZenModeHelper.mConfig.automaticRules.get(inactiveRuleId).snoozing) + .isFalse(); + } } @Test - @EnableFlags({Flags.FLAG_MODES_API, Flags.FLAG_MODES_UI}) - public void setManualZenMode_off_doesNotSnoozeRulesIfFromUser( - @TestParameter ChangeOrigin setZenOrigin) { - // Start with an active rule and an inactive rule - mZenModeHelper.mConfig.automaticRules.clear(); - AutomaticZenRule activeRule = new AutomaticZenRule.Builder("Test", CONDITION_ID) - .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) - .build(); - String activeRuleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), - activeRule, UPDATE_ORIGIN_APP, "add it", CUSTOM_PKG_UID); - mZenModeHelper.setAutomaticZenRuleState(activeRuleId, CONDITION_TRUE, UPDATE_ORIGIN_APP, - CUSTOM_PKG_UID); - AutomaticZenRule inactiveRule = new AutomaticZenRule.Builder("Test", CONDITION_ID) - .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) - .build(); - String inactiveRuleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), - inactiveRule, UPDATE_ORIGIN_APP, "add it", CUSTOM_PKG_UID); + @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI}) + public void setManualZenMode_off_doesNotSnoozeRulesIfFromUser() { + for (ChangeOrigin origin : ChangeOrigin.values()) { + // Start with an active rule and an inactive rule + mZenModeHelper.mConfig.automaticRules.clear(); + AutomaticZenRule activeRule = new AutomaticZenRule.Builder("Test", CONDITION_ID) + .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) + .build(); + String activeRuleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), + activeRule, UPDATE_ORIGIN_APP, "add it", CUSTOM_PKG_UID); + mZenModeHelper.setAutomaticZenRuleState(activeRuleId, CONDITION_TRUE, UPDATE_ORIGIN_APP, + CUSTOM_PKG_UID); + AutomaticZenRule inactiveRule = new AutomaticZenRule.Builder("Test", CONDITION_ID) + .setInterruptionFilter(INTERRUPTION_FILTER_PRIORITY) + .build(); + String inactiveRuleId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), + inactiveRule, UPDATE_ORIGIN_APP, "add it", CUSTOM_PKG_UID); - assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_IMPORTANT_INTERRUPTIONS); - - // User turns DND off. - mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, null, setZenOrigin.value(), - "snoozing", "systemui", Process.SYSTEM_UID); - ZenModeConfig config = mZenModeHelper.mConfig; - if (setZenOrigin == ChangeOrigin.ORIGIN_USER) { - // Other rule was unaffected. assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_IMPORTANT_INTERRUPTIONS); - assertThat(config.automaticRules.get(activeRuleId).snoozing).isFalse(); - assertThat(config.automaticRules.get(inactiveRuleId).snoozing).isFalse(); - } else { - assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_OFF); - assertThat(config.automaticRules.get(activeRuleId).snoozing).isTrue(); - assertThat(config.automaticRules.get(inactiveRuleId).snoozing).isFalse(); + + // User turns DND off. + mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, null, origin.value(), + "snoozing", "systemui", Process.SYSTEM_UID); + ZenModeConfig config = mZenModeHelper.mConfig; + if (origin == ChangeOrigin.ORIGIN_USER) { + // Other rule was unaffected. + assertWithMessage("Failure for origin " + origin.name()).that( + mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_IMPORTANT_INTERRUPTIONS); + assertWithMessage("Failure for origin " + origin.name()).that( + config.automaticRules.get(activeRuleId).snoozing).isFalse(); + assertWithMessage("Failure for origin " + origin.name()).that( + config.automaticRules.get(inactiveRuleId).snoozing).isFalse(); + } else { + assertWithMessage("Failure for origin " + origin.name()).that( + mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_OFF); + assertWithMessage("Failure for origin " + origin.name()).that( + config.automaticRules.get(activeRuleId).snoozing).isTrue(); + assertWithMessage("Failure for origin " + origin.name()).that( + config.automaticRules.get(inactiveRuleId).snoozing).isFalse(); + } } } @@ -3002,45 +3019,17 @@ public class ZenModeHelperTest extends UiServiceTestCase { assertEquals(ZEN_MODE_OFF, mZenModeHelper.mZenMode); } - private enum ModesFlag { - MODES_UI(2, /* originForUserActionInSystemUi= */ UPDATE_ORIGIN_USER), - MODES_API(1, /* originForUserActionInSystemUi= */ UPDATE_ORIGIN_USER), - DISABLED(0, /* originForUserActionInSystemUi= */ UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI); - - private final int mFlagsEnabled; - @ConfigChangeOrigin - private final int mOriginForUserActionInSystemUi; - - ModesFlag(int flagsEnabled, @ConfigChangeOrigin int originForUserActionInSystemUi) { - this.mFlagsEnabled = flagsEnabled; - this.mOriginForUserActionInSystemUi = originForUserActionInSystemUi; - } - - void applyFlags(SetFlagsRule setFlagsRule) { - if (mFlagsEnabled >= 1) { - setFlagsRule.enableFlags(Flags.FLAG_MODES_API); - } else { - setFlagsRule.disableFlags(Flags.FLAG_MODES_API); - } - if (mFlagsEnabled >= 2) { - setFlagsRule.enableFlags(Flags.FLAG_MODES_UI); - } else { - setFlagsRule.disableFlags(Flags.FLAG_MODES_UI); - } - } - } - @Test - public void testZenModeEventLog_setManualZenMode(@TestParameter ModesFlag modesFlag) + public void testZenModeEventLog_setManualZenMode() throws IllegalArgumentException { - modesFlag.applyFlags(mSetFlagsRule); mTestFlagResolver.setFlagOverride(LOG_DND_STATE_EVENTS, true); setupZenConfig(); // Turn zen mode on (to important_interruptions) // Need to additionally call the looper in order to finish the post-apply-config process mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, - modesFlag.mOriginForUserActionInSystemUi, "", null, Process.SYSTEM_UID); + Flags.modesApi() ? UPDATE_ORIGIN_USER: UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "", null, + Process.SYSTEM_UID); // Now turn zen mode off, but via a different package UID -- this should get registered as // "not an action by the user" because some other app is changing zen mode @@ -3068,7 +3057,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { assertEquals(DNDProtoEnums.MANUAL_RULE, mZenModeEventLogger.getChangedRuleType(0)); assertEquals(1, mZenModeEventLogger.getNumRulesActive(0)); assertThat(mZenModeEventLogger.getFromSystemOrSystemUi(0)).isEqualTo( - modesFlag == ModesFlag.DISABLED); + !(Flags.modesUi() || Flags.modesApi())); assertTrue(mZenModeEventLogger.getIsUserAction(0)); assertEquals(Process.SYSTEM_UID, mZenModeEventLogger.getPackageUid(0)); checkDndProtoMatchesSetupZenConfig(mZenModeEventLogger.getPolicyProto(0)); @@ -3097,9 +3086,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - public void testZenModeEventLog_automaticRules(@TestParameter ModesFlag modesFlag) + public void testZenModeEventLog_automaticRules() throws IllegalArgumentException { - modesFlag.applyFlags(mSetFlagsRule); mTestFlagResolver.setFlagOverride(LOG_DND_STATE_EVENTS, true); setupZenConfig(); @@ -3116,15 +3104,14 @@ public class ZenModeHelperTest extends UiServiceTestCase { // Event 1: Mimic the rule coming on automatically by setting the Condition to STATE_TRUE mZenModeHelper.setAutomaticZenRuleState(id, new Condition(zenRule.getConditionId(), "", STATE_TRUE), - UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, - Process.SYSTEM_UID); + UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, Process.SYSTEM_UID); // Event 2: "User" turns off the automatic rule (sets it to not enabled) zenRule.setEnabled(false); mZenModeHelper.updateAutomaticZenRule(id, zenRule, - modesFlag.mOriginForUserActionInSystemUi, "", Process.SYSTEM_UID); + Flags.modesApi() ? UPDATE_ORIGIN_USER: UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "", + Process.SYSTEM_UID); - // Add a new system rule AutomaticZenRule systemRule = new AutomaticZenRule("systemRule", null, new ComponentName("android", "ScheduleConditionProvider"), @@ -3132,7 +3119,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { null, NotificationManager.INTERRUPTION_FILTER_PRIORITY, true); String systemId = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), systemRule, - UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "test", Process.SYSTEM_UID); + Flags.modesApi() ? UPDATE_ORIGIN_USER: UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "test", + Process.SYSTEM_UID); // Event 3: turn on the system rule mZenModeHelper.setAutomaticZenRuleState(systemId, @@ -3140,9 +3128,9 @@ public class ZenModeHelperTest extends UiServiceTestCase { UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, Process.SYSTEM_UID); // Event 4: "User" deletes the rule - mZenModeHelper.removeAutomaticZenRule(systemId, modesFlag.mOriginForUserActionInSystemUi, - "", Process.SYSTEM_UID); - + mZenModeHelper.removeAutomaticZenRule(systemId, + Flags.modesApi() ? UPDATE_ORIGIN_USER: UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "", + Process.SYSTEM_UID); // In total, this represents 4 events assertEquals(4, mZenModeEventLogger.numLoggedChanges()); @@ -3201,7 +3189,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - @EnableFlags(Flags.FLAG_MODES_API) + @EnableFlags(FLAG_MODES_API) public void testZenModeEventLog_automaticRuleActivatedFromAppByAppAndUser() throws IllegalArgumentException { mTestFlagResolver.setFlagOverride(LOG_DND_STATE_EVENTS, true); @@ -3288,22 +3276,19 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - public void testZenModeEventLog_policyChanges(@TestParameter ModesFlag modesFlag) + public void testZenModeEventLog_policyChanges() throws IllegalArgumentException { - modesFlag.applyFlags(mSetFlagsRule); mTestFlagResolver.setFlagOverride(LOG_DND_STATE_EVENTS, true); setupZenConfig(); // First just turn zen mode on mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, - modesFlag.mOriginForUserActionInSystemUi, "", null, Process.SYSTEM_UID); + UPDATE_ORIGIN_USER, "", null, Process.SYSTEM_UID); // Now change the policy slightly; want to confirm that this'll be reflected in the logs ZenModeConfig newConfig = mZenModeHelper.mConfig.copy(); - newConfig.allowAlarms = true; - newConfig.allowRepeatCallers = false; - mZenModeHelper.setNotificationPolicy(newConfig.toNotificationPolicy(), - modesFlag.mOriginForUserActionInSystemUi, Process.SYSTEM_UID); + mZenModeHelper.setNotificationPolicy(new Policy(PRIORITY_CATEGORY_ALARMS, 0, 0), + UPDATE_ORIGIN_USER, Process.SYSTEM_UID); // Turn zen mode off; we want to make sure policy changes do not get logged when zen mode // is off. @@ -3311,10 +3296,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { null, Process.SYSTEM_UID); // Change the policy again - newConfig.allowMessages = false; - newConfig.allowRepeatCallers = true; - mZenModeHelper.setNotificationPolicy(newConfig.toNotificationPolicy(), - modesFlag.mOriginForUserActionInSystemUi, Process.SYSTEM_UID); + mZenModeHelper.setNotificationPolicy(new Policy(PRIORITY_CATEGORY_REPEAT_CALLERS, 0, 0), + UPDATE_ORIGIN_USER, Process.SYSTEM_UID); // Total events: we only expect ones for turning on, changing policy, and turning off assertEquals(3, mZenModeEventLogger.numLoggedChanges()); @@ -3347,9 +3330,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - public void testZenModeEventLog_ruleCounts(@TestParameter ModesFlag modesFlag) - throws IllegalArgumentException { - modesFlag.applyFlags(mSetFlagsRule); + public void testZenModeEventLog_ruleCounts() throws IllegalArgumentException { mTestFlagResolver.setFlagOverride(LOG_DND_STATE_EVENTS, true); setupZenConfig(); @@ -3374,14 +3355,12 @@ public class ZenModeHelperTest extends UiServiceTestCase { // Rule 3, has stricter settings than the default settings ZenModeConfig ruleConfig = mZenModeHelper.mConfig.copy(); - ruleConfig.allowReminders = false; - ruleConfig.allowCalls = false; - ruleConfig.allowMessages = false; + ruleConfig.applyNotificationPolicy(new Policy(0, 0, 0)); AutomaticZenRule zenRule3 = new AutomaticZenRule("name3", null, new ComponentName("android", "ScheduleConditionProvider"), ZenModeConfig.toScheduleConditionId(new ScheduleInfo()), - ruleConfig.toZenPolicy(), + ruleConfig.getZenPolicy(), NotificationManager.INTERRUPTION_FILTER_PRIORITY, true); String id3 = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule3, UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "test", Process.SYSTEM_UID); @@ -3452,10 +3431,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - public void testZenModeEventLog_noLogWithNoConfigChange( - @TestParameter ModesFlag modesFlag) throws IllegalArgumentException { + public void testZenModeEventLog_noLogWithNoConfigChange() throws IllegalArgumentException { // If evaluateZenMode is called independently of a config change, don't log. - modesFlag.applyFlags(mSetFlagsRule); mTestFlagResolver.setFlagOverride(LOG_DND_STATE_EVENTS, true); setupZenConfig(); @@ -3472,17 +3449,16 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - public void testZenModeEventLog_reassignUid(@TestParameter ModesFlag modesFlag) + public void testZenModeEventLog_reassignUid() throws IllegalArgumentException { // Test that, only in specific cases, we reassign the calling UID to one associated with // the automatic rule owner. - modesFlag.applyFlags(mSetFlagsRule); mTestFlagResolver.setFlagOverride(LOG_DND_STATE_EVENTS, true); setupZenConfig(); // Explicitly set up all rules with the same policy as the manual rule so there will be // no policy changes in this test case. - ZenPolicy manualRulePolicy = mZenModeHelper.mConfig.toZenPolicy(); + ZenPolicy manualRulePolicy = mZenModeHelper.mConfig.getZenPolicy(); // Rule 1, owned by a package AutomaticZenRule zenRule = new AutomaticZenRule("name", @@ -3502,7 +3478,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { manualRulePolicy, NotificationManager.INTERRUPTION_FILTER_PRIORITY, true); String id2 = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), zenRule2, - modesFlag.mOriginForUserActionInSystemUi, "test", Process.SYSTEM_UID); + UPDATE_ORIGIN_USER, "test", Process.SYSTEM_UID); // Turn on rule 1; call looks like it's from the system. Because setting a condition is // typically an automatic (non-user-initiated) action, expect the calling UID to be @@ -3521,7 +3497,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { // from the system-provided one. zenRule.setEnabled(false); mZenModeHelper.updateAutomaticZenRule(id, zenRule, - modesFlag.mOriginForUserActionInSystemUi, "", Process.SYSTEM_UID); + UPDATE_ORIGIN_USER, "", Process.SYSTEM_UID); // Add a manual rule. Any manual rule changes should not get calling uids reassigned. mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, UPDATE_ORIGIN_APP, @@ -3578,10 +3554,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - public void testZenModeEventLog_channelsBypassingChanges( - @TestParameter ModesFlag modesFlag) { + public void testZenModeEventLog_channelsBypassingChanges() { // Verify that the right thing happens when the canBypassDnd value changes. - modesFlag.applyFlags(mSetFlagsRule); mTestFlagResolver.setFlagOverride(LOG_DND_STATE_EVENTS, true); setupZenConfig(); @@ -3589,21 +3563,25 @@ public class ZenModeHelperTest extends UiServiceTestCase { // as a user action, and *should* get its UID reassigned. mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, "", CUSTOM_PKG_NAME, Process.SYSTEM_UID); + assertEquals(1, mZenModeEventLogger.numLoggedChanges()); // Now change apps bypassing to true ZenModeConfig newConfig = mZenModeHelper.mConfig.copy(); newConfig.areChannelsBypassingDnd = true; mZenModeHelper.setNotificationPolicy(newConfig.toNotificationPolicy(), UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, Process.SYSTEM_UID); + assertEquals(2, mZenModeEventLogger.numLoggedChanges()); // and then back to false, all without changing anything else newConfig.areChannelsBypassingDnd = false; mZenModeHelper.setNotificationPolicy(newConfig.toNotificationPolicy(), UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, Process.SYSTEM_UID); + assertEquals(3, mZenModeEventLogger.numLoggedChanges()); // Turn off manual mode, call from a package: don't reset UID even though enabler is set mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, null, UPDATE_ORIGIN_APP, "", CUSTOM_PKG_NAME, 12345); + assertEquals(4, mZenModeEventLogger.numLoggedChanges()); // And likewise when turning it back on again mZenModeHelper.setManualZenMode(ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, UPDATE_ORIGIN_APP, @@ -3642,11 +3620,11 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test + @EnableFlags(FLAG_MODES_API) public void testZenModeEventLog_policyAllowChannels() { // when modes_api flag is on, ensure that any change in allow_channels gets logged, // even when there are no other changes. mTestFlagResolver.setFlagOverride(LOG_DND_STATE_EVENTS, true); - mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API); // Default zen config has allow channels = priority (aka on) setupZenConfig(); @@ -3658,8 +3636,12 @@ public class ZenModeHelperTest extends UiServiceTestCase { // Now change only the channels part of the policy; want to confirm that this'll be // reflected in the logs ZenModeConfig newConfig = mZenModeHelper.mConfig.copy(); - newConfig.allowPriorityChannels = false; - mZenModeHelper.setNotificationPolicy(newConfig.toNotificationPolicy(), + Policy oldPolicy = newConfig.toNotificationPolicy(); + Policy newPolicy = new Policy(oldPolicy.priorityCategories, oldPolicy.priorityCallSenders, + oldPolicy.priorityMessageSenders, oldPolicy.suppressedVisualEffects, + STATE_PRIORITY_CHANNELS_BLOCKED, + oldPolicy.priorityConversationSenders); + mZenModeHelper.setNotificationPolicy(newPolicy, UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, Process.SYSTEM_UID); // Total events: one for turning on, one for changing policy @@ -3688,7 +3670,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - @EnableFlags(Flags.FLAG_MODES_API) + @EnableFlags(FLAG_MODES_API) public void testZenModeEventLog_ruleWithInterruptionFilterAll_notLoggedAsDndChange() { mTestFlagResolver.setFlagOverride(LOG_DND_STATE_EVENTS, true); setupZenConfig(); @@ -3730,7 +3712,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - @EnableFlags(Flags.FLAG_MODES_API) + @EnableFlags(FLAG_MODES_API) public void testZenModeEventLog_activeRuleTypes() { mTestFlagResolver.setFlagOverride(LOG_DND_STATE_EVENTS, true); setupZenConfig(); @@ -3748,7 +3730,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { // Create immersive rule AutomaticZenRule immersive = new AutomaticZenRule.Builder("Immersed", CONDITION_ID) .setType(TYPE_IMMERSIVE) - .setZenPolicy(mZenModeHelper.mConfig.toZenPolicy()) // same as the manual rule + .setZenPolicy(mZenModeHelper.mConfig.getZenPolicy()) // same as the manual rule .build(); String immersiveId = mZenModeHelper.addAutomaticZenRule(mPkg, immersive, UPDATE_ORIGIN_APP, "reason", CUSTOM_PKG_UID); @@ -3819,10 +3801,9 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - @DisableFlags(Flags.FLAG_MODES_API) + @DisableFlags(FLAG_MODES_API) public void testUpdateConsolidatedPolicy_preModesApiDefaultRulesOnly_takesGlobalDefault() { setupZenConfig(); - // When there's one automatic rule active and it doesn't specify a policy, test that the // resulting consolidated policy is one that matches the default rule settings. AutomaticZenRule zenRule = new AutomaticZenRule("name", @@ -3839,6 +3820,9 @@ public class ZenModeHelperTest extends UiServiceTestCase { new Condition(zenRule.getConditionId(), "", STATE_TRUE), UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI, Process.SYSTEM_UID); + assertEquals(mZenModeHelper.getNotificationPolicy(), + mZenModeHelper.getConsolidatedNotificationPolicy()); + // inspect the consolidated policy. Based on setupZenConfig() values. assertFalse(mZenModeHelper.mConsolidatedPolicy.allowAlarms()); assertFalse(mZenModeHelper.mConsolidatedPolicy.allowMedia()); @@ -3853,9 +3837,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - public void testUpdateConsolidatedPolicy_modesApiDefaultRulesOnly_takesDefault( - @TestParameter({"MODES_UI", "MODES_API"}) ModesFlag modesFlag) { - modesFlag.applyFlags(mSetFlagsRule); + public void testUpdateConsolidatedPolicy_modesApiDefaultRulesOnly_takesDefault() { setupZenConfig(); // When there's one automatic rule active and it doesn't specify a policy, test that the @@ -3876,13 +3858,13 @@ public class ZenModeHelperTest extends UiServiceTestCase { // inspect the consolidated policy, which should match the device default settings. assertThat(ZenAdapters.notificationPolicyToZenPolicy(mZenModeHelper.mConsolidatedPolicy)) - .isEqualTo(modesFlag == ModesFlag.MODES_UI + .isEqualTo(Flags.modesUi() ? mZenModeHelper.getDefaultZenPolicy() - : mZenModeHelper.mConfig.toZenPolicy()); + : mZenModeHelper.mConfig.getZenPolicy()); } @Test - @DisableFlags(Flags.FLAG_MODES_API) + @DisableFlags(FLAG_MODES_API) public void testUpdateConsolidatedPolicy_preModesApiCustomPolicyOnly_fillInWithGlobal() { setupZenConfig(); @@ -3928,9 +3910,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - public void testUpdateConsolidatedPolicy_modesApiCustomPolicyOnly_fillInWithDefault( - @TestParameter({"MODES_UI", "MODES_API"}) ModesFlag modesFlag) { - modesFlag.applyFlags(mSetFlagsRule); + @EnableFlags(FLAG_MODES_API) + public void testUpdateConsolidatedPolicy_modesApiCustomPolicyOnly_fillInWithDefault() { setupZenConfig(); // when there's only one automatic rule active and it has a custom policy, make sure that's @@ -3962,12 +3943,12 @@ public class ZenModeHelperTest extends UiServiceTestCase { // policy for every field specified, and take default values (from either device default // policy or manual rule) for unspecified things assertThat(mZenModeHelper.mConsolidatedPolicy.allowAlarms()).isEqualTo( - modesFlag == ModesFlag.MODES_UI ? true : false); // default + Flags.modesUi() ? true : false); // default assertThat(mZenModeHelper.mConsolidatedPolicy.allowMedia()).isEqualTo( - modesFlag == ModesFlag.MODES_UI ? true : false); // default + Flags.modesUi() ? true : false); // default assertThat(mZenModeHelper.mConsolidatedPolicy.allowSystem()).isTrue(); // custom assertThat(mZenModeHelper.mConsolidatedPolicy.allowReminders()).isEqualTo( - modesFlag == ModesFlag.MODES_UI ? false : true); // default + Flags.modesUi() ? false : true); // default assertThat(mZenModeHelper.mConsolidatedPolicy.allowCalls()).isFalse(); // custom assertThat(mZenModeHelper.mConsolidatedPolicy.allowMessages()).isTrue(); // default assertThat(mZenModeHelper.mConsolidatedPolicy.allowRepeatCallers()).isFalse(); // custom @@ -3976,7 +3957,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - @DisableFlags(Flags.FLAG_MODES_API) + @DisableFlags(FLAG_MODES_API) public void testUpdateConsolidatedPolicy_preModesApiDefaultAndCustomActive_mergesWithGlobal() { setupZenConfig(); @@ -4037,9 +4018,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - public void testUpdateConsolidatedPolicy_modesApiDefaultAndCustomActive_mergesWithDefault( - @TestParameter({"MODES_UI", "MODES_API"}) ModesFlag modesFlag) { - modesFlag.applyFlags(mSetFlagsRule); + @EnableFlags(FLAG_MODES_API) + public void testUpdateConsolidatedPolicy_modesApiDefaultAndCustomActive_mergesWithDefault() { setupZenConfig(); // when there are two rules active, one inheriting the default policy and one setting its @@ -4088,10 +4068,10 @@ public class ZenModeHelperTest extends UiServiceTestCase { // restrictive option of each of the two assertThat(mZenModeHelper.mConsolidatedPolicy.allowAlarms()).isFalse(); // custom stricter assertThat(mZenModeHelper.mConsolidatedPolicy.allowMedia()).isEqualTo( - modesFlag == ModesFlag.MODES_UI ? true : false); // default + Flags.modesUi() ? true : false); // default assertThat(mZenModeHelper.mConsolidatedPolicy.allowSystem()).isFalse(); // default stricter assertThat(mZenModeHelper.mConsolidatedPolicy.allowReminders()).isEqualTo( - modesFlag == ModesFlag.MODES_UI ? false : true); // default + Flags.modesUi() ? false : true); // default assertThat(mZenModeHelper.mConsolidatedPolicy.allowCalls()).isFalse(); // custom stricter assertThat(mZenModeHelper.mConsolidatedPolicy.allowMessages()).isTrue(); // default assertThat(mZenModeHelper.mConsolidatedPolicy.allowConversations()).isTrue(); // default @@ -4099,12 +4079,12 @@ public class ZenModeHelperTest extends UiServiceTestCase { .isFalse(); // custom stricter assertThat(mZenModeHelper.mConsolidatedPolicy.showBadges()).isFalse(); // custom stricter assertThat(mZenModeHelper.mConsolidatedPolicy.showPeeking()).isEqualTo( - modesFlag == ModesFlag.MODES_UI ? false : true); // default + Flags.modesUi() ? false : true); // default } @Test + @EnableFlags(FLAG_MODES_API) public void testUpdateConsolidatedPolicy_allowChannels() { - mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API); setupZenConfig(); // one rule, custom policy, allows channels @@ -4153,9 +4133,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - public void testUpdateConsolidatedPolicy_ignoresActiveRulesWithInterruptionFilterAll( - @TestParameter({"MODES_UI", "MODES_API"}) ModesFlag modesFlag) { - modesFlag.applyFlags(mSetFlagsRule); + @EnableFlags(FLAG_MODES_API) + public void testUpdateConsolidatedPolicy_ignoresActiveRulesWithInterruptionFilterAll() { setupZenConfig(); // Rules with INTERRUPTION_FILTER_ALL are skipped when calculating consolidated policy. @@ -4193,11 +4172,11 @@ public class ZenModeHelperTest extends UiServiceTestCase { // Consolidated Policy should be default + rule1. assertThat(mZenModeHelper.mConsolidatedPolicy.allowAlarms()).isEqualTo( - modesFlag == ModesFlag.MODES_UI ? true : false); // default + Flags.modesUi() ? true : false); // default assertThat(mZenModeHelper.mConsolidatedPolicy.allowMedia()).isTrue(); // priority rule assertThat(mZenModeHelper.mConsolidatedPolicy.allowSystem()).isTrue(); // priority rule assertThat(mZenModeHelper.mConsolidatedPolicy.allowReminders()).isEqualTo( - modesFlag == ModesFlag.MODES_UI ? false : true); // default + Flags.modesUi() ? false : true); // default assertThat(mZenModeHelper.mConsolidatedPolicy.allowCalls()).isTrue(); // default assertThat(mZenModeHelper.mConsolidatedPolicy.allowMessages()).isTrue(); // default assertThat(mZenModeHelper.mConsolidatedPolicy.allowConversations()).isTrue(); // default @@ -4205,8 +4184,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test + @EnableFlags(FLAG_MODES_API) public void zenRuleToAutomaticZenRule_allFields() { - mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API); when(mPackageManager.getPackagesForUid(anyInt())).thenReturn( new String[]{OWNER.getPackageName()}); @@ -4249,8 +4228,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test + @EnableFlags(FLAG_MODES_API) public void automaticZenRuleToZenRule_allFields() { - mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API); when(mPackageManager.getPackagesForUid(anyInt())).thenReturn( new String[]{OWNER.getPackageName()}); @@ -4291,7 +4270,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - @EnableFlags(Flags.FLAG_MODES_API) + @EnableFlags(FLAG_MODES_API) public void updateAutomaticZenRule_fromApp_updatesNameUnlessUserModified() { // Add a starting rule with the name OriginalName. AutomaticZenRule azrBase = new AutomaticZenRule.Builder("OriginalName", CONDITION_ID) @@ -4348,7 +4327,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - @EnableFlags(Flags.FLAG_MODES_API) + @EnableFlags(FLAG_MODES_API) public void updateAutomaticZenRule_fromUser_updatesBitmaskAndValue() { // Adds a starting rule with empty zen policies and device effects AutomaticZenRule azrBase = new AutomaticZenRule.Builder(NAME, CONDITION_ID) @@ -4385,7 +4364,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { assertThat(rule.getInterruptionFilter()).isEqualTo(INTERRUPTION_FILTER_PRIORITY); assertThat(rule.getIconResId()).isEqualTo(ICON_RES_ID); assertThat(rule.getZenPolicy().getPriorityChannelsAllowed()).isEqualTo( - ZenPolicy.STATE_DISALLOW); + STATE_DISALLOW); assertThat(rule.getDeviceEffects().shouldDisplayGrayscale()).isTrue(); @@ -4401,7 +4380,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - @EnableFlags(Flags.FLAG_MODES_API) + @EnableFlags(FLAG_MODES_API) public void updateAutomaticZenRule_fromSystemUi_updatesValues() { // Adds a starting rule with empty zen policies and device effects AutomaticZenRule azrBase = new AutomaticZenRule.Builder(NAME, CONDITION_ID) @@ -4441,7 +4420,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { // UPDATE_ORIGIN_SYSTEM_OR_SYSTEMUI should change the value but NOT update the bitmask. assertThat(rule.getIconResId()).isEqualTo(ICON_RES_ID); assertThat(rule.getZenPolicy().getPriorityCategoryReminders()) - .isEqualTo(ZenPolicy.STATE_ALLOW); + .isEqualTo(STATE_ALLOW); assertThat(rule.getDeviceEffects().shouldDisplayGrayscale()).isTrue(); ZenRule storedRule = mZenModeHelper.mConfig.automaticRules.get(ruleId); @@ -4451,7 +4430,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - @EnableFlags(Flags.FLAG_MODES_API) + @EnableFlags(FLAG_MODES_API) public void updateAutomaticZenRule_fromApp_updatesValuesIfRuleNotUserModified() { // Adds a starting rule with empty zen policies and device effects AutomaticZenRule azrBase = new AutomaticZenRule.Builder(NAME, CONDITION_ID) @@ -4490,7 +4469,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { assertThat(storedRule.zenMode).isEqualTo(ZEN_MODE_ALARMS); assertThat(storedRule.zenPolicy.getPriorityCategoryReminders()) - .isEqualTo(ZenPolicy.STATE_ALLOW); + .isEqualTo(STATE_ALLOW); assertThat(storedRule.zenDeviceEffects.shouldDisplayGrayscale()).isTrue(); assertThat(storedRule.userModifiedFields).isEqualTo(0); assertThat(storedRule.zenPolicyUserModifiedFields).isEqualTo(0); @@ -4514,7 +4493,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { // so the rule is not changed, and neither is the bitmask. assertThat(ruleUser.getInterruptionFilter()).isEqualTo(INTERRUPTION_FILTER_ALL); assertThat(ruleUser.getZenPolicy().getPriorityCategoryReminders()) - .isEqualTo(ZenPolicy.STATE_DISALLOW); + .isEqualTo(STATE_DISALLOW); assertThat(ruleUser.getDeviceEffects().shouldDisplayGrayscale()).isFalse(); storedRule = mZenModeHelper.mConfig.automaticRules.get(ruleIdUser); @@ -4525,7 +4504,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - @EnableFlags(Flags.FLAG_MODES_API) + @EnableFlags(FLAG_MODES_API) public void addAutomaticZenRule_updatesValues() { // Adds a starting rule with empty zen policies and device effects AutomaticZenRule azrBase = new AutomaticZenRule.Builder(NAME, CONDITION_ID) @@ -4543,7 +4522,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { // The values are modified but the bitmask is not. assertThat(rule.getZenPolicy().getPriorityCategoryReminders()) - .isEqualTo(ZenPolicy.STATE_ALLOW); + .isEqualTo(STATE_ALLOW); assertThat(rule.getDeviceEffects().shouldDisplayGrayscale()).isTrue(); ZenRule storedRule = mZenModeHelper.mConfig.automaticRules.get(ruleId); @@ -4551,7 +4530,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - @EnableFlags(Flags.FLAG_MODES_API) + @EnableFlags(FLAG_MODES_API) public void updateAutomaticZenRule_nullDeviceEffectsUpdate() { // Adds a starting rule with empty zen policies and device effects ZenDeviceEffects zde = new ZenDeviceEffects.Builder().setShouldUseNightMode(true).build(); @@ -4578,7 +4557,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - @EnableFlags(Flags.FLAG_MODES_API) + @EnableFlags(FLAG_MODES_API) public void updateAutomaticZenRule_nullPolicyUpdate() { // Adds a starting rule with set zen policy and empty device effects AutomaticZenRule azrBase = new AutomaticZenRule.Builder(NAME, CONDITION_ID) @@ -4607,7 +4586,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - @EnableFlags(Flags.FLAG_MODES_API) + @EnableFlags(FLAG_MODES_API) public void automaticZenRuleToZenRule_nullToNonNullPolicyUpdate() { when(mContext.checkCallingPermission(anyString())) .thenReturn(PackageManager.PERMISSION_GRANTED); @@ -4654,7 +4633,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { // New ZenPolicy differs from the default config assertThat(rule.getZenPolicy()).isNotNull(); assertThat(rule.getZenPolicy().getPriorityChannelsAllowed()).isEqualTo( - ZenPolicy.STATE_DISALLOW); + STATE_DISALLOW); ZenRule storedRule = mZenModeHelper.mConfig.automaticRules.get(ruleId); assertThat(storedRule.canBeUpdatedByApp()).isFalse(); @@ -4671,7 +4650,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - @EnableFlags(Flags.FLAG_MODES_API) + @EnableFlags(FLAG_MODES_API) public void automaticZenRuleToZenRule_nullToNonNullDeviceEffectsUpdate() { // Adds a starting rule with empty zen policies and device effects AutomaticZenRule azrBase = new AutomaticZenRule.Builder(NAME, CONDITION_ID) @@ -4775,8 +4754,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test + @EnableFlags(FLAG_MODES_API) public void testUpdateAutomaticRule_activated_triggersBroadcast() throws Exception { - mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API); setupZenConfig(); // Add a new automatic zen rule that's enabled @@ -4815,8 +4794,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test + @EnableFlags(FLAG_MODES_API) public void testUpdateAutomaticRule_deactivatedByUser_triggersBroadcast() throws Exception { - mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API); setupZenConfig(); // Add a new automatic zen rule that's enabled @@ -4860,8 +4839,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test + @EnableFlags(FLAG_MODES_API) public void testUpdateAutomaticRule_deactivatedByApp_triggersBroadcast() throws Exception { - mSetFlagsRule.enableFlags(Flags.FLAG_MODES_API); setupZenConfig(); // Add a new automatic zen rule that's enabled @@ -4937,7 +4916,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - @EnableFlags(Flags.FLAG_MODES_API) + @EnableFlags(FLAG_MODES_API) public void updateAutomaticZenRule_ruleChanged_deactivatesRule() { assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_OFF); AutomaticZenRule rule = new AutomaticZenRule.Builder("rule", CONDITION_ID) @@ -4961,7 +4940,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - @EnableFlags(Flags.FLAG_MODES_API) + @EnableFlags(FLAG_MODES_API) public void updateAutomaticZenRule_ruleNotChanged_doesNotDeactivateRule() { assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_OFF); AutomaticZenRule rule = new AutomaticZenRule.Builder("rule", CONDITION_ID) @@ -4984,7 +4963,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - @EnableFlags({Flags.FLAG_MODES_API, Flags.FLAG_MODES_UI}) + @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI}) public void updateAutomaticZenRule_ruleChangedByUser_doesNotDeactivateRule() { assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_OFF); AutomaticZenRule rule = new AutomaticZenRule.Builder("rule", CONDITION_ID) @@ -5009,7 +4988,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - @EnableFlags({Flags.FLAG_MODES_API, Flags.FLAG_MODES_UI}) + @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI}) public void updateAutomaticZenRule_ruleDisabledByUser_doesNotReactivateOnReenable() { assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_OFF); AutomaticZenRule rule = new AutomaticZenRule.Builder("rule", CONDITION_ID) @@ -5034,7 +5013,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - @EnableFlags(Flags.FLAG_MODES_API) + @EnableFlags(FLAG_MODES_API) public void removeAutomaticZenRule_propagatesOriginToEffectsApplier() { mZenModeHelper.setDeviceEffectsApplier(mDeviceEffectsApplier); reset(mDeviceEffectsApplier); @@ -5057,8 +5036,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test + @EnableFlags(FLAG_MODES_API) public void testDeviceEffects_applied() { - mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API); mZenModeHelper.setDeviceEffectsApplier(mDeviceEffectsApplier); verify(mDeviceEffectsApplier).apply(eq(NO_EFFECTS), eq(UPDATE_ORIGIN_INIT)); @@ -5077,8 +5056,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test + @EnableFlags(FLAG_MODES_API) public void testDeviceEffects_onDeactivateRule_applied() { - mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API); mZenModeHelper.setDeviceEffectsApplier(mDeviceEffectsApplier); ZenDeviceEffects zde = new ZenDeviceEffects.Builder().setShouldUseNightMode(true).build(); @@ -5096,8 +5075,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test + @EnableFlags(FLAG_MODES_API) public void testDeviceEffects_changeToConsolidatedEffects_applied() { - mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API); mZenModeHelper.setDeviceEffectsApplier(mDeviceEffectsApplier); verify(mDeviceEffectsApplier).apply(eq(NO_EFFECTS), eq(UPDATE_ORIGIN_INIT)); @@ -5136,8 +5115,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test + @EnableFlags(FLAG_MODES_API) public void testDeviceEffects_noChangeToConsolidatedEffects_notApplied() { - mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API); mZenModeHelper.setDeviceEffectsApplier(mDeviceEffectsApplier); verify(mDeviceEffectsApplier).apply(eq(NO_EFFECTS), eq(UPDATE_ORIGIN_INIT)); @@ -5161,9 +5140,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test + @EnableFlags(FLAG_MODES_API) public void testDeviceEffects_activeBeforeApplierProvided_appliedWhenProvided() { - mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API); - ZenDeviceEffects zde = new ZenDeviceEffects.Builder().setShouldUseNightMode(true).build(); String ruleId = addRuleWithEffects(zde); verify(mDeviceEffectsApplier, never()).apply(any(), anyInt()); @@ -5178,8 +5156,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test + @EnableFlags(FLAG_MODES_API) public void testDeviceEffects_onUserSwitch_appliedImmediately() { - mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API); mZenModeHelper.setDeviceEffectsApplier(mDeviceEffectsApplier); verify(mDeviceEffectsApplier).apply(eq(NO_EFFECTS), eq(UPDATE_ORIGIN_INIT)); @@ -5214,9 +5192,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test + @EnableFlags(FLAG_MODES_API) public void removeAndAddAutomaticZenRule_wasCustomized_isRestored() { - mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API); - // Start with a rule. mZenModeHelper.mConfig.automaticRules.clear(); mTestClock.setNowMillis(1000); @@ -5257,7 +5234,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { assertThat(newRuleId).isEqualTo(ruleId); assertThat(finalRule.getInterruptionFilter()).isEqualTo(INTERRUPTION_FILTER_ALARMS); assertThat(finalRule.getZenPolicy().getPriorityCategoryRepeatCallers()).isEqualTo( - ZenPolicy.STATE_ALLOW); + STATE_ALLOW); ZenRule storedRule = mZenModeHelper.mConfig.automaticRules.get(ruleId); assertThat(storedRule.userModifiedFields).isEqualTo( @@ -5270,9 +5247,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test + @EnableFlags(FLAG_MODES_API) public void removeAndAddAutomaticZenRule_wasNotCustomized_isNotRestored() { - mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API); - // Start with a single rule. mZenModeHelper.mConfig.automaticRules.clear(); mTestClock.setNowMillis(1000); @@ -5303,9 +5279,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test + @EnableFlags(FLAG_MODES_API) public void removeAndAddAutomaticZenRule_recreatedButNotByApp_isNotRestored() { - mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API); - // Start with a single rule. mZenModeHelper.mConfig.automaticRules.clear(); mTestClock.setNowMillis(1000); @@ -5345,16 +5320,15 @@ public class ZenModeHelperTest extends UiServiceTestCase { assertThat(newRuleId).isNotEqualTo(ruleId); assertThat(finalRule.getInterruptionFilter()).isEqualTo(INTERRUPTION_FILTER_PRIORITY); assertThat(finalRule.getZenPolicy().getPriorityCategoryRepeatCallers()).isEqualTo( - ZenPolicy.STATE_DISALLOW); + STATE_DISALLOW); // Also, we discarded the "deleted rule" since we're not interested in recreating it. assertThat(mZenModeHelper.mConfig.deletedRules).hasSize(0); } @Test + @EnableFlags(FLAG_MODES_API) public void removeAndAddAutomaticZenRule_removedByUser_isNotRestored() { - mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API); - // Start with a single rule. mZenModeHelper.mConfig.automaticRules.clear(); mTestClock.setNowMillis(1000); @@ -5394,8 +5368,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test + @EnableFlags(FLAG_MODES_API) public void removeAutomaticZenRule_preservedForRestoringByPackageAndConditionId() { - mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API); mContext.getTestablePermissions().setPermission(Manifest.permission.MANAGE_NOTIFICATIONS, PERMISSION_GRANTED); // So that canManageAZR passes although packages don't match. mZenModeHelper.mConfig.automaticRules.clear(); @@ -5434,8 +5408,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test + @EnableFlags(FLAG_MODES_API) public void removeAllZenRules_preservedForRestoring() { - mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API); mZenModeHelper.mConfig.automaticRules.clear(); mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), @@ -5456,8 +5430,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test + @EnableFlags(FLAG_MODES_API) public void removeAllZenRules_fromSystem_deletesPreservedRulesToo() { - mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API); mZenModeHelper.mConfig.automaticRules.clear(); // Start with deleted rules from 2 different packages. @@ -5476,7 +5450,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - @EnableFlags(Flags.FLAG_MODES_API) + @EnableFlags(FLAG_MODES_API) public void removeAndAddAutomaticZenRule_wasActive_isRestoredAsInactive() { // Start with a rule. mZenModeHelper.mConfig.automaticRules.clear(); @@ -5525,7 +5499,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - @EnableFlags(Flags.FLAG_MODES_API) + @EnableFlags(FLAG_MODES_API) public void removeAndAddAutomaticZenRule_wasSnoozed_isRestoredAsInactive() { // Start with a rule. mZenModeHelper.mConfig.automaticRules.clear(); @@ -5579,8 +5553,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test + @EnableFlags(FLAG_MODES_API) public void testRuleCleanup() throws Exception { - mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API); Instant now = Instant.ofEpochMilli(1701796461000L); Instant yesterday = now.minus(1, ChronoUnit.DAYS); Instant aWeekAgo = now.minus(7, ChronoUnit.DAYS); @@ -5637,7 +5611,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - @EnableFlags(Flags.FLAG_MODES_API) + @EnableFlags(FLAG_MODES_API) public void getAutomaticZenRuleState_ownedRule_returnsRuleState() { String id = mZenModeHelper.addAutomaticZenRule(mContext.getPackageName(), new AutomaticZenRule.Builder("Rule", CONDITION_ID) @@ -5662,7 +5636,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - @EnableFlags(Flags.FLAG_MODES_API) + @EnableFlags(FLAG_MODES_API) public void getAutomaticZenRuleState_notOwnedRule_returnsStateUnknown() { // Assume existence of a system-owned rule that is currently ACTIVE. ZenRule systemRule = newZenRule("android", Instant.now(), null); @@ -5678,7 +5652,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - @EnableFlags(Flags.FLAG_MODES_API) + @EnableFlags(FLAG_MODES_API) public void setAutomaticZenRuleState_idForNotOwnedRule_ignored() { // Assume existence of an other-package-owned rule that is currently ACTIVE. assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_OFF); @@ -5699,7 +5673,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - @EnableFlags(Flags.FLAG_MODES_API) + @EnableFlags(FLAG_MODES_API) public void setAutomaticZenRuleState_conditionForNotOwnedRule_ignored() { // Assume existence of an other-package-owned rule that is currently ACTIVE. assertThat(mZenModeHelper.getZenMode()).isEqualTo(ZEN_MODE_OFF); @@ -5720,7 +5694,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - @EnableFlags(android.app.Flags.FLAG_MODES_API) + @EnableFlags(FLAG_MODES_API) public void testCallbacks_policy() throws Exception { setupZenConfig(); assertThat(mZenModeHelper.getNotificationPolicy().allowReminders()).isTrue(); @@ -5740,10 +5714,9 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - @EnableFlags(android.app.Flags.FLAG_MODES_API) + @EnableFlags(FLAG_MODES_API) public void testCallbacks_consolidatedPolicy() throws Exception { - setupZenConfig(); - assertThat(mZenModeHelper.getConsolidatedNotificationPolicy().allowAlarms()).isTrue(); + assertThat(mZenModeHelper.getConsolidatedNotificationPolicy().allowMedia()).isTrue(); SettableFuture<Policy> futureConsolidatedPolicy = SettableFuture.create(); mZenModeHelper.addCallback(new ZenModeHelper.Callback() { @Override @@ -5762,12 +5735,12 @@ public class ZenModeHelperTest extends UiServiceTestCase { new Condition(CONDITION_ID, "", STATE_TRUE), UPDATE_ORIGIN_APP, CUSTOM_PKG_UID); Policy callbackPolicy = futureConsolidatedPolicy.get(1, TimeUnit.SECONDS); - assertThat(callbackPolicy.allowAlarms()).isFalse(); + assertThat(callbackPolicy.allowMedia()).isFalse(); } @Test + @EnableFlags(FLAG_MODES_API) public void applyGlobalZenModeAsImplicitZenRule_createsImplicitRuleAndActivatesIt() { - mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API); mZenModeHelper.mConfig.automaticRules.clear(); mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID, @@ -5777,13 +5750,13 @@ public class ZenModeHelperTest extends UiServiceTestCase { .comparingElementsUsing(IGNORE_METADATA) .containsExactly( expectedImplicitRule(CUSTOM_PKG_NAME, ZEN_MODE_IMPORTANT_INTERRUPTIONS, - mZenModeHelper.mConfig.toZenPolicy(), // copy of global config + mZenModeHelper.mConfig.getZenPolicy(), // copy of global config true)); } @Test + @EnableFlags(FLAG_MODES_API) public void applyGlobalZenModeAsImplicitZenRule_updatesImplicitRuleAndActivatesIt() { - mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API); mZenModeHelper.mConfig.automaticRules.clear(); mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID, @@ -5798,12 +5771,12 @@ public class ZenModeHelperTest extends UiServiceTestCase { .comparingElementsUsing(IGNORE_METADATA) .containsExactly( expectedImplicitRule(CUSTOM_PKG_NAME, ZEN_MODE_ALARMS, - mZenModeHelper.mConfig.toZenPolicy(), // copy of global config + mZenModeHelper.mConfig.getZenPolicy(), // copy of global config true)); } @Test - @EnableFlags(android.app.Flags.FLAG_MODES_API) + @EnableFlags(FLAG_MODES_API) public void applyGlobalZenModeAsImplicitZenRule_ruleCustomized_doesNotUpdateRule() { mZenModeHelper.mConfig.automaticRules.clear(); String pkg = mContext.getPackageName(); @@ -5835,7 +5808,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - @EnableFlags(android.app.Flags.FLAG_MODES_API) + @EnableFlags(FLAG_MODES_API) public void applyGlobalZenModeAsImplicitZenRule_ruleCustomizedButNotFilter_updatesRule() { mZenModeHelper.mConfig.automaticRules.clear(); String pkg = mContext.getPackageName(); @@ -5866,8 +5839,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test + @EnableFlags(FLAG_MODES_API) public void applyGlobalZenModeAsImplicitZenRule_modeOff_deactivatesImplicitRule() { - mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API); mZenModeHelper.mConfig.automaticRules.clear(); mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(mPkg, CUSTOM_PKG_UID, ZEN_MODE_IMPORTANT_INTERRUPTIONS); @@ -5883,8 +5856,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test + @EnableFlags(FLAG_MODES_API) public void applyGlobalZenModeAsImplicitZenRule_modeOffButNoPreviousRule_ignored() { - mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API); mZenModeHelper.mConfig.automaticRules.clear(); mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID, @@ -5894,8 +5867,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test + @EnableFlags(FLAG_MODES_API) public void applyGlobalZenModeAsImplicitZenRule_update_unsnoozesRule() { - mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API); mZenModeHelper.mConfig.automaticRules.clear(); mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID, @@ -5915,8 +5888,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test + @DisableFlags(FLAG_MODES_API) public void applyGlobalZenModeAsImplicitZenRule_flagOff_ignored() { - mSetFlagsRule.disableFlags(android.app.Flags.FLAG_MODES_API); mZenModeHelper.mConfig.automaticRules.clear(); withoutWtfCrash( @@ -5928,8 +5901,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test + @EnableFlags(FLAG_MODES_API) public void applyGlobalPolicyAsImplicitZenRule_createsImplicitRule() { - mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API); mZenModeHelper.mConfig.automaticRules.clear(); Policy policy = new Policy(PRIORITY_CATEGORY_CALLS | PRIORITY_CATEGORY_CONVERSATIONS, @@ -5952,8 +5925,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test + @EnableFlags(FLAG_MODES_API) public void applyGlobalPolicyAsImplicitZenRule_updatesImplicitRule() { - mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API); mZenModeHelper.mConfig.automaticRules.clear(); Policy original = new Policy(PRIORITY_CATEGORY_CALLS | PRIORITY_CATEGORY_CONVERSATIONS, @@ -5983,7 +5956,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - @EnableFlags(android.app.Flags.FLAG_MODES_API) + @EnableFlags(FLAG_MODES_API) public void applyGlobalPolicyAsImplicitZenRule_ruleCustomized_doesNotUpdateRule() { mZenModeHelper.mConfig.automaticRules.clear(); String pkg = mContext.getPackageName(); @@ -5995,7 +5968,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { // Store this for checking later. ZenPolicy originalEffectiveZenPolicy = new ZenPolicy.Builder( - mZenModeHelper.mConfig.toZenPolicy()).allowMedia(true).build(); + mZenModeHelper.mConfig.getZenPolicy()).allowMedia(true).build(); // From user, update that rule's policy. AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(ruleId); @@ -6023,7 +5996,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - @EnableFlags(android.app.Flags.FLAG_MODES_API) + @EnableFlags(FLAG_MODES_API) public void applyGlobalPolicyAsImplicitZenRule_ruleCustomizedButNotZenPolicy_updatesRule() { mZenModeHelper.mConfig.automaticRules.clear(); String pkg = mContext.getPackageName(); @@ -6035,7 +6008,7 @@ public class ZenModeHelperTest extends UiServiceTestCase { // Store this for checking later. ZenPolicy originalEffectiveZenPolicy = new ZenPolicy.Builder( - mZenModeHelper.mConfig.toZenPolicy()).allowMedia(true).build(); + mZenModeHelper.mConfig.getZenPolicy()).allowMedia(true).build(); // From user, update something in that rule, but not the ZenPolicy. AutomaticZenRule rule = mZenModeHelper.getAutomaticZenRule(ruleId); @@ -6060,8 +6033,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test + @DisableFlags(FLAG_MODES_API) public void applyGlobalPolicyAsImplicitZenRule_flagOff_ignored() { - mSetFlagsRule.disableFlags(android.app.Flags.FLAG_MODES_API); mZenModeHelper.mConfig.automaticRules.clear(); withoutWtfCrash( @@ -6072,8 +6045,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test + @EnableFlags(FLAG_MODES_API) public void getNotificationPolicyFromImplicitZenRule_returnsSetPolicy() { - mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API); Policy writtenPolicy = new Policy(PRIORITY_CATEGORY_CALLS | PRIORITY_CATEGORY_CONVERSATIONS, PRIORITY_SENDERS_CONTACTS, PRIORITY_SENDERS_STARRED, Policy.getAllSuppressedVisualEffects(), STATE_FALSE, @@ -6088,32 +6061,30 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test + @EnableFlags(FLAG_MODES_API) + @DisableFlags(FLAG_MODES_UI) public void getNotificationPolicyFromImplicitZenRule_ruleWithoutPolicy_copiesGlobalPolicy() { - mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API); - mZenModeHelper.mConfig.allowCalls = true; - mZenModeHelper.mConfig.allowConversations = false; - // Implicit rule will get the global policy at the time of rule creation. mZenModeHelper.applyGlobalZenModeAsImplicitZenRule(CUSTOM_PKG_NAME, CUSTOM_PKG_UID, - ZEN_MODE_ALARMS); - - // If the policy then changes afterwards, we should keep the snapshotted version. - mZenModeHelper.mConfig.allowCalls = false; + ZEN_MODE_IMPORTANT_INTERRUPTIONS); + // If the policy then changes afterwards, it should inherit updates because user cannot + // edit the policy in the UI. + mZenModeHelper.setNotificationPolicy(new Policy(PRIORITY_CATEGORY_ALARMS, 0, 0), + UPDATE_ORIGIN_APP, 1); Policy readPolicy = mZenModeHelper.getNotificationPolicyFromImplicitZenRule( CUSTOM_PKG_NAME); assertThat(readPolicy).isNotNull(); - assertThat(readPolicy.allowCalls()).isTrue(); - assertThat(readPolicy.allowConversations()).isFalse(); + assertThat(readPolicy.allowCalls()).isFalse(); + assertThat(readPolicy.allowAlarms()).isTrue(); } @Test + @EnableFlags(FLAG_MODES_API) public void getNotificationPolicyFromImplicitZenRule_noImplicitRule_returnsGlobalPolicy() { - mSetFlagsRule.enableFlags(android.app.Flags.FLAG_MODES_API); - - mZenModeHelper.mConfig.allowCalls = true; - mZenModeHelper.mConfig.allowConversations = false; + Policy policy = new Policy(PRIORITY_CATEGORY_CALLS, PRIORITY_SENDERS_STARRED, 0); + mZenModeHelper.setNotificationPolicy(policy, UPDATE_ORIGIN_USER, 1); Policy readPolicy = mZenModeHelper.getNotificationPolicyFromImplicitZenRule( CUSTOM_PKG_NAME); @@ -6124,8 +6095,8 @@ public class ZenModeHelperTest extends UiServiceTestCase { } @Test - @EnableFlags(Flags.FLAG_MODES_API) - @DisableFlags(Flags.FLAG_MODES_UI) + @EnableFlags(FLAG_MODES_API) + @DisableFlags(FLAG_MODES_UI) public void setNotificationPolicy_updatesRulePolicies_ifRulePolicyIsDefaultOrGlobalPolicy() { ZenPolicy defaultZenPolicy = mZenModeHelper.getDefaultZenPolicy(); Policy previousManualPolicy = mZenModeHelper.mConfig.toNotificationPolicy(); @@ -6185,6 +6156,35 @@ public class ZenModeHelperTest extends UiServiceTestCase { assertThat(storedRule.getIconResId()).isEqualTo(0); } + @Test + @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI}) + public void setManualZenRuleDeviceEffects_noPreexistingMode() { + ZenDeviceEffects effects = new ZenDeviceEffects.Builder() + .setShouldDimWallpaper(true) + .build(); + mZenModeHelper.setManualZenRuleDeviceEffects(effects, UPDATE_ORIGIN_USER, "settings", 1000); + + assertThat(mZenModeHelper.getConfig().manualRule).isNotNull(); + assertThat(mZenModeHelper.getConfig().isManualActive()).isFalse(); + assertThat(mZenModeHelper.getConfig().manualRule.zenDeviceEffects).isEqualTo(effects); + } + + @Test + @EnableFlags({FLAG_MODES_API, FLAG_MODES_UI}) + public void setManualZenRuleDeviceEffects_preexistingMode() { + mZenModeHelper.setManualZenMode(ZEN_MODE_OFF, Uri.EMPTY, UPDATE_ORIGIN_USER, + "create manual rule", "settings", 1000); + + ZenDeviceEffects effects = new ZenDeviceEffects.Builder() + .setShouldDimWallpaper(true) + .build(); + mZenModeHelper.setManualZenRuleDeviceEffects(effects, UPDATE_ORIGIN_USER, "settings", 1000); + + assertThat(mZenModeHelper.getConfig().manualRule).isNotNull(); + assertThat(mZenModeHelper.getConfig().isManualActive()).isFalse(); + assertThat(mZenModeHelper.getConfig().manualRule.zenDeviceEffects).isEqualTo(effects); + } + private static void addZenRule(ZenModeConfig config, String id, String ownerPkg, int zenMode, @Nullable ZenPolicy zenPolicy) { ZenRule rule = new ZenRule(); @@ -6246,34 +6246,19 @@ public class ZenModeHelperTest extends UiServiceTestCase { // TODO: b/310620812 - Update setup methods to include allowChannels() when MODES_API is inlined private void setupZenConfig() { - mZenModeHelper.mZenMode = ZEN_MODE_OFF; - mZenModeHelper.mConfig.allowAlarms = false; - mZenModeHelper.mConfig.allowMedia = false; - mZenModeHelper.mConfig.allowSystem = false; - mZenModeHelper.mConfig.allowReminders = true; - mZenModeHelper.mConfig.allowCalls = true; - mZenModeHelper.mConfig.allowCallsFrom = PRIORITY_SENDERS_STARRED; - mZenModeHelper.mConfig.allowMessages = true; - mZenModeHelper.mConfig.allowConversations = true; - mZenModeHelper.mConfig.allowEvents = true; - mZenModeHelper.mConfig.allowRepeatCallers = true; - mZenModeHelper.mConfig.suppressedVisualEffects = SUPPRESSED_EFFECT_BADGE; - mZenModeHelper.mConfig.manualRule = null; - } - - private void setupZenConfigMaintained() { - // config is still the same as when it was setup (setupZenConfig) - assertFalse(mZenModeHelper.mConfig.allowAlarms); - assertFalse(mZenModeHelper.mConfig.allowMedia); - assertFalse(mZenModeHelper.mConfig.allowSystem); - assertTrue(mZenModeHelper.mConfig.allowReminders); - assertTrue(mZenModeHelper.mConfig.allowCalls); - assertEquals(PRIORITY_SENDERS_STARRED, mZenModeHelper.mConfig.allowCallsFrom); - assertTrue(mZenModeHelper.mConfig.allowMessages); - assertTrue(mZenModeHelper.mConfig.allowConversations); - assertTrue(mZenModeHelper.mConfig.allowEvents); - assertTrue(mZenModeHelper.mConfig.allowRepeatCallers); - assertEquals(SUPPRESSED_EFFECT_BADGE, mZenModeHelper.mConfig.suppressedVisualEffects); + Policy customPolicy = new Policy(PRIORITY_CATEGORY_REMINDERS + | PRIORITY_CATEGORY_CALLS | PRIORITY_CATEGORY_MESSAGES + | PRIORITY_CATEGORY_EVENTS | PRIORITY_CATEGORY_REPEAT_CALLERS + | PRIORITY_CATEGORY_CONVERSATIONS, + PRIORITY_SENDERS_STARRED, + PRIORITY_SENDERS_STARRED, + SUPPRESSED_EFFECT_BADGE, + 0, + CONVERSATION_SENDERS_IMPORTANT); + mZenModeHelper.setNotificationPolicy(customPolicy, UPDATE_ORIGIN_UNKNOWN, 1); + if (!Flags.modesUi()) { + mZenModeHelper.mConfig.manualRule = null; + } } private void checkDndProtoMatchesSetupZenConfig(DNDPolicyProto dndProto) { diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java index e8bac66a724a..175a09db54e3 100644 --- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java +++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java @@ -80,6 +80,7 @@ import android.service.usb.UsbDeviceManagerProto; import android.service.usb.UsbHandlerProto; import android.util.Pair; import android.util.Slog; +import android.text.TextUtils; import com.android.internal.annotations.GuardedBy; import com.android.internal.logging.MetricsLogger; @@ -110,6 +111,8 @@ import java.util.NoSuchElementException; import java.util.Scanner; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /** * UsbDeviceManager manages USB state in device mode. @@ -135,6 +138,11 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser */ private static final String NORMAL_BOOT = "normal"; + /** + * UDC controller for the ConfigFS USB Gadgets. + */ + private static final String USB_CONTROLLER_NAME_PROPERTY = "sys.usb.controller"; + private static final String USB_STATE_MATCH = "DEVPATH=/devices/virtual/android_usb/android0"; private static final String ACCESSORY_START_MATCH = @@ -932,6 +940,42 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser sEventLogger.enqueue(new EventLogger.StringEvent("USB intent: " + intent)); } + private void getMidiCardDevice() throws FileNotFoundException { + String controllerName = getSystemProperty(USB_CONTROLLER_NAME_PROPERTY, ""); + if (TextUtils.isEmpty(controllerName)) { + throw new FileNotFoundException("controller name not found"); + } + + File soundDir = new File("/sys/class/udc/" + controllerName + "/gadget/sound"); + if (!soundDir.exists()) { + throw new FileNotFoundException("sound device not found"); + } + + // There should be exactly one sound card + File[] cardDirs = FileUtils.listFilesOrEmpty(soundDir, + (dir, file) -> file.startsWith("card")); + if (cardDirs.length != 1) { + throw new FileNotFoundException("sound card not match"); + } + + // There should be exactly one midi device + File[] midis = FileUtils.listFilesOrEmpty(cardDirs[0], + (dir, file) -> file.startsWith("midi")); + if (midis.length != 1) { + throw new FileNotFoundException("MIDI device not match"); + } + + Pattern pattern = Pattern.compile("midiC(\\d+)D(\\d+)"); + Matcher matcher = pattern.matcher(midis[0].getName()); + if (matcher.matches()) { + mMidiCard = Integer.parseInt(matcher.group(1)); + mMidiDevice = Integer.parseInt(matcher.group(2)); + Slog.i(TAG, "Found MIDI card " + mMidiCard + " device " + mMidiDevice); + } else { + throw new FileNotFoundException("MIDI name not match"); + } + } + private void updateUsbFunctions() { updateMidiFunction(); updateMtpFunction(); @@ -941,17 +985,26 @@ public class UsbDeviceManager implements ActivityTaskManagerInternal.ScreenObser boolean enabled = (mCurrentFunctions & UsbManager.FUNCTION_MIDI) != 0; if (enabled != mMidiEnabled) { if (enabled) { - Scanner scanner = null; - try { - scanner = new Scanner(new File(MIDI_ALSA_PATH)); - mMidiCard = scanner.nextInt(); - mMidiDevice = scanner.nextInt(); - } catch (FileNotFoundException e) { - Slog.e(TAG, "could not open MIDI file", e); - enabled = false; - } finally { - if (scanner != null) { - scanner.close(); + if (android.hardware.usb.flags.Flags.enableUsbSysfsMidiIdentification()) { + try { + getMidiCardDevice(); + } catch (FileNotFoundException e) { + Slog.e(TAG, "could not identify MIDI device", e); + enabled = false; + } + } else { + Scanner scanner = null; + try { + scanner = new Scanner(new File(MIDI_ALSA_PATH)); + mMidiCard = scanner.nextInt(); + mMidiDevice = scanner.nextInt(); + } catch (FileNotFoundException e) { + Slog.e(TAG, "could not open MIDI file", e); + enabled = false; + } finally { + if (scanner != null) { + scanner.close(); + } } } } diff --git a/telephony/java/android/telephony/ims/ImsRegistrationAttributes.java b/telephony/java/android/telephony/ims/ImsRegistrationAttributes.java index f548dbada3cb..c7af66167a09 100644 --- a/telephony/java/android/telephony/ims/ImsRegistrationAttributes.java +++ b/telephony/java/android/telephony/ims/ImsRegistrationAttributes.java @@ -256,6 +256,26 @@ public final class ImsRegistrationAttributes implements Parcelable { } /** + * Get the attribute flag ATTR_REGISTRATION_TYPE_EMERGENCY. + * @return {@code true} if the ATTR_REGISTRATION_TYPE_EMERGENCY attribute has been set, or + * {@code false} if it has not been set. + */ + @FlaggedApi(Flags.FLAG_EMERGENCY_REGISTRATION_STATE) + public boolean getFlagRegistrationTypeEmergency() { + return (mImsAttributeFlags & ATTR_REGISTRATION_TYPE_EMERGENCY) != 0; + } + + /** + * Get the attribute flag ATTR_VIRTUAL_FOR_ANONYMOUS_EMERGENCY_CALL. + * @return {@code true} if the ATTR_VIRTUAL_FOR_ANONYMOUS_EMERGENCY_CALL attribute has been set, + * or {@code false} if it has not been set. + */ + @FlaggedApi(Flags.FLAG_EMERGENCY_REGISTRATION_STATE) + public boolean getFlagVirtualRegistrationForEmergencyCall() { + return (mImsAttributeFlags & ATTR_VIRTUAL_FOR_ANONYMOUS_EMERGENCY_CALL) != 0; + } + + /** * Gets the Set of feature tags associated with the current IMS registration, if the IMS * service supports supplying this information. * <p> diff --git a/tests/Input/assets/testPointerScale.png b/tests/Input/assets/testPointerScale.png Binary files differnew file mode 100644 index 000000000000..54d37c24afc6 --- /dev/null +++ b/tests/Input/assets/testPointerScale.png diff --git a/tests/Input/src/com/android/test/input/PointerIconLoadingTest.kt b/tests/Input/src/com/android/test/input/PointerIconLoadingTest.kt index dac425329f48..d196b85a7466 100644 --- a/tests/Input/src/com/android/test/input/PointerIconLoadingTest.kt +++ b/tests/Input/src/com/android/test/input/PointerIconLoadingTest.kt @@ -93,7 +93,29 @@ class PointerIconLoadingTest { PointerIcon.getLoadedSystemIcon( ContextThemeWrapper(context, theme), PointerIcon.TYPE_ARROW, - /* useLargeIcons= */ false) + /* useLargeIcons= */ false, + /* pointerScale= */ 1f) + + pointerIcon.getBitmap().assertAgainstGolden( + screenshotRule, + testName.methodName, + exactScreenshotMatcher + ) + } + + @Test + fun testPointerScale() { + assumeTrue(enableVectorCursors()) + assumeTrue(enableVectorCursorA11ySettings()) + + val pointerScale = 2f + + val pointerIcon = + PointerIcon.getLoadedSystemIcon( + context, + PointerIcon.TYPE_ARROW, + /* useLargeIcons= */ false, + pointerScale) pointerIcon.getBitmap().assertAgainstGolden( screenshotRule, |