diff options
972 files changed, 18068 insertions, 8233 deletions
diff --git a/cmds/app_process/Android.bp b/cmds/app_process/Android.bp index a1575173ded6..3c7609e1d8ed 100644 --- a/cmds/app_process/Android.bp +++ b/cmds/app_process/Android.bp @@ -56,6 +56,7 @@ cc_binary { "libsigchain", "libutils", + "libutilscallstack", // This is a list of libraries that need to be included in order to avoid // bad apps. This prevents a library from having a mismatch when resolving diff --git a/cmds/incidentd/src/IncidentService.cpp b/cmds/incidentd/src/IncidentService.cpp index 5ebf3e2c3047..de35ffc3fdb9 100644 --- a/cmds/incidentd/src/IncidentService.cpp +++ b/cmds/incidentd/src/IncidentService.cpp @@ -52,7 +52,11 @@ enum { #define SKIPPED_DUMPSTATE_SECTIONS { \ 1100, 1101, 1102, 1103, 1104, 1105, 1106, 1107, 1108, /* Logs */ \ 1200, 1201, 1202, /* Native, hal, java traces */ \ - 3018, /* dumpsys meminfo*/ } + /* dumpsys sections except for odpm data (3054- 3056) which are still needed */ \ + 3000, 3001, 3002, 3003, 3004, 3005, 3006, 3007, 3008, 3009, 3010, 3011, 3012, 3013, \ + 3014, 3015, 3016, 3017, 3018, 3019, 3020, 3021, 3022, 3023, 3024, 3027, 3028, 3029, \ + 3030, 3031, 3032, 3033, 3034, 3035, 3036, 3037, 3038, 3039, 3040, 3041, 3042, 3043, \ + 3044, 3045, 3046, 3047, 3048, 3049, 3050, 3051, 3052, 3053, 4000, 4001,} namespace android { namespace os { diff --git a/core/api/current.txt b/core/api/current.txt index 216bbab882a9..bba21f418e41 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -24820,7 +24820,7 @@ package android.media { method public android.view.Surface getSurface(); method public boolean isPrivacySensitive(); method public void pause() throws java.lang.IllegalStateException; - method public void prepare() throws java.io.IOException, java.lang.IllegalStateException; + method @RequiresPermission(value=android.Manifest.permission.RECORD_AUDIO, conditional=true) public void prepare() throws java.io.IOException, java.lang.IllegalStateException; method public void registerAudioRecordingCallback(@NonNull java.util.concurrent.Executor, @NonNull android.media.AudioManager.AudioRecordingCallback); method public void release(); method public void removeOnRoutingChangedListener(android.media.AudioRouting.OnRoutingChangedListener); @@ -24861,7 +24861,7 @@ package android.media { method public void setVideoProfile(@NonNull android.media.EncoderProfiles.VideoProfile); method public void setVideoSize(int, int) throws java.lang.IllegalStateException; method public void setVideoSource(int) throws java.lang.IllegalStateException; - method public void start() throws java.lang.IllegalStateException; + method @RequiresPermission(value=android.Manifest.permission.RECORD_AUDIO, conditional=true) public void start() throws java.lang.IllegalStateException; method public void stop() throws java.lang.IllegalStateException; method public void unregisterAudioRecordingCallback(@NonNull android.media.AudioManager.AudioRecordingCallback); field public static final int MEDIA_ERROR_SERVER_DIED = 100; // 0x64 diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 75ef8b6301d1..95b9b49dae3d 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -2940,6 +2940,13 @@ package android.app.smartspace.uitemplatedata { package android.app.supervision { + @FlaggedApi("android.app.supervision.flags.enable_supervision_app_service") public class SupervisionAppService extends android.app.Service { + ctor public SupervisionAppService(); + method @Nullable public final android.os.IBinder onBind(@Nullable android.content.Intent); + method @FlaggedApi("android.app.supervision.flags.enable_supervision_app_service") public void onDisabled(); + method @FlaggedApi("android.app.supervision.flags.enable_supervision_app_service") public void onEnabled(); + } + @FlaggedApi("android.app.supervision.flags.supervision_manager_apis") public class SupervisionManager { method @FlaggedApi("android.app.supervision.flags.supervision_manager_apis") @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.QUERY_USERS}) public android.content.Intent createConfirmSupervisionCredentialsIntent(); method @FlaggedApi("android.app.supervision.flags.supervision_manager_apis") @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.QUERY_USERS}) public boolean isSupervisionEnabled(); @@ -19271,7 +19278,6 @@ package android.webkit { method @Deprecated public abstract void setUseWebViewBackgroundForOverscrollBackground(boolean); method @Deprecated public abstract void setUserAgent(int); method public abstract void setVideoOverlayForEmbeddedEncryptedVideoEnabled(boolean); - field @FlaggedApi("android.webkit.enable_chips") public static final long ENABLE_CHIPS = 380890146L; // 0x16b3ec22L field public static final long ENABLE_SIMPLIFIED_DARK_MODE = 214741472L; // 0xcccb1e0L field @FlaggedApi("android.webkit.user_agent_reduction") public static final long ENABLE_USER_AGENT_REDUCTION = 371034303L; // 0x161d88bfL } diff --git a/core/java/android/app/AppCompatTaskInfo.java b/core/java/android/app/AppCompatTaskInfo.java index 3fd9d8b26611..85621c9c3fab 100644 --- a/core/java/android/app/AppCompatTaskInfo.java +++ b/core/java/android/app/AppCompatTaskInfo.java @@ -136,7 +136,7 @@ public class AppCompatTaskInfo implements Parcelable { private static final int FLAGS_ORGANIZER_INTERESTED = FLAG_IS_FROM_LETTERBOX_DOUBLE_TAP | FLAG_ELIGIBLE_FOR_USER_ASPECT_RATIO_BUTTON | FLAG_FULLSCREEN_OVERRIDE_SYSTEM | FLAG_FULLSCREEN_OVERRIDE_USER | FLAG_HAS_MIN_ASPECT_RATIO_OVERRIDE - | FLAG_OPT_OUT_EDGE_TO_EDGE; + | FLAG_OPT_OUT_EDGE_TO_EDGE | FLAG_ENABLE_RESTART_MENU_FOR_DISPLAY_MOVE; @TopActivityFlag private static final int FLAGS_COMPAT_UI_INTERESTED = FLAGS_ORGANIZER_INTERESTED @@ -179,7 +179,8 @@ public class AppCompatTaskInfo implements Parcelable { */ public boolean hasCompatUI() { return isTopActivityInSizeCompat() || eligibleForLetterboxEducation() - || isLetterboxDoubleTapEnabled() || eligibleForUserAspectRatioButton(); + || isLetterboxDoubleTapEnabled() || eligibleForUserAspectRatioButton() + || isRestartMenuEnabledForDisplayMove(); } /** diff --git a/core/java/android/app/ApplicationStartInfo.java b/core/java/android/app/ApplicationStartInfo.java index 2e8031dd22fe..2559bd036039 100644 --- a/core/java/android/app/ApplicationStartInfo.java +++ b/core/java/android/app/ApplicationStartInfo.java @@ -231,9 +231,9 @@ public final class ApplicationStartInfo implements Parcelable { public static final int START_COMPONENT_OTHER = 5; /** - * @see #getMonoticCreationTimeMs + * @see #getMonotonicCreationTimeMs */ - private long mMonoticCreationTimeMs; + private long mMonotonicCreationTimeMs; /** * @see #getStartupState @@ -545,8 +545,8 @@ public final class ApplicationStartInfo implements Parcelable { * * @hide */ - public long getMonoticCreationTimeMs() { - return mMonoticCreationTimeMs; + public long getMonotonicCreationTimeMs() { + return mMonotonicCreationTimeMs; } /** @@ -751,14 +751,14 @@ public final class ApplicationStartInfo implements Parcelable { dest.writeParcelable(mStartIntent, flags); dest.writeInt(mLaunchMode); dest.writeBoolean(mWasForceStopped); - dest.writeLong(mMonoticCreationTimeMs); + dest.writeLong(mMonotonicCreationTimeMs); dest.writeInt(mStartComponent); } // LINT.ThenChange(:read_parcel) /** @hide */ public ApplicationStartInfo(long monotonicCreationTimeMs) { - mMonoticCreationTimeMs = monotonicCreationTimeMs; + mMonotonicCreationTimeMs = monotonicCreationTimeMs; } /** @hide */ @@ -776,7 +776,7 @@ public final class ApplicationStartInfo implements Parcelable { mStartIntent = other.mStartIntent; mLaunchMode = other.mLaunchMode; mWasForceStopped = other.mWasForceStopped; - mMonoticCreationTimeMs = other.mMonoticCreationTimeMs; + mMonotonicCreationTimeMs = other.mMonotonicCreationTimeMs; mStartComponent = other.mStartComponent; } @@ -803,7 +803,7 @@ public final class ApplicationStartInfo implements Parcelable { in.readParcelable(Intent.class.getClassLoader(), android.content.Intent.class); mLaunchMode = in.readInt(); mWasForceStopped = in.readBoolean(); - mMonoticCreationTimeMs = in.readLong(); + mMonotonicCreationTimeMs = in.readLong(); mStartComponent = in.readInt(); } // LINT.ThenChange(:write_parcel) @@ -887,7 +887,7 @@ public final class ApplicationStartInfo implements Parcelable { } proto.write(ApplicationStartInfoProto.LAUNCH_MODE, mLaunchMode); proto.write(ApplicationStartInfoProto.WAS_FORCE_STOPPED, mWasForceStopped); - proto.write(ApplicationStartInfoProto.MONOTONIC_CREATION_TIME_MS, mMonoticCreationTimeMs); + proto.write(ApplicationStartInfoProto.MONOTONIC_CREATION_TIME_MS, mMonotonicCreationTimeMs); proto.write(ApplicationStartInfoProto.START_COMPONENT, mStartComponent); proto.end(token); } @@ -980,7 +980,7 @@ public final class ApplicationStartInfo implements Parcelable { ApplicationStartInfoProto.WAS_FORCE_STOPPED); break; case (int) ApplicationStartInfoProto.MONOTONIC_CREATION_TIME_MS: - mMonoticCreationTimeMs = proto.readLong( + mMonotonicCreationTimeMs = proto.readLong( ApplicationStartInfoProto.MONOTONIC_CREATION_TIME_MS); break; case (int) ApplicationStartInfoProto.START_COMPONENT: @@ -999,7 +999,7 @@ public final class ApplicationStartInfo implements Parcelable { sb.append(prefix) .append("ApplicationStartInfo ").append(seqSuffix).append(':') .append('\n') - .append(" monotonicCreationTimeMs=").append(mMonoticCreationTimeMs) + .append(" monotonicCreationTimeMs=").append(mMonotonicCreationTimeMs) .append('\n') .append(" pid=").append(mPid) .append(" realUid=").append(mRealUid) @@ -1094,7 +1094,7 @@ public final class ApplicationStartInfo implements Parcelable { && TextUtils.equals(mProcessName, o.mProcessName) && timestampsEquals(o) && mWasForceStopped == o.mWasForceStopped - && mMonoticCreationTimeMs == o.mMonoticCreationTimeMs + && mMonotonicCreationTimeMs == o.mMonotonicCreationTimeMs && mStartComponent == o.mStartComponent; } @@ -1102,7 +1102,7 @@ public final class ApplicationStartInfo implements Parcelable { public int hashCode() { return Objects.hash(mPid, mRealUid, mPackageUid, mDefiningUid, mReason, mStartupState, mStartType, mLaunchMode, mPackageName, mProcessName, mStartupTimestampsNs, - mMonoticCreationTimeMs, mStartComponent); + mMonotonicCreationTimeMs, mStartComponent); } private boolean timestampsEquals(@NonNull ApplicationStartInfo other) { diff --git a/core/java/android/app/ForegroundServiceTypePolicy.java b/core/java/android/app/ForegroundServiceTypePolicy.java index 6efc4ef55180..3003b79435e2 100644 --- a/core/java/android/app/ForegroundServiceTypePolicy.java +++ b/core/java/android/app/ForegroundServiceTypePolicy.java @@ -49,11 +49,14 @@ import android.annotation.Nullable; import android.annotation.SuppressLint; import android.app.compat.CompatChanges; import android.app.role.RoleManager; +import android.companion.virtual.VirtualDevice; +import android.companion.virtual.VirtualDeviceManager; import android.compat.Compatibility; import android.compat.annotation.ChangeId; import android.compat.annotation.Disabled; import android.compat.annotation.EnabledAfter; import android.compat.annotation.Overridable; +import android.content.AttributionSource; import android.content.Context; import android.content.PermissionChecker; import android.content.pm.PackageManager; @@ -67,6 +70,7 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.os.UserHandle; import android.permission.PermissionCheckerManager; +import android.permission.PermissionManager; import android.provider.DeviceConfig; import android.text.TextUtils; import android.util.ArrayMap; @@ -1174,17 +1178,48 @@ public abstract class ForegroundServiceTypePolicy { @PackageManager.PermissionResult public int checkPermission(@NonNull Context context, int callerUid, int callerPid, String packageName, boolean allowWhileInUse) { - return checkPermission(context, mName, callerUid, callerPid, packageName, - allowWhileInUse); + int permissionResult = checkPermission(context, mName, callerUid, callerPid, + packageName, allowWhileInUse, Context.DEVICE_ID_DEFAULT); + + if (permissionResult == PERMISSION_GRANTED + || !PermissionManager.DEVICE_AWARE_PERMISSIONS.contains(mName)) { + return permissionResult; + } + + // For device aware permissions, check if the permission is granted on any other + // active virtual device + VirtualDeviceManager vdm = context.getSystemService(VirtualDeviceManager.class); + if (vdm == null) { + return permissionResult; + } + + final List<VirtualDevice> virtualDevices = vdm.getVirtualDevices(); + for (int i = 0, size = virtualDevices.size(); i < size; i++) { + final VirtualDevice virtualDevice = virtualDevices.get(i); + int resolvedDeviceId = PermissionManager.resolveDeviceIdForPermissionCheck( + context, virtualDevice.getDeviceId(), mName); + // we already checked on the default device context + if (resolvedDeviceId == Context.DEVICE_ID_DEFAULT) { + continue; + } + permissionResult = checkPermission(context, mName, callerUid, callerPid, + packageName, allowWhileInUse, resolvedDeviceId); + if (permissionResult == PERMISSION_GRANTED) { + break; + } + } + + return permissionResult; } @SuppressLint("AndroidFrameworkRequiresPermission") @PackageManager.PermissionResult int checkPermission(@NonNull Context context, @NonNull String name, int callerUid, - int callerPid, String packageName, boolean allowWhileInUse) { + int callerPid, String packageName, boolean allowWhileInUse, int deviceId) { + final AttributionSource attributionSource = new AttributionSource(callerUid, + packageName, null /*attributionTag*/, deviceId); @PermissionCheckerManager.PermissionResult final int result = - PermissionChecker.checkPermissionForPreflight(context, name, - callerPid, callerUid, packageName); + PermissionChecker.checkPermissionForPreflight(context, name, attributionSource); if (result == PERMISSION_HARD_DENIED) { // If the user didn't grant this permission at all. return PERMISSION_DENIED; @@ -1196,7 +1231,7 @@ public abstract class ForegroundServiceTypePolicy { ? PERMISSION_GRANTED : PERMISSION_DENIED; } final AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class); - final int mode = appOpsManager.unsafeCheckOpRawNoThrow(opCode, callerUid, packageName); + final int mode = appOpsManager.unsafeCheckOpRawNoThrow(opCode, attributionSource); switch (mode) { case MODE_ALLOWED: // The appop is just allowed, plain and simple. diff --git a/core/java/android/app/PropertyInvalidatedCache.java b/core/java/android/app/PropertyInvalidatedCache.java index 38141cf20ce3..6e495768bfd4 100644 --- a/core/java/android/app/PropertyInvalidatedCache.java +++ b/core/java/android/app/PropertyInvalidatedCache.java @@ -1417,7 +1417,36 @@ public class PropertyInvalidatedCache<Query, Result> { } /** - * Enable or disable testing. The protocol requires that the mode toggle: for instance, it is + * Throw if the current process is not allowed to use test APIs. + */ + @android.ravenwood.annotation.RavenwoodReplace + private static void throwIfNotTest() { + final ActivityThread activityThread = ActivityThread.currentActivityThread(); + if (activityThread == null) { + // Only tests can reach here. + return; + } + final Instrumentation instrumentation = activityThread.getInstrumentation(); + if (instrumentation == null) { + // Only tests can reach here. + return; + } + if (instrumentation.isInstrumenting()) { + return; + } + if (Flags.enforcePicTestmodeProtocol()) { + throw new IllegalStateException("Test-only API called not from a test."); + } + } + + /** + * Do not throw if running under ravenwood. + */ + private static void throwIfNotTest$ravenwood() { + } + + /** + * Enable or disable test mode. The protocol requires that the mode toggle: for instance, it is * illegal to clear the test mode if the test mode is already off. Enabling test mode puts * all caches in the process into test mode; all nonces are initialized to UNSET and * subsequent reads and writes are to process memory. This has the effect of disabling all @@ -1425,10 +1454,12 @@ public class PropertyInvalidatedCache<Query, Result> { * operation. * @param mode The desired test mode. * @throws IllegalStateException if the supplied mode is already set. + * @throws IllegalStateException if the process is not running an instrumentation test. * @hide */ @VisibleForTesting public static void setTestMode(boolean mode) { + throwIfNotTest(); synchronized (sGlobalLock) { if (sTestMode == mode) { final String msg = "cannot set test mode redundantly: mode=" + mode; @@ -1464,9 +1495,11 @@ public class PropertyInvalidatedCache<Query, Result> { * for which it would not otherwise have permission. Caches in test mode do NOT write their * values to the system properties. The effect is local to the current process. Test mode * must be true when this method is called. + * @throws IllegalStateException if the process is not running an instrumentation test. * @hide */ public void testPropertyName() { + throwIfNotTest(); synchronized (sGlobalLock) { if (sTestMode == false) { throw new IllegalStateException("cannot test property name with test mode off"); @@ -1777,10 +1810,12 @@ public class PropertyInvalidatedCache<Query, Result> { * When multiple caches share a single property value, using an instance method on one of * the cache objects to invalidate all of the cache objects becomes confusing and you should * just use the static version of this function. + * @throws IllegalStateException if the process is not running an instrumentation test. * @hide */ @VisibleForTesting public void disableSystemWide() { + throwIfNotTest(); disableSystemWide(mPropertyName); } diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java index 01868cc601fe..927d46999284 100644 --- a/core/java/android/app/StatusBarManager.java +++ b/core/java/android/app/StatusBarManager.java @@ -58,8 +58,10 @@ import com.android.internal.statusbar.IStatusBarService; import com.android.internal.statusbar.IUndoMediaTransferCallback; import com.android.internal.statusbar.NotificationVisibility; +import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -119,6 +121,7 @@ public class StatusBarManager { | DISABLE_SEARCH | DISABLE_ONGOING_CALL_CHIP; /** @hide */ + @Target(ElementType.TYPE_USE) @IntDef(flag = true, prefix = {"DISABLE_"}, value = { DISABLE_NONE, DISABLE_EXPAND, @@ -161,6 +164,7 @@ public class StatusBarManager { | DISABLE2_NOTIFICATION_SHADE | DISABLE2_GLOBAL_ACTIONS | DISABLE2_ROTATE_SUGGESTIONS; /** @hide */ + @Target(ElementType.TYPE_USE) @IntDef(flag = true, prefix = { "DISABLE2_" }, value = { DISABLE2_NONE, DISABLE2_MASK, diff --git a/core/java/android/app/WallpaperManager.java b/core/java/android/app/WallpaperManager.java index 7f1870bd2d87..e8d2e2871ef2 100644 --- a/core/java/android/app/WallpaperManager.java +++ b/core/java/android/app/WallpaperManager.java @@ -20,6 +20,7 @@ import static android.Manifest.permission.MANAGE_EXTERNAL_STORAGE; import static android.Manifest.permission.READ_WALLPAPER_INTERNAL; import static android.Manifest.permission.SET_WALLPAPER_DIM_AMOUNT; import static android.app.Flags.FLAG_LIVE_WALLPAPER_CONTENT_HANDLING; +import static android.app.Flags.enableConnectedDisplaysWallpaper; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.os.ParcelFileDescriptor.MODE_READ_ONLY; @@ -874,7 +875,13 @@ public class WallpaperManager { return null; } try (InputStream is = new ParcelFileDescriptor.AutoCloseInputStream(pfd)) { - ImageDecoder.Source src = ImageDecoder.createSource(context.getResources(), is); + ImageDecoder.Source src; + if (enableConnectedDisplaysWallpaper()) { + src = ImageDecoder.createSource(context.getResources(), is, + /* density= */ 0); + } else { + src = ImageDecoder.createSource(context.getResources(), is); + } return ImageDecoder.decodeBitmap(src, ((decoder, info, source) -> { // Mutable and hardware config can't be set at the same time. decoder.setMutableRequired(!hardware); diff --git a/core/java/android/app/WindowConfiguration.java b/core/java/android/app/WindowConfiguration.java index c6d0f61b529e..8c99bd8e2ed9 100644 --- a/core/java/android/app/WindowConfiguration.java +++ b/core/java/android/app/WindowConfiguration.java @@ -790,12 +790,6 @@ public class WindowConfiguration implements Parcelable, Comparable<WindowConfigu || mWindowingMode == WINDOWING_MODE_MULTI_WINDOW; } - /** Returns true if the task bounds should persist across power cycles. - * @hide */ - public boolean persistTaskBounds() { - return mWindowingMode == WINDOWING_MODE_FREEFORM; - } - /** * Returns true if the tasks associated with this window configuration are floating. * Floating tasks are laid out differently as they are allowed to extend past the display bounds diff --git a/core/java/android/app/activity_manager.aconfig b/core/java/android/app/activity_manager.aconfig index e431426d5d09..a26bfa4f586c 100644 --- a/core/java/android/app/activity_manager.aconfig +++ b/core/java/android/app/activity_manager.aconfig @@ -170,3 +170,30 @@ flag { description: "Holdback study for jank_perceptible_narrow" bug: "304837972" } + +flag { + namespace: "system_performance" + name: "app_start_info_cleanup_old_records" + description: "Cleanup old records to reduce size of in memory store." + bug: "384539178" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { + namespace: "system_performance" + name: "app_start_info_keep_records_sorted" + description: "Ensure records are kept sorted to avoid extra work" + bug: "384539178" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { + name: "enable_process_observer_broadcast_on_process_started" + namespace: "system_performance" + description: "Enable ProcessObserver's onProcessStarted callbacks." + bug: "323959187" +} diff --git a/core/java/android/app/supervision/SupervisionAppService.java b/core/java/android/app/supervision/SupervisionAppService.java index 4530be5c270a..93eb96204444 100644 --- a/core/java/android/app/supervision/SupervisionAppService.java +++ b/core/java/android/app/supervision/SupervisionAppService.java @@ -16,7 +16,11 @@ package android.app.supervision; +import android.annotation.FlaggedApi; +import android.annotation.Nullable; +import android.annotation.SystemApi; import android.app.Service; +import android.app.supervision.flags.Flags; import android.content.Intent; import android.os.IBinder; @@ -26,31 +30,43 @@ import android.os.IBinder; * * @hide */ +@SystemApi +@FlaggedApi(Flags.FLAG_ENABLE_SUPERVISION_APP_SERVICE) public class SupervisionAppService extends Service { - private final ISupervisionAppService mBinder = new ISupervisionAppService.Stub() { - @Override - public void onEnabled() { - SupervisionAppService.this.onEnabled(); - } + private final ISupervisionAppService mBinder = + new ISupervisionAppService.Stub() { + @Override + public void onEnabled() { + SupervisionAppService.this.onEnabled(); + } - @Override - public void onDisabled() { - SupervisionAppService.this.onDisabled(); - } - }; + @Override + public void onDisabled() { + SupervisionAppService.this.onDisabled(); + } + }; + @Nullable @Override - public final IBinder onBind(Intent intent) { + public final IBinder onBind(@Nullable Intent intent) { return mBinder.asBinder(); } /** * Called when supervision is enabled. + * + * @hide */ + @SystemApi + @FlaggedApi(Flags.FLAG_ENABLE_SUPERVISION_APP_SERVICE) public void onEnabled() {} /** * Called when supervision is disabled. + * + * @hide */ + @SystemApi + @FlaggedApi(Flags.FLAG_ENABLE_SUPERVISION_APP_SERVICE) public void onDisabled() {} } diff --git a/core/java/android/companion/virtual/flags/flags.aconfig b/core/java/android/companion/virtual/flags/flags.aconfig index 615a6dffdf99..161f05bc5139 100644 --- a/core/java/android/companion/virtual/flags/flags.aconfig +++ b/core/java/android/companion/virtual/flags/flags.aconfig @@ -166,3 +166,10 @@ flag { bug: "393517834" is_exported: true } + +flag { + name: "external_virtual_cameras" + namespace: "virtual_devices" + description: "Allow external virtual cameras visible only in the Context of the virtual device" + bug: "375609768" +} diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index bb62ac321202..a253613e060c 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -680,6 +680,7 @@ public class Intent implements Parcelable, Cloneable { private static final String ATTR_COMPONENT = "component"; private static final String ATTR_DATA = "data"; private static final String ATTR_FLAGS = "flags"; + private static final String ATTR_PACKAGE = "package"; // --------------------------------------------------------------------- // --------------------------------------------------------------------- @@ -12893,6 +12894,9 @@ public class Intent implements Parcelable, Cloneable { if (mComponent != null) { out.attribute(null, ATTR_COMPONENT, mComponent.flattenToShortString()); } + if (android.content.flags.Flags.intentSaveToXmlPackage() && mPackage != null) { + out.attribute(null, ATTR_PACKAGE, mPackage); + } out.attribute(null, ATTR_FLAGS, Integer.toHexString(getFlags())); if (mCategories != null) { @@ -12926,6 +12930,9 @@ public class Intent implements Parcelable, Cloneable { intent.setComponent(ComponentName.unflattenFromString(attrValue)); } else if (ATTR_FLAGS.equals(attrName)) { intent.setFlags(Integer.parseInt(attrValue, 16)); + } else if (android.content.flags.Flags.intentSaveToXmlPackage() + && ATTR_PACKAGE.equals(attrName)) { + intent.setPackage(attrValue); } else { Log.e(TAG, "restoreFromXml: unknown attribute=" + attrName); } diff --git a/core/java/android/content/flags/flags.aconfig b/core/java/android/content/flags/flags.aconfig index aac04b3a9d15..148532b62c36 100644 --- a/core/java/android/content/flags/flags.aconfig +++ b/core/java/android/content/flags/flags.aconfig @@ -7,4 +7,14 @@ flag { namespace: "machine_learning" description: "This flag enables the newly added flag for binding package-private isolated processes." bug: "312706530" -}
\ No newline at end of file +} + +flag { + namespace: "system_performance" + name: "intent_save_to_xml_package" + description: "Add package to saveToXml so save then restore passes filterEquals." + bug: "369856202" + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/core/java/android/content/pm/RegisteredServicesCache.java b/core/java/android/content/pm/RegisteredServicesCache.java index ded35b23608d..1ddab2c86ec2 100644 --- a/core/java/android/content/pm/RegisteredServicesCache.java +++ b/core/java/android/content/pm/RegisteredServicesCache.java @@ -104,14 +104,6 @@ public abstract class RegisteredServicesCache<V> { private final Handler mBackgroundHandler; - private final Runnable mClearServiceInfoCachesRunnable = new Runnable() { - public void run() { - synchronized (mUserIdToServiceInfoCaches) { - mUserIdToServiceInfoCaches.clear(); - } - } - }; - private static class UserServices<V> { @GuardedBy("mServicesLock") final Map<V, Integer> persistentServices = Maps.newHashMap(); @@ -565,9 +557,11 @@ public abstract class RegisteredServicesCache<V> { if (Flags.optimizeParsingInRegisteredServicesCache()) { synchronized (mUserIdToServiceInfoCaches) { - if (mUserIdToServiceInfoCaches.numMaps() > 0) { - mBackgroundHandler.removeCallbacks(mClearServiceInfoCachesRunnable); - mBackgroundHandler.postDelayed(mClearServiceInfoCachesRunnable, + if (mUserIdToServiceInfoCaches.numElementsForKey(userId) > 0) { + final Integer token = Integer.valueOf(userId); + mBackgroundHandler.removeCallbacksAndEqualMessages(token); + mBackgroundHandler.postDelayed( + new ClearServiceInfoCachesTimeoutRunnable(userId), token, SERVICE_INFO_CACHES_TIMEOUT_MILLIS); } } @@ -953,4 +947,19 @@ public abstract class RegisteredServicesCache<V> { return BackgroundThread.getHandler(); } } + + class ClearServiceInfoCachesTimeoutRunnable implements Runnable { + final int mUserId; + + ClearServiceInfoCachesTimeoutRunnable(int userId) { + this.mUserId = userId; + } + + @Override + public void run() { + synchronized (mUserIdToServiceInfoCaches) { + mUserIdToServiceInfoCaches.delete(mUserId); + } + } + } } diff --git a/core/java/android/hardware/biometrics/flags.aconfig b/core/java/android/hardware/biometrics/flags.aconfig index 4815f3e4f524..061e6f44c9c7 100644 --- a/core/java/android/hardware/biometrics/flags.aconfig +++ b/core/java/android/hardware/biometrics/flags.aconfig @@ -64,3 +64,11 @@ flag { purpose: PURPOSE_BUGFIX } } + +flag { + name: "move_fm_api_to_bm" + is_exported: true + namespace: "biometrics_framework" + description: "Feature flag for moving some FingerprintManager APIs to BiometricManager to unblock FM removal." + bug: "323957939" +} diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl index 1c2150f3c09f..5537135f7bfa 100644 --- a/core/java/android/hardware/input/IInputManager.aidl +++ b/core/java/android/hardware/input/IInputManager.aidl @@ -273,7 +273,7 @@ interface IInputManager { @PermissionManuallyEnforced @JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = " + "android.Manifest.permission.MANAGE_KEY_GESTURES)") - void registerKeyGestureHandler(IKeyGestureHandler handler); + void registerKeyGestureHandler(in int[] keyGesturesToHandle, IKeyGestureHandler handler); @PermissionManuallyEnforced @JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = " diff --git a/core/java/android/hardware/input/IKeyGestureHandler.aidl b/core/java/android/hardware/input/IKeyGestureHandler.aidl index 4da991ee85b1..08b015892710 100644 --- a/core/java/android/hardware/input/IKeyGestureHandler.aidl +++ b/core/java/android/hardware/input/IKeyGestureHandler.aidl @@ -20,12 +20,12 @@ import android.hardware.input.AidlKeyGestureEvent; import android.os.IBinder; /** @hide */ -interface IKeyGestureHandler { +oneway interface IKeyGestureHandler { /** - * Called when a key gesture starts, ends, or is cancelled. If a handler returns {@code true}, - * it means they intend to handle the full gesture and should handle all the events pertaining - * to that gesture. + * Called when a key gesture starts, ends, or is cancelled. It is only sent to the handler that + * registered the callback for that particular gesture type. + * {@see IInputManager#registerKeyGestureHandler(int[], IKeyGestureHandler)} */ - boolean handleKeyGesture(in AidlKeyGestureEvent event, in IBinder focusedToken); + void handleKeyGesture(in AidlKeyGestureEvent event, in IBinder focusedToken); } diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java index d6419afb2a5a..a66ac76d7597 100644 --- a/core/java/android/hardware/input/InputManager.java +++ b/core/java/android/hardware/input/InputManager.java @@ -1446,16 +1446,18 @@ public final class InputManager { /** * Registers a key gesture event handler for {@link KeyGestureEvent} handling. * + * @param keyGesturesToHandle list of KeyGestureTypes to listen to * @param handler the {@link KeyGestureEventHandler} - * @throws IllegalArgumentException if {@code handler} has already been registered previously. + * @throws IllegalArgumentException if {@code handler} has already been registered previously + * or key gestures provided are already registered by some other gesture handler. * @throws NullPointerException if {@code handler} or {@code executor} is null. * @hide * @see #unregisterKeyGestureEventHandler(KeyGestureEventHandler) */ @RequiresPermission(Manifest.permission.MANAGE_KEY_GESTURES) - public void registerKeyGestureEventHandler(@NonNull KeyGestureEventHandler handler) - throws IllegalArgumentException { - mGlobal.registerKeyGestureEventHandler(handler); + public void registerKeyGestureEventHandler(List<Integer> keyGesturesToHandle, + @NonNull KeyGestureEventHandler handler) throws IllegalArgumentException { + mGlobal.registerKeyGestureEventHandler(keyGesturesToHandle, handler); } /** @@ -1463,7 +1465,7 @@ public final class InputManager { * * @param handler the {@link KeyGestureEventHandler} * @hide - * @see #registerKeyGestureEventHandler(KeyGestureEventHandler) + * @see #registerKeyGestureEventHandler(List, KeyGestureEventHandler) */ @RequiresPermission(Manifest.permission.MANAGE_KEY_GESTURES) public void unregisterKeyGestureEventHandler(@NonNull KeyGestureEventHandler handler) { @@ -1741,7 +1743,7 @@ public final class InputManager { * {@see KeyGestureEventListener} which is to listen to successfully handled key gestures, this * interface allows system components to register handler for handling key gestures. * - * @see #registerKeyGestureEventHandler(KeyGestureEventHandler) + * @see #registerKeyGestureEventHandler(List, KeyGestureEventHandler) * @see #unregisterKeyGestureEventHandler(KeyGestureEventHandler) * * <p> NOTE: All callbacks will occur on system main and input threads, so the caller needs @@ -1750,14 +1752,11 @@ public final class InputManager { */ public interface KeyGestureEventHandler { /** - * Called when a key gesture event starts, is completed, or is cancelled. If a handler - * returns {@code true}, it implies that the handler intends to handle the key gesture and - * only this handler will receive the future events for this key gesture. + * Called when a key gesture event starts, is completed, or is cancelled. * * @param event the gesture event */ - boolean handleKeyGestureEvent(@NonNull KeyGestureEvent event, - @Nullable IBinder focusedToken); + void handleKeyGestureEvent(@NonNull KeyGestureEvent event, @Nullable IBinder focusedToken); } /** @hide */ diff --git a/core/java/android/hardware/input/InputManagerGlobal.java b/core/java/android/hardware/input/InputManagerGlobal.java index c4b4831ba76e..754182ce3d11 100644 --- a/core/java/android/hardware/input/InputManagerGlobal.java +++ b/core/java/android/hardware/input/InputManagerGlobal.java @@ -25,8 +25,8 @@ import android.hardware.BatteryState; import android.hardware.SensorManager; import android.hardware.input.InputManager.InputDeviceBatteryListener; import android.hardware.input.InputManager.InputDeviceListener; -import android.hardware.input.InputManager.KeyGestureEventHandler; import android.hardware.input.InputManager.KeyEventActivityListener; +import android.hardware.input.InputManager.KeyGestureEventHandler; import android.hardware.input.InputManager.KeyGestureEventListener; import android.hardware.input.InputManager.KeyboardBacklightListener; import android.hardware.input.InputManager.OnTabletModeChangedListener; @@ -49,6 +49,7 @@ import android.os.ServiceManager; import android.os.VibrationEffect; import android.os.Vibrator; import android.os.VibratorManager; +import android.util.IntArray; import android.util.Log; import android.util.SparseArray; import android.view.Display; @@ -132,13 +133,13 @@ public final class InputManagerGlobal { @Nullable private IKeyEventActivityListener mKeyEventActivityListener; - private final Object mKeyGestureEventHandlerLock = new Object(); - @GuardedBy("mKeyGestureEventHandlerLock") - @Nullable - private ArrayList<KeyGestureEventHandler> mKeyGestureEventHandlers; - @GuardedBy("mKeyGestureEventHandlerLock") + @GuardedBy("mKeyGesturesToHandlerMap") @Nullable private IKeyGestureHandler mKeyGestureHandler; + @GuardedBy("mKeyGesturesToHandlerMap") + private final SparseArray<KeyGestureEventHandler> mKeyGesturesToHandlerMap = + new SparseArray<>(); + // InputDeviceSensorManager gets notified synchronously from the binder thread when input // devices change, so it must be synchronized with the input device listeners. @@ -1177,50 +1178,69 @@ public final class InputManagerGlobal { private class LocalKeyGestureHandler extends IKeyGestureHandler.Stub { @Override - public boolean handleKeyGesture(@NonNull AidlKeyGestureEvent ev, IBinder focusedToken) { - synchronized (mKeyGestureEventHandlerLock) { - if (mKeyGestureEventHandlers == null) { - return false; - } - final int numHandlers = mKeyGestureEventHandlers.size(); - final KeyGestureEvent event = new KeyGestureEvent(ev); - for (int i = 0; i < numHandlers; i++) { - KeyGestureEventHandler handler = mKeyGestureEventHandlers.get(i); - if (handler.handleKeyGestureEvent(event, focusedToken)) { - return true; - } + public void handleKeyGesture(@NonNull AidlKeyGestureEvent ev, IBinder focusedToken) { + synchronized (mKeyGesturesToHandlerMap) { + KeyGestureEventHandler handler = mKeyGesturesToHandlerMap.get(ev.gestureType); + if (handler == null) { + Log.w(TAG, "Key gesture event " + ev.gestureType + + " occurred without a registered handler!"); + return; } + handler.handleKeyGestureEvent(new KeyGestureEvent(ev), focusedToken); } - return false; } } /** - * @see InputManager#registerKeyGestureEventHandler(KeyGestureEventHandler) + * @see InputManager#registerKeyGestureEventHandler(List, KeyGestureEventHandler) */ @RequiresPermission(Manifest.permission.MANAGE_KEY_GESTURES) - void registerKeyGestureEventHandler(@NonNull KeyGestureEventHandler handler) - throws IllegalArgumentException { + void registerKeyGestureEventHandler(List<Integer> keyGesturesToHandle, + @NonNull KeyGestureEventHandler handler) throws IllegalArgumentException { + Objects.requireNonNull(keyGesturesToHandle, "List of gestures should not be null"); Objects.requireNonNull(handler, "handler should not be null"); - synchronized (mKeyGestureEventHandlerLock) { - if (mKeyGestureHandler == null) { - mKeyGestureEventHandlers = new ArrayList<>(); - mKeyGestureHandler = new LocalKeyGestureHandler(); + if (keyGesturesToHandle.isEmpty()) { + throw new IllegalArgumentException("No key gestures provided!"); + } - try { - mIm.registerKeyGestureHandler(mKeyGestureHandler); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); + synchronized (mKeyGesturesToHandlerMap) { + IntArray newKeyGestures = new IntArray( + keyGesturesToHandle.size() + mKeyGesturesToHandlerMap.size()); + + // Check if the handler already exists + for (int i = 0; i < mKeyGesturesToHandlerMap.size(); i++) { + KeyGestureEventHandler h = mKeyGesturesToHandlerMap.valueAt(i); + if (h == handler) { + throw new IllegalArgumentException("Handler has already been registered!"); } + newKeyGestures.add(mKeyGesturesToHandlerMap.keyAt(i)); } - final int numHandlers = mKeyGestureEventHandlers.size(); - for (int i = 0; i < numHandlers; i++) { - if (mKeyGestureEventHandlers.get(i) == handler) { - throw new IllegalArgumentException("Handler has already been registered!"); + + // Check if any of the key gestures are already handled by existing handlers + for (int gesture : keyGesturesToHandle) { + if (mKeyGesturesToHandlerMap.contains(gesture)) { + throw new IllegalArgumentException("Key gesture " + gesture + + " is already registered by another handler!"); + } + newKeyGestures.add(gesture); + } + + try { + // If handler was already registered for this process, we need to unregister and + // re-register it for the new set of gestures + if (mKeyGestureHandler != null) { + mIm.unregisterKeyGestureHandler(mKeyGestureHandler); + } else { + mKeyGestureHandler = new LocalKeyGestureHandler(); + } + mIm.registerKeyGestureHandler(newKeyGestures.toArray(), mKeyGestureHandler); + for (int gesture : keyGesturesToHandle) { + mKeyGesturesToHandlerMap.put(gesture, handler); } + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); } - mKeyGestureEventHandlers.add(handler); } } @@ -1231,18 +1251,21 @@ public final class InputManagerGlobal { void unregisterKeyGestureEventHandler(@NonNull KeyGestureEventHandler handler) { Objects.requireNonNull(handler, "handler should not be null"); - synchronized (mKeyGestureEventHandlerLock) { - if (mKeyGestureEventHandlers == null) { + synchronized (mKeyGesturesToHandlerMap) { + if (mKeyGestureHandler == null) { return; } - mKeyGestureEventHandlers.removeIf(existingHandler -> existingHandler == handler); - if (mKeyGestureEventHandlers.isEmpty()) { + for (int i = mKeyGesturesToHandlerMap.size() - 1; i >= 0; i--) { + if (mKeyGesturesToHandlerMap.valueAt(i) == handler) { + mKeyGesturesToHandlerMap.removeAt(i); + } + } + if (mKeyGesturesToHandlerMap.size() == 0) { try { mIm.unregisterKeyGestureHandler(mKeyGestureHandler); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } - mKeyGestureEventHandlers = null; mKeyGestureHandler = null; } } diff --git a/core/java/android/inputmethodservice/NavigationBarController.java b/core/java/android/inputmethodservice/NavigationBarController.java index f420b5d7b886..7da053d0010e 100644 --- a/core/java/android/inputmethodservice/NavigationBarController.java +++ b/core/java/android/inputmethodservice/NavigationBarController.java @@ -236,7 +236,12 @@ final class NavigationBarController { systemInsets.bottom, Gravity.BOTTOM)); mLastInsets = systemInsets; } else { - decorView.addView(mNavigationBarFrame); + // If systemInsets are null, the DecorView is not attached to the window yet. + // Use the final captionBar height as the initial one, otherwise it resolves to + // match parent, and can lead to full size IME insets. + final int height = getImeCaptionBarHeight(true /* imeDrawsImeNavBar */); + decorView.addView(mNavigationBarFrame, new FrameLayout.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, height, Gravity.BOTTOM)); } final NavigationBarView navigationBarView = mNavigationBarFrame.findViewByPredicate( NavigationBarView.class::isInstance); @@ -461,7 +466,7 @@ final class NavigationBarController { final Insets systemInsets = getSystemInsets(); if (systemInsets != null) { if (!Objects.equals(systemInsets, mLastInsets)) { - mNavigationBarFrame.setLayoutParams(new NavigationBarFrame.LayoutParams( + mNavigationBarFrame.setLayoutParams(new FrameLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, systemInsets.bottom, Gravity.BOTTOM)); mLastInsets = systemInsets; diff --git a/core/java/android/os/CombinedMessageQueue/MessageQueue.java b/core/java/android/os/CombinedMessageQueue/MessageQueue.java index c3ec96d17437..c21959b16fbb 100644 --- a/core/java/android/os/CombinedMessageQueue/MessageQueue.java +++ b/core/java/android/os/CombinedMessageQueue/MessageQueue.java @@ -144,6 +144,12 @@ public final class MessageQueue { return; } + // Holdback study. + if (Flags.messageQueueForceLegacy()) { + sIsProcessAllowedToUseConcurrent = false; + return; + } + if (Flags.forceConcurrentMessageQueue()) { // b/379472827: Robolectric tests use reflection to access MessageQueue.mMessages. // This is a hack to allow Robolectric tests to use the legacy implementation. diff --git a/core/java/android/os/IpcDataCache.java b/core/java/android/os/IpcDataCache.java index 2e7c3be53d90..07a7f8b50f20 100644 --- a/core/java/android/os/IpcDataCache.java +++ b/core/java/android/os/IpcDataCache.java @@ -718,7 +718,7 @@ public class IpcDataCache<Query, Result> extends PropertyInvalidatedCache<Query, } /** - * Enable or disable testing. The protocol requires that the mode toggle: for instance, it is + * Enable or disable test mode. The protocol requires that the mode toggle: for instance, it is * illegal to clear the test mode if the test mode is already off. Enabling test mode puts * all caches in the process into test mode; all nonces are initialized to UNSET and * subsequent reads and writes are to process memory. This has the effect of disabling all @@ -726,6 +726,7 @@ public class IpcDataCache<Query, Result> extends PropertyInvalidatedCache<Query, * operation. * @param mode The desired test mode. * @throws IllegalStateException if the supplied mode is already set. + * @throws IllegalStateException if the process is not running an instrumentation test. * @hide */ @TestApi diff --git a/core/java/android/os/flags.aconfig b/core/java/android/os/flags.aconfig index 0150d171d51c..b52a454ea956 100644 --- a/core/java/android/os/flags.aconfig +++ b/core/java/android/os/flags.aconfig @@ -4,6 +4,15 @@ container: "system" # keep-sorted start block=yes newline_separated=yes flag { + # Holdback study for concurrent MessageQueue. + # Do not promote beyond trunkfood. + namespace: "system_performance" + name: "message_queue_force_legacy" + description: "Whether to holdback concurrent MessageQueue (force legacy)." + bug: "336880969" +} + +flag { name: "adpf_gpu_report_actual_work_duration" is_exported: true namespace: "game" diff --git a/core/java/android/security/advancedprotection/AdvancedProtectionManager.java b/core/java/android/security/advancedprotection/AdvancedProtectionManager.java index 62b2bcf32442..cb2b13d1e120 100644 --- a/core/java/android/security/advancedprotection/AdvancedProtectionManager.java +++ b/core/java/android/security/advancedprotection/AdvancedProtectionManager.java @@ -349,7 +349,8 @@ public final class AdvancedProtectionManager { * * @param featureId The feature identifier. * @param type The type of the feature describing the action that needs to be explained - * in the dialog or null for default explanation. + * in the dialog or {@link #SUPPORT_DIALOG_TYPE_UNKNOWN} for default + * explanation. * @return Intent An intent to be used to start the dialog-activity that explains a feature was * disabled by advanced protection. * @hide @@ -373,7 +374,27 @@ public final class AdvancedProtectionManager { return intent; } - /** @hide */ + /** + * Called by a feature to display a support dialog when a feature was disabled by advanced + * protection based on a policy identifier or restriction. This returns an intent that can be + * used with {@link Context#startActivity(Intent)} to display the dialog. + * + * <p>At the moment, if the dialog is for {@link #FEATURE_ID_DISALLOW_CELLULAR_2G} or + * {@link #FEATURE_ID_ENABLE_MTE} and the provided type is + * {@link #SUPPORT_DIALOG_TYPE_UNKNOWN}, the type will be changed to + * {@link #SUPPORT_DIALOG_TYPE_DISABLED_SETTING} in the returned intent, as these features only + * have a disabled setting UI. + * + * <p>Note that this method doesn't check if the feature is actually disabled, i.e. this method + * will always return an intent. + * + * @param identifier The policy identifier or restriction. + * @param type The type of the feature describing the action that needs to be explained + * in the dialog or {@link #SUPPORT_DIALOG_TYPE_UNKNOWN} for default + * explanation. + * @return Intent An intent to be used to start the dialog-activity that explains a feature was + * disabled by advanced protection. + * @hide */ public static @NonNull Intent createSupportIntentForPolicyIdentifierOrRestriction( @NonNull String identifier, @SupportDialogType int type) { Objects.requireNonNull(identifier); @@ -382,16 +403,21 @@ public final class AdvancedProtectionManager { + " SUPPORT_DIALOG_TYPE_* APIs."); } final int featureId; + int dialogType = type; if (DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY.equals(identifier)) { featureId = FEATURE_ID_DISALLOW_INSTALL_UNKNOWN_SOURCES; } else if (DISALLOW_CELLULAR_2G.equals(identifier)) { featureId = FEATURE_ID_DISALLOW_CELLULAR_2G; + dialogType = (dialogType == SUPPORT_DIALOG_TYPE_UNKNOWN) + ? SUPPORT_DIALOG_TYPE_DISABLED_SETTING : dialogType; } else if (MEMORY_TAGGING_POLICY.equals(identifier)) { featureId = FEATURE_ID_ENABLE_MTE; + dialogType = (dialogType == SUPPORT_DIALOG_TYPE_UNKNOWN) + ? SUPPORT_DIALOG_TYPE_DISABLED_SETTING : dialogType; } else { throw new UnsupportedOperationException("Unsupported identifier: " + identifier); } - return createSupportIntent(featureId, type); + return createSupportIntent(featureId, dialogType); } /** @hide */ diff --git a/core/java/android/security/flags.aconfig b/core/java/android/security/flags.aconfig index 9fd4618bc5da..0a922d61a786 100644 --- a/core/java/android/security/flags.aconfig +++ b/core/java/android/security/flags.aconfig @@ -97,10 +97,13 @@ flag { } flag { - name: "clear_strong_auth_on_add_primary_credential" + name: "clear_strong_auth_on_adding_primary_credential" namespace: "biometrics" - description: "Clear StrongAuth on add credential" + description: "Clear StrongAuth on adding credential" bug: "320817991" + metadata { + purpose: PURPOSE_BUGFIX + } } flag { diff --git a/core/java/android/util/ArrayMap.java b/core/java/android/util/ArrayMap.java index 7ee0ff15c5ad..c59907937d6a 100644 --- a/core/java/android/util/ArrayMap.java +++ b/core/java/android/util/ArrayMap.java @@ -129,7 +129,7 @@ public final class ArrayMap<K, V> implements Map<K, V> { return ContainerHelpers.binarySearch(hashes, N, hash); } catch (ArrayIndexOutOfBoundsException e) { if (CONCURRENT_MODIFICATION_EXCEPTIONS) { - throw new ConcurrentModificationException(); + throw new ConcurrentModificationException(e); } else { throw e; // the cache is poisoned at this point, there's not much we can do } diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java index 6b7b81887706..4c578fb93600 100644 --- a/core/java/android/view/InsetsController.java +++ b/core/java/android/view/InsetsController.java @@ -1030,10 +1030,18 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation handlePendingControlRequest(statsToken); } else { if (showTypes[0] != 0) { + if ((showTypes[0] & ime()) != 0) { + ImeTracker.forLogging().onProgress(statsToken, + ImeTracker.PHASE_CLIENT_ON_CONTROLS_CHANGED); + } applyAnimation(showTypes[0], true /* show */, false /* fromIme */, false /* skipsCallbacks */, statsToken); } if (hideTypes[0] != 0) { + if ((hideTypes[0] & ime()) != 0) { + ImeTracker.forLogging().onProgress(statsToken, + ImeTracker.PHASE_CLIENT_ON_CONTROLS_CHANGED); + } applyAnimation(hideTypes[0], false /* show */, false /* fromIme */, // The animation of hiding transient types shouldn't be detected by the // app. Otherwise, it might be able to react to the callbacks and cause @@ -1041,6 +1049,10 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation (hideTypes[0] & ~transientTypes[0]) == 0 /* skipsCallbacks */, statsToken); } + if ((showTypes[0] & ime()) == 0 && (hideTypes[0] & ime()) == 0) { + ImeTracker.forLogging().onCancelled(statsToken, + ImeTracker.PHASE_CLIENT_ON_CONTROLS_CHANGED); + } } } else { if (showTypes[0] != 0) { @@ -2027,8 +2039,8 @@ public class InsetsController implements WindowInsetsController, InsetsAnimation } else if (Flags.refactorInsetsController()) { if ((typesToReport & ime()) != 0 && mImeSourceConsumer != null) { InsetsSourceControl control = mImeSourceConsumer.getControl(); - if (control != null && control.getLeash() == null) { - // If the IME was requested twice, and we didn't receive the controls + if (control == null || control.getLeash() == null) { + // If the IME was requested to show twice, and we didn't receive the controls // yet, this request will not continue. It should be cancelled here, as // it would time out otherwise. ImeTracker.forLogging().onCancelled(statsToken, diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index 1b57b0045537..94e9aa709369 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -1070,9 +1070,9 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall } if (mSurfacePackage != null) { - mSurfaceControlViewHostParent.detach(); mEmbeddedWindowParams.clear(); if (releaseSurfacePackage) { + mSurfaceControlViewHostParent.detach(); mSurfacePackage.release(); mSurfacePackage = null; } diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java index 624216776f42..b97f28da7559 100644 --- a/core/java/android/view/WindowManagerGlobal.java +++ b/core/java/android/view/WindowManagerGlobal.java @@ -329,13 +329,17 @@ public final class WindowManagerGlobal { /** * Adds a listener that will be notified whenever {@link #getWindowViews()} changes. The - * current value is provided immediately. If it was registered previously then this is ano op. + * current value is provided immediately using the provided {@link Executor}. If this + * {@link Consumer} was registered previously, then this is a no op. */ public void addWindowViewsListener(@NonNull Executor executor, @NonNull Consumer<List<View>> consumer) { synchronized (mLock) { + if (mWindowViewsListenerGroup.isConsumerPresent(consumer)) { + return; + } mWindowViewsListenerGroup.addListener(executor, consumer); - mWindowViewsListenerGroup.accept(getWindowViews()); + executor.execute(() -> consumer.accept(getWindowViews())); } } diff --git a/core/java/android/view/inputmethod/ImeTracker.java b/core/java/android/view/inputmethod/ImeTracker.java index 5dadf32d2a36..b1ba8b32d2f4 100644 --- a/core/java/android/view/inputmethod/ImeTracker.java +++ b/core/java/android/view/inputmethod/ImeTracker.java @@ -231,6 +231,7 @@ public interface ImeTracker { PHASE_WM_WINDOW_ANIMATING_TYPES_CHANGED, PHASE_WM_NOTIFY_HIDE_ANIMATION_FINISHED, PHASE_WM_UPDATE_DISPLAY_WINDOW_ANIMATING_TYPES, + PHASE_CLIENT_ON_CONTROLS_CHANGED, }) @Retention(RetentionPolicy.SOURCE) @interface Phase {} @@ -469,6 +470,9 @@ public interface ImeTracker { /** The control target reported its animatingTypes back to WindowManagerService. */ int PHASE_WM_UPDATE_DISPLAY_WINDOW_ANIMATING_TYPES = ImeProtoEnums.PHASE_WM_UPDATE_DISPLAY_WINDOW_ANIMATING_TYPES; + /** InsetsController received a control for the IME. */ + int PHASE_CLIENT_ON_CONTROLS_CHANGED = + ImeProtoEnums.PHASE_CLIENT_ON_CONTROLS_CHANGED; /** * Called when an IME request is started. diff --git a/core/java/android/view/inspector/WindowInspector.java b/core/java/android/view/inspector/WindowInspector.java index 3ebca3c9d9b6..f0cc01133e07 100644 --- a/core/java/android/view/inspector/WindowInspector.java +++ b/core/java/android/view/inspector/WindowInspector.java @@ -42,8 +42,9 @@ public final class WindowInspector { } /** - * Adds a listener that is notified whenever the list of global window views changes. If a - * {@link Consumer} is already registered this method is a no op. + * Adds a listener that is notified whenever the value of {@link #getGlobalWindowViews()} + * changes. The current value is provided immediately using the provided {@link Executor}. + * If this {@link Consumer} is already registered, then this method is a no op. * @see #getGlobalWindowViews() */ @FlaggedApi(android.view.flags.Flags.FLAG_ROOT_VIEW_CHANGED_LISTENER) diff --git a/core/java/android/view/translation/ListenerGroup.java b/core/java/android/view/translation/ListenerGroup.java index bf506815f841..5c70805042fa 100644 --- a/core/java/android/view/translation/ListenerGroup.java +++ b/core/java/android/view/translation/ListenerGroup.java @@ -48,7 +48,7 @@ public class ListenerGroup<T> { * is a no op. */ public void addListener(@NonNull Executor executor, @NonNull Consumer<T> consumer) { - if (isContained(consumer)) { + if (isConsumerPresent(consumer)) { return; } mListeners.add(new ListenerWrapper<>(executor, consumer)); @@ -69,7 +69,7 @@ public class ListenerGroup<T> { * Returns {@code true} if the {@link Consumer} is present in the list, {@code false} * otherwise. */ - private boolean isContained(Consumer<T> consumer) { + public boolean isConsumerPresent(Consumer<T> consumer) { return computeIndex(consumer) > -1; } diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java index ab7a4f289d73..ed3f2d12ac78 100644 --- a/core/java/android/webkit/WebSettings.java +++ b/core/java/android/webkit/WebSettings.java @@ -1789,17 +1789,4 @@ public abstract class WebSettings { * @see #setDisabledActionModeMenuItems */ public static final int MENU_ITEM_PROCESS_TEXT = 1 << 2; - - /** - * Enable CHIPS for webview. - * This provides a means to check if partitioned cookies are enabled by default. - * CHIPS will only be enabled by default for apps targeting Android B or above. - * - * @hide - */ - @ChangeId - @EnabledAfter(targetSdkVersion = android.os.Build.VERSION_CODES.VANILLA_ICE_CREAM) - @FlaggedApi(android.webkit.Flags.FLAG_ENABLE_CHIPS) - @SystemApi - public static final long ENABLE_CHIPS = 380890146L; } diff --git a/core/java/android/webkit/flags.aconfig b/core/java/android/webkit/flags.aconfig index 16cbb8abc9d6..c5176a2f1f15 100644 --- a/core/java/android/webkit/flags.aconfig +++ b/core/java/android/webkit/flags.aconfig @@ -36,14 +36,6 @@ flag { } flag { - name: "enable_chips" - is_exported: true - namespace: "webview" - description: "New feature enable CHIPS for webview" - bug: "359448044" -} - -flag { name: "file_system_access" is_exported: true namespace: "webview" diff --git a/core/java/android/window/BackMotionEvent.java b/core/java/android/window/BackMotionEvent.java index d53c787749d9..cc2afbc6aaa3 100644 --- a/core/java/android/window/BackMotionEvent.java +++ b/core/java/android/window/BackMotionEvent.java @@ -18,6 +18,7 @@ package android.window; import android.annotation.FloatRange; import android.annotation.NonNull; +import android.annotation.Nullable; import android.os.Parcel; import android.os.Parcelable; import android.view.RemoteAnimationTarget; @@ -38,6 +39,8 @@ public final class BackMotionEvent implements Parcelable { @BackEvent.SwipeEdge private final int mSwipeEdge; + @Nullable + private final RemoteAnimationTarget mDepartingAnimationTarget; /** * Creates a new {@link BackMotionEvent} instance. @@ -50,6 +53,8 @@ public final class BackMotionEvent implements Parcelable { * @param progress Value between 0 and 1 on how far along the back gesture is. * @param triggerBack Indicates whether the back arrow is in the triggered state or not * @param swipeEdge Indicates which edge the swipe starts from. + * @param departingAnimationTarget The remote animation target of the departing + * application window. */ public BackMotionEvent( float touchX, @@ -57,13 +62,15 @@ public final class BackMotionEvent implements Parcelable { long frameTimeMillis, float progress, boolean triggerBack, - @BackEvent.SwipeEdge int swipeEdge) { + @BackEvent.SwipeEdge int swipeEdge, + @Nullable RemoteAnimationTarget departingAnimationTarget) { mTouchX = touchX; mTouchY = touchY; mFrameTimeMillis = frameTimeMillis; mProgress = progress; mTriggerBack = triggerBack; mSwipeEdge = swipeEdge; + mDepartingAnimationTarget = departingAnimationTarget; } private BackMotionEvent(@NonNull Parcel in) { @@ -72,6 +79,7 @@ public final class BackMotionEvent implements Parcelable { mProgress = in.readFloat(); mTriggerBack = in.readBoolean(); mSwipeEdge = in.readInt(); + mDepartingAnimationTarget = in.readTypedObject(RemoteAnimationTarget.CREATOR); mFrameTimeMillis = in.readLong(); } @@ -100,6 +108,7 @@ public final class BackMotionEvent implements Parcelable { dest.writeFloat(mProgress); dest.writeBoolean(mTriggerBack); dest.writeInt(mSwipeEdge); + dest.writeTypedObject(mDepartingAnimationTarget, flags); dest.writeLong(mFrameTimeMillis); } @@ -151,6 +160,16 @@ public final class BackMotionEvent implements Parcelable { return mFrameTimeMillis; } + /** + * Returns the {@link RemoteAnimationTarget} of the top departing application window, + * or {@code null} if the top window should not be moved for the current type of back + * destination. + */ + @Nullable + public RemoteAnimationTarget getDepartingAnimationTarget() { + return mDepartingAnimationTarget; + } + @Override public String toString() { return "BackMotionEvent{" @@ -160,6 +179,7 @@ public final class BackMotionEvent implements Parcelable { + ", mProgress=" + mProgress + ", mTriggerBack=" + mTriggerBack + ", mSwipeEdge=" + mSwipeEdge + + ", mDepartingAnimationTarget=" + mDepartingAnimationTarget + "}"; } } diff --git a/core/java/android/window/BackTouchTracker.java b/core/java/android/window/BackTouchTracker.java index ea1b64066cfe..4908068d51e4 100644 --- a/core/java/android/window/BackTouchTracker.java +++ b/core/java/android/window/BackTouchTracker.java @@ -20,6 +20,7 @@ import android.annotation.FloatRange; import android.os.SystemProperties; import android.util.MathUtils; import android.view.MotionEvent; +import android.view.RemoteAnimationTarget; import java.io.PrintWriter; @@ -146,14 +147,15 @@ public class BackTouchTracker { } /** Creates a start {@link BackMotionEvent}. */ - public BackMotionEvent createStartEvent() { + public BackMotionEvent createStartEvent(RemoteAnimationTarget target) { return new BackMotionEvent( /* touchX = */ mInitTouchX, /* touchY = */ mInitTouchY, /* frameTimeMillis = */ 0, /* progress = */ 0, /* triggerBack = */ mTriggerBack, - /* swipeEdge = */ mSwipeEdge); + /* swipeEdge = */ mSwipeEdge, + /* departingAnimationTarget = */ target); } /** Creates a progress {@link BackMotionEvent}. */ @@ -237,7 +239,8 @@ public class BackTouchTracker { /* frameTimeMillis = */ 0, /* progress = */ progress, /* triggerBack = */ mTriggerBack, - /* swipeEdge = */ mSwipeEdge); + /* swipeEdge = */ mSwipeEdge, + /* departingAnimationTarget = */ null); } /** Sets the thresholds for computing progress. */ diff --git a/core/java/android/window/DesktopModeFlags.java b/core/java/android/window/DesktopModeFlags.java index 5b3044e1988a..4bd0d97a54b0 100644 --- a/core/java/android/window/DesktopModeFlags.java +++ b/core/java/android/window/DesktopModeFlags.java @@ -44,7 +44,7 @@ public enum DesktopModeFlags { // All desktop mode related flags to be overridden by developer option toggle will be added here // go/keep-sorted start DISABLE_DESKTOP_LAUNCH_PARAMS_OUTSIDE_DESKTOP_BUG_FIX( - Flags::disableDesktopLaunchParamsOutsideDesktopBugFix, false), + Flags::disableDesktopLaunchParamsOutsideDesktopBugFix, true), DISABLE_NON_RESIZABLE_APP_SNAP_RESIZE(Flags::disableNonResizableAppSnapResizing, true), ENABLE_ACCESSIBLE_CUSTOM_HEADERS(Flags::enableAccessibleCustomHeaders, true), ENABLE_APP_HEADER_WITH_TASK_DENSITY(Flags::enableAppHeaderWithTaskDensity, true), @@ -103,7 +103,7 @@ public enum DesktopModeFlags { ENABLE_DESKTOP_WINDOWING_TASK_LIMIT(Flags::enableDesktopWindowingTaskLimit, true), ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY(Flags::enableDesktopWindowingWallpaperActivity, true), - ENABLE_DRAG_RESIZE_SET_UP_IN_BG_THREAD(Flags::enableDragResizeSetUpInBgThread, false), + ENABLE_DRAG_RESIZE_SET_UP_IN_BG_THREAD(Flags::enableDragResizeSetUpInBgThread, true), ENABLE_DRAG_TO_DESKTOP_INCOMING_TRANSITIONS_BUGFIX( Flags::enableDragToDesktopIncomingTransitionsBugfix, false), ENABLE_FULLY_IMMERSIVE_IN_DESKTOP(Flags::enableFullyImmersiveInDesktop, true), @@ -111,15 +111,16 @@ public enum DesktopModeFlags { ENABLE_HOLD_TO_DRAG_APP_HANDLE(Flags::enableHoldToDragAppHandle, true), ENABLE_INPUT_LAYER_TRANSITION_FIX(Flags::enableInputLayerTransitionFix, false), ENABLE_MINIMIZE_BUTTON(Flags::enableMinimizeButton, true), - ENABLE_MODALS_FULLSCREEN_WITH_PERMISSIONS(Flags::enableModalsFullscreenWithPermission, false), + ENABLE_MODALS_FULLSCREEN_WITH_PERMISSIONS(Flags::enableModalsFullscreenWithPermission, true), ENABLE_OPAQUE_BACKGROUND_FOR_TRANSPARENT_WINDOWS( Flags::enableOpaqueBackgroundForTransparentWindows, true), ENABLE_QUICKSWITCH_DESKTOP_SPLIT_BUGFIX(Flags::enableQuickswitchDesktopSplitBugfix, true), + ENABLE_REQUEST_FULLSCREEN_BUGFIX(Flags::enableRequestFullscreenBugfix, false), ENABLE_RESIZING_METRICS(Flags::enableResizingMetrics, true), ENABLE_RESTORE_TO_PREVIOUS_SIZE_FROM_DESKTOP_IMMERSIVE( Flags::enableRestoreToPreviousSizeFromDesktopImmersive, true), ENABLE_SHELL_INITIAL_BOUNDS_REGRESSION_BUG_FIX( - Flags::enableShellInitialBoundsRegressionBugFix, false), + Flags::enableShellInitialBoundsRegressionBugFix, true), ENABLE_START_LAUNCH_TRANSITION_FROM_TASKBAR_BUGFIX( Flags::enableStartLaunchTransitionFromTaskbarBugfix, true), ENABLE_TASKBAR_OVERFLOW(Flags::enableTaskbarOverflow, false), @@ -137,7 +138,7 @@ public enum DesktopModeFlags { ENABLE_WINDOWING_SCALED_RESIZING(Flags::enableWindowingScaledResizing, true), ENABLE_WINDOWING_TRANSITION_HANDLERS_OBSERVERS( Flags::enableWindowingTransitionHandlersObservers, false), - EXCLUDE_CAPTION_FROM_APP_BOUNDS(Flags::excludeCaptionFromAppBounds, false), + EXCLUDE_CAPTION_FROM_APP_BOUNDS(Flags::excludeCaptionFromAppBounds, true), FORCE_CLOSE_TOP_TRANSPARENT_FULLSCREEN_TASK( Flags::forceCloseTopTransparentFullscreenTask, false), IGNORE_ASPECT_RATIO_RESTRICTIONS_FOR_RESIZEABLE_FREEFORM_ACTIVITIES( diff --git a/core/java/android/window/ImeOnBackInvokedDispatcher.java b/core/java/android/window/ImeOnBackInvokedDispatcher.java index 69613a748884..d478108d928a 100644 --- a/core/java/android/window/ImeOnBackInvokedDispatcher.java +++ b/core/java/android/window/ImeOnBackInvokedDispatcher.java @@ -270,7 +270,8 @@ public class ImeOnBackInvokedDispatcher implements OnBackInvokedDispatcher, Parc } mIOnBackInvokedCallback.onBackStarted( new BackMotionEvent(backEvent.getTouchX(), backEvent.getTouchY(), frameTime, - backEvent.getProgress(), false, backEvent.getSwipeEdge())); + backEvent.getProgress(), false, backEvent.getSwipeEdge(), + null)); } catch (RemoteException e) { Log.e(TAG, "Exception when invoking forwarded callback. e: ", e); } @@ -285,7 +286,8 @@ public class ImeOnBackInvokedDispatcher implements OnBackInvokedDispatcher, Parc } mIOnBackInvokedCallback.onBackProgressed( new BackMotionEvent(backEvent.getTouchX(), backEvent.getTouchY(), frameTime, - backEvent.getProgress(), false, backEvent.getSwipeEdge())); + backEvent.getProgress(), false, backEvent.getSwipeEdge(), + null)); } catch (RemoteException e) { Log.e(TAG, "Exception when invoking forwarded callback. e: ", e); } diff --git a/core/java/android/window/flags/lse_desktop_experience.aconfig b/core/java/android/window/flags/lse_desktop_experience.aconfig index e4142a171669..0d87b73a5e03 100644 --- a/core/java/android/window/flags/lse_desktop_experience.aconfig +++ b/core/java/android/window/flags/lse_desktop_experience.aconfig @@ -971,6 +971,16 @@ flag { } flag { + name: "enable_request_fullscreen_bugfix" + namespace: "lse_desktop_experience" + description: "Fixes split to fullscreen restoration using the Activity#requestFullscreenMode API" + bug: "402973271" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { name: "enable_dynamic_radius_computation_bugfix" namespace: "lse_desktop_experience" description: "Enables bugfix to compute the corner/shadow radius of desktop windows dynamically with the current window context." @@ -986,3 +996,13 @@ flag { description: "Enables the home to be shown behind the desktop." bug: "375644149" } + +flag { + name: "enable_desktop_ime_bugfix" + namespace: "lse_desktop_experience" + description: "Enables bugfix to handle IME interactions in desktop windowing." + bug: "388570293" + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/core/java/android/window/flags/responsible_apis.aconfig b/core/java/android/window/flags/responsible_apis.aconfig index 36219812c002..7039add0b179 100644 --- a/core/java/android/window/flags/responsible_apis.aconfig +++ b/core/java/android/window/flags/responsible_apis.aconfig @@ -88,3 +88,10 @@ flag { description: "Clear the allowlist duration when clearAllowBgActivityStarts is called" bug: "322159724" } + +flag { + name: "bal_additional_logging" + namespace: "responsible_apis" + description: "Enable additional logging." + bug: "403398176" +} diff --git a/core/java/com/android/internal/app/AssistUtils.java b/core/java/com/android/internal/app/AssistUtils.java index 4261a0f14767..dd9cf9d7718e 100644 --- a/core/java/com/android/internal/app/AssistUtils.java +++ b/core/java/com/android/internal/app/AssistUtils.java @@ -61,6 +61,8 @@ public class AssistUtils { public static final int INVOCATION_TYPE_ASSIST_BUTTON = 7; /** value for INVOCATION_TYPE_KEY: long press on nav handle */ public static final int INVOCATION_TYPE_NAV_HANDLE_LONG_PRESS = 8; + /** value for INVOCATION_TYPE_KEY: sysui launcher */ + public static final int INVOCATION_TYPE_LAUNCHER_SYSTEM_SHORTCUT = 9; private final Context mContext; private final IVoiceInteractionManagerService mVoiceInteractionManagerService; diff --git a/core/java/com/android/internal/app/MediaRouteDialogPresenter.java b/core/java/com/android/internal/app/MediaRouteDialogPresenter.java index 5628b7ed9d15..76b289416076 100644 --- a/core/java/com/android/internal/app/MediaRouteDialogPresenter.java +++ b/core/java/com/android/internal/app/MediaRouteDialogPresenter.java @@ -86,10 +86,7 @@ public abstract class MediaRouteDialogPresenter { public static Dialog createDialog(Context context, int routeTypes, View.OnClickListener extendedSettingsClickListener, int theme, boolean showProgressBarWhenEmpty) { - final MediaRouter router = context.getSystemService(MediaRouter.class); - - MediaRouter.RouteInfo route = router.getSelectedRoute(); - if (route.isDefault() || !route.matchesTypes(routeTypes)) { + if (shouldShowChooserDialog(context, routeTypes)) { final MediaRouteChooserDialog d = new MediaRouteChooserDialog(context, theme, showProgressBarWhenEmpty); d.setRouteTypes(routeTypes); @@ -99,4 +96,11 @@ public abstract class MediaRouteDialogPresenter { return new MediaRouteControllerDialog(context, theme); } } + + /** Whether we should show the chooser dialog or the controller dialog.. */ + public static boolean shouldShowChooserDialog(Context context, int routeTypes) { + final MediaRouter router = context.getSystemService(MediaRouter.class); + MediaRouter.RouteInfo route = router.getSelectedRoute(); + return route.isDefault() || !route.matchesTypes(routeTypes); + } } diff --git a/core/java/com/android/internal/jank/Cuj.java b/core/java/com/android/internal/jank/Cuj.java index e125e258c596..c25f6b1dcacb 100644 --- a/core/java/com/android/internal/jank/Cuj.java +++ b/core/java/com/android/internal/jank/Cuj.java @@ -322,8 +322,18 @@ public class Cuj { */ public static final int CUJ_DESKTOP_MODE_MOVE_WINDOW_TO_DISPLAY = 129; + /** + * Track the animation of an ongoing call app back into its status bar chip (displaying the call + * icon and timer) when returning Home. + * + * <p>Tracking starts when the RemoteTransition registered to handle the transition from the app + * to Home is sent the onAnimationStart() signal and start the animation. Tracking ends when + * the animation is fully settled and the transition is complete.</p> + */ + public static final int CUJ_STATUS_BAR_APP_RETURN_TO_CALL_CHIP = 130; + // When adding a CUJ, update this and make sure to also update CUJ_TO_STATSD_INTERACTION_TYPE. - @VisibleForTesting static final int LAST_CUJ = CUJ_DESKTOP_MODE_MOVE_WINDOW_TO_DISPLAY; + @VisibleForTesting static final int LAST_CUJ = CUJ_STATUS_BAR_APP_RETURN_TO_CALL_CHIP; /** @hide */ @IntDef({ @@ -444,7 +454,8 @@ public class Cuj { CUJ_LAUNCHER_WORK_UTILITY_VIEW_EXPAND, CUJ_LAUNCHER_WORK_UTILITY_VIEW_SHRINK, CUJ_DEFAULT_TASK_TO_TASK_ANIMATION, - CUJ_DESKTOP_MODE_MOVE_WINDOW_TO_DISPLAY + CUJ_DESKTOP_MODE_MOVE_WINDOW_TO_DISPLAY, + CUJ_STATUS_BAR_APP_RETURN_TO_CALL_CHIP }) @Retention(RetentionPolicy.SOURCE) public @interface CujType {} @@ -576,6 +587,7 @@ public class Cuj { CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_WORK_UTILITY_VIEW_SHRINK] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_WORK_UTILITY_VIEW_SHRINK; CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_DEFAULT_TASK_TO_TASK_ANIMATION] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__DEFAULT_TASK_TO_TASK_ANIMATION; CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_DESKTOP_MODE_MOVE_WINDOW_TO_DISPLAY] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__DESKTOP_MODE_MOVE_WINDOW_TO_DISPLAY; + CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_STATUS_BAR_APP_RETURN_TO_CALL_CHIP] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__STATUS_BAR_APP_RETURN_TO_CALL_CHIP; } private Cuj() { @@ -830,6 +842,8 @@ public class Cuj { return "DEFAULT_TASK_TO_TASK_ANIMATION"; case CUJ_DESKTOP_MODE_MOVE_WINDOW_TO_DISPLAY: return "DESKTOP_MODE_MOVE_WINDOW_TO_DISPLAY"; + case CUJ_STATUS_BAR_APP_RETURN_TO_CALL_CHIP: + return "STATUS_BAR_APP_RETURN_TO_CALL_CHIP"; } return "UNKNOWN"; } diff --git a/core/java/com/android/internal/statusbar/DisableStates.aidl b/core/java/com/android/internal/statusbar/DisableStates.aidl new file mode 100644 index 000000000000..fd9882f0f7c2 --- /dev/null +++ b/core/java/com/android/internal/statusbar/DisableStates.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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.statusbar; + +parcelable DisableStates; diff --git a/core/java/com/android/internal/statusbar/DisableStates.java b/core/java/com/android/internal/statusbar/DisableStates.java new file mode 100644 index 000000000000..ca2fd6c03558 --- /dev/null +++ b/core/java/com/android/internal/statusbar/DisableStates.java @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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.statusbar; + +import android.app.StatusBarManager.Disable2Flags; +import android.app.StatusBarManager.DisableFlags; +import android.os.Parcel; +import android.os.Parcelable; +import android.util.Pair; + +import java.util.HashMap; +import java.util.Map; + +/** + * Holds display ids with their disable flags. + */ +public class DisableStates implements Parcelable { + + /** + * A map of display IDs (integers) with corresponding disable flags. + */ + public Map<Integer, Pair<@DisableFlags Integer, @Disable2Flags Integer>> displaysWithStates; + + /** + * Whether the disable state change should be animated. + */ + public boolean animate; + + public DisableStates( + Map<Integer, Pair<@DisableFlags Integer, @Disable2Flags Integer>> displaysWithStates, + boolean animate) { + this.displaysWithStates = displaysWithStates; + this.animate = animate; + } + + public DisableStates( + Map<Integer, Pair<@DisableFlags Integer, @Disable2Flags Integer>> displaysWithStates) { + this(displaysWithStates, true); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(displaysWithStates.size()); // Write the size of the map + for (Map.Entry<Integer, Pair<Integer, Integer>> entry : displaysWithStates.entrySet()) { + dest.writeInt(entry.getKey()); + dest.writeInt(entry.getValue().first); + dest.writeInt(entry.getValue().second); + } + dest.writeBoolean(animate); + } + + /** + * Used to make this class parcelable. + */ + public static final Parcelable.Creator<DisableStates> CREATOR = new Parcelable.Creator<>() { + @Override + public DisableStates createFromParcel(Parcel source) { + int size = source.readInt(); // Read the size of the map + Map<Integer, Pair<Integer, Integer>> displaysWithStates = new HashMap<>(size); + for (int i = 0; i < size; i++) { + int key = source.readInt(); + int first = source.readInt(); + int second = source.readInt(); + displaysWithStates.put(key, new Pair<>(first, second)); + } + final boolean animate = source.readBoolean(); + return new DisableStates(displaysWithStates, animate); + } + + @Override + public DisableStates[] newArray(int size) { + return new DisableStates[size]; + } + }; +} + diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl index 5a180d7358dd..ce9b036f2fd7 100644 --- a/core/java/com/android/internal/statusbar/IStatusBar.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl @@ -32,6 +32,7 @@ import android.os.UserHandle; import android.view.KeyEvent; import android.service.notification.StatusBarNotification; +import com.android.internal.statusbar.DisableStates; import com.android.internal.statusbar.IAddTileResultCallback; import com.android.internal.statusbar.IUndoMediaTransferCallback; import com.android.internal.statusbar.LetterboxDetails; @@ -44,6 +45,7 @@ oneway interface IStatusBar void setIcon(String slot, in StatusBarIcon icon); void removeIcon(String slot); void disable(int displayId, int state1, int state2); + void disableForAllDisplays(in DisableStates disableStates); void animateExpandNotificationsPanel(); void animateExpandSettingsPanel(String subPanel); void animateCollapsePanels(); diff --git a/core/java/com/android/internal/util/LatencyTracker.java b/core/java/com/android/internal/util/LatencyTracker.java index 33794a59fa21..ac2c51cfe888 100644 --- a/core/java/com/android/internal/util/LatencyTracker.java +++ b/core/java/com/android/internal/util/LatencyTracker.java @@ -24,6 +24,7 @@ import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPOR import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_CHECK_CREDENTIAL_UNLOCKED; import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG; import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_DESKTOP_MODE_ENTER_APP_HANDLE_MENU; +import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_DESKTOP_MODE_EXIT_MODE; import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_EXPAND_PANEL; import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_FACE_WAKE_AND_UNLOCK; import static com.android.internal.util.FrameworkStatsLog.UIACTION_LATENCY_REPORTED__ACTION__ACTION_FINGERPRINT_WAKE_AND_UNLOCK; @@ -290,6 +291,16 @@ public class LatencyTracker { */ public static final int ACTION_DESKTOP_MODE_ENTER_APP_HANDLE_MENU = 31; + /** + * Time it takes for the "exit desktop" mode animation to begin after the user provides input. + * <p> + * Starts when the user provides input to exit desktop mode and enter full screen mode for an + * app. This including selecting the full screen button in an app handle's menu, dragging an + * app's window handle to the top of the screen, and using the appropriate keyboard shortcut. + * Ends when the animation to exit desktop mode begins. + */ + public static final int ACTION_DESKTOP_MODE_EXIT_MODE = 32; + private static final int[] ACTIONS_ALL = { ACTION_EXPAND_PANEL, ACTION_TOGGLE_RECENTS, @@ -323,6 +334,7 @@ public class LatencyTracker { ACTION_SHADE_WINDOW_DISPLAY_CHANGE, ACTION_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG, ACTION_DESKTOP_MODE_ENTER_APP_HANDLE_MENU, + ACTION_DESKTOP_MODE_EXIT_MODE, }; /** @hide */ @@ -359,6 +371,7 @@ public class LatencyTracker { ACTION_SHADE_WINDOW_DISPLAY_CHANGE, ACTION_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG, ACTION_DESKTOP_MODE_ENTER_APP_HANDLE_MENU, + ACTION_DESKTOP_MODE_EXIT_MODE, }) @Retention(RetentionPolicy.SOURCE) public @interface Action {} @@ -397,6 +410,7 @@ public class LatencyTracker { UIACTION_LATENCY_REPORTED__ACTION__ACTION_SHADE_WINDOW_DISPLAY_CHANGE, UIACTION_LATENCY_REPORTED__ACTION__ACTION_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG, UIACTION_LATENCY_REPORTED__ACTION__ACTION_DESKTOP_MODE_ENTER_APP_HANDLE_MENU, + UIACTION_LATENCY_REPORTED__ACTION__ACTION_DESKTOP_MODE_EXIT_MODE, }; private final Object mLock = new Object(); @@ -601,6 +615,8 @@ public class LatencyTracker { return "ACTION_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG"; case UIACTION_LATENCY_REPORTED__ACTION__ACTION_DESKTOP_MODE_ENTER_APP_HANDLE_MENU: return "ACTION_DESKTOP_MODE_ENTER_APP_HANDLE_MENU"; + case UIACTION_LATENCY_REPORTED__ACTION__ACTION_DESKTOP_MODE_EXIT_MODE: + return "ACTION_DESKTOP_MODE_EXIT_MODE"; default: throw new IllegalArgumentException("Invalid action"); } 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 766fbf1a80f5..d62538b6d1ed 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java +++ b/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java @@ -73,7 +73,7 @@ public class CoreDocument implements Serializable { // We also keep a more fine-grained BUILD number, exposed as // ID_API_LEVEL = DOCUMENT_API_LEVEL + BUILD - static final float BUILD = 0.7f; + static final float BUILD = 0.8f; private static final boolean UPDATE_VARIABLES_BEFORE_LAYOUT = false; 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 add9d5bae552..20252366e264 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/Operations.java +++ b/core/java/com/android/internal/widget/remotecompose/core/Operations.java @@ -30,6 +30,7 @@ import com.android.internal.widget.remotecompose.core.operations.DataListFloat; import com.android.internal.widget.remotecompose.core.operations.DataListIds; import com.android.internal.widget.remotecompose.core.operations.DataMapIds; import com.android.internal.widget.remotecompose.core.operations.DataMapLookup; +import com.android.internal.widget.remotecompose.core.operations.DebugMessage; 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.DrawBitmapFontText; @@ -231,6 +232,7 @@ public class Operations { public static final int PATH_COMBINE = 175; public static final int HAPTIC_FEEDBACK = 177; public static final int CONDITIONAL_OPERATIONS = 178; + public static final int DEBUG_MESSAGE = 179; ///////////////////////////////////////// ====================== @@ -443,6 +445,7 @@ public class Operations { map.put(PATH_COMBINE, PathCombine::read); map.put(HAPTIC_FEEDBACK, HapticFeedback::read); map.put(CONDITIONAL_OPERATIONS, ConditionalOperations::read); + map.put(DEBUG_MESSAGE, DebugMessage::read); // map.put(ACCESSIBILITY_CUSTOM_ACTION, CoreSemantics::read); } 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 1f026687680f..eb7399afd2b7 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java +++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java @@ -33,6 +33,7 @@ import com.android.internal.widget.remotecompose.core.operations.DataListFloat; import com.android.internal.widget.remotecompose.core.operations.DataListIds; import com.android.internal.widget.remotecompose.core.operations.DataMapIds; import com.android.internal.widget.remotecompose.core.operations.DataMapLookup; +import com.android.internal.widget.remotecompose.core.operations.DebugMessage; 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.DrawBitmapFontText; @@ -1870,6 +1871,46 @@ public class RemoteComposeBuffer { } /** + * Add a scroll modifier + * + * @param direction HORIZONTAL(0) or VERTICAL(1) + * @param positionId the position id as a NaN + */ + public void addModifierScroll(int direction, float positionId) { + float max = this.reserveFloatVariable(); + float notchMax = this.reserveFloatVariable(); + float touchExpressionDirection = + direction != 0 ? RemoteContext.FLOAT_TOUCH_POS_X : RemoteContext.FLOAT_TOUCH_POS_Y; + + ScrollModifierOperation.apply(mBuffer, direction, positionId, max, notchMax); + this.addTouchExpression( + positionId, + 0f, + 0f, + max, + 0f, + 3, + new float[] { + touchExpressionDirection, -1, MUL, + }, + TouchExpression.STOP_GENTLY, + null, + null); + ContainerEnd.apply(mBuffer); + } + + /** + * Add a scroll modifier + * + * @param direction HORIZONTAL(0) or VERTICAL(1) + */ + public void addModifierScroll(int direction) { + float max = this.reserveFloatVariable(); + ScrollModifierOperation.apply(mBuffer, direction, 0f, max, 0f); + ContainerEnd.apply(mBuffer); + } + + /** * Add a background modifier of provided color * * @param color the color of the background @@ -2464,4 +2505,15 @@ public class RemoteComposeBuffer { public void addConditionalOperations(byte type, float a, float b) { ConditionalOperations.apply(mBuffer, type, a, b); } + + /** + * Add a debug message + * + * @param textId text id + * @param value value + * @param flags flags + */ + public void addDebugMessage(int textId, float value, int flags) { + DebugMessage.apply(mBuffer, textId, value, flags); + } } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DebugMessage.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DebugMessage.java new file mode 100644 index 000000000000..c27bd8b577bf --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DebugMessage.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 android.annotation.NonNull; + +import com.android.internal.widget.remotecompose.core.Operation; +import com.android.internal.widget.remotecompose.core.Operations; +import com.android.internal.widget.remotecompose.core.RemoteContext; +import com.android.internal.widget.remotecompose.core.VariableSupport; +import com.android.internal.widget.remotecompose.core.WireBuffer; +import com.android.internal.widget.remotecompose.core.documentation.DocumentationBuilder; +import com.android.internal.widget.remotecompose.core.documentation.DocumentedOperation; + +import java.util.List; + +/** + * This prints debugging message useful for debugging. It should not be use in production documents + */ +public class DebugMessage extends Operation implements VariableSupport { + private static final int OP_CODE = Operations.DEBUG_MESSAGE; + private static final String CLASS_NAME = "DebugMessage"; + int mTextID; + float mFloatValue; + float mOutFloatValue; + int mFlags = 0; + + public DebugMessage(int textID, float value, int flags) { + mTextID = textID; + mFloatValue = value; + mFlags = flags; + } + + @Override + public void updateVariables(@NonNull RemoteContext context) { + System.out.println("Debug message : updateVariables "); + mOutFloatValue = + Float.isNaN(mFloatValue) + ? context.getFloat(Utils.idFromNan(mFloatValue)) + : mFloatValue; + System.out.println( + "Debug message : updateVariables " + + Utils.floatToString(mFloatValue, mOutFloatValue)); + } + + @Override + public void registerListening(@NonNull RemoteContext context) { + System.out.println("Debug message : registerListening "); + + if (Float.isNaN(mFloatValue)) { + System.out.println("Debug message : registerListening " + mFloatValue); + context.listensTo(Utils.idFromNan(mFloatValue), this); + } + } + + @Override + public void write(@NonNull WireBuffer buffer) { + apply(buffer, mTextID, mFloatValue, mFlags); + } + + @NonNull + @Override + public String toString() { + return "DebugMessage " + + mTextID + + ", " + + Utils.floatToString(mFloatValue, mOutFloatValue) + + ", " + + mFlags; + } + + /** + * Read this operation and add it to the list of operations + * + * @param buffer the buffer to read + * @param operations the list of operations that will be added to + */ + public static void read(@NonNull WireBuffer buffer, @NonNull List<Operation> operations) { + int text = buffer.readInt(); + float floatValue = buffer.readFloat(); + int flags = buffer.readInt(); + DebugMessage op = new DebugMessage(text, floatValue, flags); + operations.add(op); + } + + /** + * The name of the class + * + * @return the name + */ + @NonNull + public static String name() { + return CLASS_NAME; + } + + /** + * The OP_CODE for this command + * + * @return the opcode + */ + public static int id() { + return OP_CODE; + } + + /** + * Writes out the operation to the buffer + * + * @param buffer write the command to the buffer + * @param textID id of the text + * @param value value to print + * @param flags flags to print + */ + public static void apply(@NonNull WireBuffer buffer, int textID, float value, int flags) { + buffer.start(OP_CODE); + buffer.writeInt(textID); + buffer.writeFloat(value); + buffer.writeInt(flags); + } + + /** + * Populate the documentation with a description of this operation + * + * @param doc to append the description to. + */ + public static void documentation(@NonNull DocumentationBuilder doc) { + doc.operation("DebugMessage Operations", id(), CLASS_NAME) + .description("Print debugging messages") + .field(DocumentedOperation.INT, "textId", "test to print") + .field(DocumentedOperation.FLOAT, "value", "value of a float to print") + .field(DocumentedOperation.INT, "flags", "print additional information"); + } + + @Override + public void apply(@NonNull RemoteContext context) { + String str = context.getText(mTextID); + System.out.println("Debug message : " + str + " " + mOutFloatValue + " " + mFlags); + } + + @NonNull + @Override + public String deepToString(@NonNull String indent) { + return indent + toString(); + } +} diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/TimeAttribute.java b/core/java/com/android/internal/widget/remotecompose/core/operations/TimeAttribute.java index dee79a45de3d..67d3a6584897 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/TimeAttribute.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TimeAttribute.java @@ -266,10 +266,12 @@ public class TimeAttribute extends PaintOperation { case TIME_FROM_NOW_SEC: case TIME_FROM_ARG_SEC: ctx.loadFloat(mId, (delta) * 1E-3f); + ctx.needsRepaint(); break; case TIME_FROM_ARG_MIN: case TIME_FROM_NOW_MIN: ctx.loadFloat(mId, (float) (delta * 1E-3 / 60)); + ctx.needsRepaint(); break; case TIME_FROM_ARG_HR: case TIME_FROM_NOW_HR: @@ -298,6 +300,7 @@ public class TimeAttribute extends PaintOperation { break; case TIME_FROM_LOAD_SEC: ctx.loadFloat(mId, (value - load_time) * 1E-3f); + ctx.needsRepaint(); break; } } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/TouchExpression.java b/core/java/com/android/internal/widget/remotecompose/core/operations/TouchExpression.java index f24672922367..3e5dff8ad277 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/TouchExpression.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TouchExpression.java @@ -494,7 +494,7 @@ public class TouchExpression extends Operation mTouchUpTime = context.getAnimationTime(); float dest = getStopPosition(value, slope); - float time = mMaxTime * Math.abs(dest - value) / (2 * mMaxVelocity); + float time = Math.min(2, mMaxTime * Math.abs(dest - value) / (2 * mMaxVelocity)); mEasyTouch.config(value, dest, slope, time, mMaxAcceleration, mMaxVelocity, null); mEasingToStop = true; context.needsRepaint(); 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 17f4fc82af5f..e76fb0654df6 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 @@ -42,6 +42,9 @@ import java.util.Set; public class RemoteComposeCanvas extends FrameLayout implements View.OnAttachStateChangeListener { static final boolean USE_VIEW_AREA_CLICK = true; // Use views to represent click areas + static final float DEFAULT_FRAME_RATE = 60f; + static final float POST_TO_NEXT_FRAME_THRESHOLD = 60f; + RemoteComposeDocument mDocument = null; int mTheme = Theme.LIGHT; boolean mInActionDown = false; @@ -53,9 +56,11 @@ public class RemoteComposeCanvas extends FrameLayout implements View.OnAttachSta long mStart = System.nanoTime(); long mLastFrameDelay = 1; - float mMaxFrameRate = 60f; // frames per seconds + float mMaxFrameRate = DEFAULT_FRAME_RATE; // frames per seconds long mMaxFrameDelay = (long) (1000 / mMaxFrameRate); + long mLastFrameCall = System.currentTimeMillis(); + private Choreographer mChoreographer; private Choreographer.FrameCallback mFrameCallback = new Choreographer.FrameCallback() { @@ -100,6 +105,7 @@ public class RemoteComposeCanvas extends FrameLayout implements View.OnAttachSta public void setDocument(RemoteComposeDocument value) { mDocument = value; + mMaxFrameRate = DEFAULT_FRAME_RATE; mDocument.initializeContext(mARContext); mDisable = false; mARContext.setDocLoadTime(); @@ -546,8 +552,25 @@ public class RemoteComposeCanvas extends FrameLayout implements View.OnAttachSta } int nextFrame = mDocument.needsRepaint(); if (nextFrame > 0) { - mLastFrameDelay = Math.max(mMaxFrameDelay, nextFrame); + if (mMaxFrameRate >= POST_TO_NEXT_FRAME_THRESHOLD) { + mLastFrameDelay = nextFrame; + } else { + mLastFrameDelay = Math.max(mMaxFrameDelay, nextFrame); + } if (mChoreographer != null) { + if (mDebug == 1) { + System.err.println( + "RC : POST CHOREOGRAPHER WITH " + + mLastFrameDelay + + " (nextFrame was " + + nextFrame + + ", max delay " + + mMaxFrameDelay + + ", " + + " max framerate is " + + mMaxFrameRate + + ")"); + } mChoreographer.postFrameCallbackDelayed(mFrameCallback, mLastFrameDelay); } if (!mARContext.useChoreographer()) { @@ -567,6 +590,16 @@ public class RemoteComposeCanvas extends FrameLayout implements View.OnAttachSta mDisable = true; invalidate(); } + if (mDebug == 1) { + long frameDelay = System.currentTimeMillis() - mLastFrameCall; + System.err.println( + "RC : Delay since last frame " + + frameDelay + + " ms (" + + (1000f / (float) frameDelay) + + " fps)"); + mLastFrameCall = System.currentTimeMillis(); + } } private void drawDisable(Canvas canvas) { diff --git a/core/res/res/layout/notification_2025_template_collapsed_base.xml b/core/res/res/layout/notification_2025_template_collapsed_base.xml index 57c89b91cff7..201f46025286 100644 --- a/core/res/res/layout/notification_2025_template_collapsed_base.xml +++ b/core/res/res/layout/notification_2025_template_collapsed_base.xml @@ -74,6 +74,7 @@ android:id="@+id/notification_headerless_view_column" android:layout_width="0px" android:layout_height="wrap_content" + android:layout_gravity="center_vertical" android:layout_weight="1" android:layout_marginVertical="@dimen/notification_2025_reduced_margin" android:orientation="vertical" diff --git a/core/res/res/layout/notification_2025_template_collapsed_media.xml b/core/res/res/layout/notification_2025_template_collapsed_media.xml index de82f9feb512..e599de12097a 100644 --- a/core/res/res/layout/notification_2025_template_collapsed_media.xml +++ b/core/res/res/layout/notification_2025_template_collapsed_media.xml @@ -76,6 +76,7 @@ android:id="@+id/notification_headerless_view_column" android:layout_width="0px" android:layout_height="wrap_content" + android:layout_gravity="center_vertical" android:layout_weight="1" android:layout_marginVertical="@dimen/notification_2025_reduced_margin" android:orientation="vertical" diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml index 568468b51f47..1cab0dc1b851 100644 --- a/core/res/res/values-af/strings.xml +++ b/core/res/res/values-af/strings.xml @@ -2273,18 +2273,12 @@ <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Rollees"</string> <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Onderbreek"</string> <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Posisie"</string> - <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) --> - <skip /> + <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Rollees op"</string> + <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Rollees af"</string> + <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Rollees na links"</string> + <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Rollees na regs"</string> + <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Gaan uit Rolleesmodus"</string> + <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Rolleespaneel"</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> is in die BEPERK-groep geplaas"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"het \'n prent gestuur"</string> @@ -2492,8 +2486,7 @@ <string name="profile_label_work_3" msgid="4834572253956798917">"Werk 3"</string> <string name="profile_label_test" msgid="9168641926186071947">"Toets"</string> <string name="profile_label_communal" msgid="8743921499944800427">"Gemeenskaplik"</string> - <!-- no translation found for profile_label_supervising (5649312778545745371) --> - <skip /> + <string name="profile_label_supervising" msgid="5649312778545745371">"Toesighoudend"</string> <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Werkprofiel"</string> <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privaat ruimte"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Kloon"</string> diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml index 20d5336959d2..bda4da077204 100644 --- a/core/res/res/values-am/strings.xml +++ b/core/res/res/values-am/strings.xml @@ -2273,18 +2273,12 @@ <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"ሸብልል"</string> <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"ባለበት አቁም"</string> <string name="accessibility_autoclick_position" msgid="2933660969907663545">"አቀማመጥ"</string> - <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) --> - <skip /> + <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"ወደ ላይ ሸብልል"</string> + <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"ወደ ታች ሸብልል"</string> + <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"ወደ ግራ ሸብልል"</string> + <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"ወደ ቀኝ ሸብልል"</string> + <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"ከሸብልል ሁነታ ውጣ"</string> + <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"የመሸብለል ፓነል"</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> ወደ የRESTRICTED ባልዲ ተከትቷል"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>፦"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"አንድ ምስል ልከዋል"</string> @@ -2492,8 +2486,7 @@ <string name="profile_label_work_3" msgid="4834572253956798917">"ሥራ 3"</string> <string name="profile_label_test" msgid="9168641926186071947">"ሙከራ"</string> <string name="profile_label_communal" msgid="8743921499944800427">"የጋራ"</string> - <!-- no translation found for profile_label_supervising (5649312778545745371) --> - <skip /> + <string name="profile_label_supervising" msgid="5649312778545745371">"ክትትል በማድረግ ላይ"</string> <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"የሥራ መገለጫ"</string> <string name="accessibility_label_private_profile" msgid="1436459319135548969">"የግል ቦታ"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"አባዛ"</string> diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml index ffc144416d91..7daa4b49786d 100644 --- a/core/res/res/values-ar/strings.xml +++ b/core/res/res/values-ar/strings.xml @@ -2490,8 +2490,7 @@ <string name="profile_label_work_3" msgid="4834572253956798917">"ملف العمل 3"</string> <string name="profile_label_test" msgid="9168641926186071947">"ملف شخصي تجريبي"</string> <string name="profile_label_communal" msgid="8743921499944800427">"ملف شخصي مشترك"</string> - <!-- no translation found for profile_label_supervising (5649312778545745371) --> - <skip /> + <string name="profile_label_supervising" msgid="5649312778545745371">"المُشرف"</string> <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"ملف العمل"</string> <string name="accessibility_label_private_profile" msgid="1436459319135548969">"المساحة الخاصة"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"نسخة طبق الأصل"</string> diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml index 5c78cded7df6..5f7ef0078a0f 100644 --- a/core/res/res/values-as/strings.xml +++ b/core/res/res/values-as/strings.xml @@ -2273,18 +2273,12 @@ <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"স্ক্ৰ’ল কৰক"</string> <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"পজ কৰক"</string> <string name="accessibility_autoclick_position" msgid="2933660969907663545">"স্থান"</string> - <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) --> - <skip /> + <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"ওপৰলৈ স্ক্ৰ’ল কৰক"</string> + <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"তললৈ স্ক্ৰ’ল কৰক"</string> + <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"বাওঁফাললৈ স্ক্ৰ’ল কৰক"</string> + <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"সোঁফাললৈ স্ক্ৰ’ল কৰক"</string> + <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"স্ক্ৰ’ল ম’ডৰ পৰা বাহিৰ হওক"</string> + <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"স্ক্ৰ’ল পেনেল"</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g>ক সীমাবদ্ধ বাকেটটোত ৰখা হৈছে"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"এখন প্ৰতিচ্ছবি পঠিয়াইছে"</string> @@ -2492,8 +2486,7 @@ <string name="profile_label_work_3" msgid="4834572253956798917">"কৰ্মস্থান ৩"</string> <string name="profile_label_test" msgid="9168641926186071947">"পৰীক্ষা"</string> <string name="profile_label_communal" msgid="8743921499944800427">"শ্বেয়াৰ কৰা"</string> - <!-- no translation found for profile_label_supervising (5649312778545745371) --> - <skip /> + <string name="profile_label_supervising" msgid="5649312778545745371">"তদাৰক কৰি থকা হৈছে"</string> <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"কৰ্মস্থানৰ প্ৰ’ফাইল"</string> <string name="accessibility_label_private_profile" msgid="1436459319135548969">"প্ৰাইভেট স্পে’চ"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"ক্ল’ন"</string> diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml index c8b500be22c6..bc8a3f337f10 100644 --- a/core/res/res/values-az/strings.xml +++ b/core/res/res/values-az/strings.xml @@ -2486,8 +2486,7 @@ <string name="profile_label_work_3" msgid="4834572253956798917">"İş 3"</string> <string name="profile_label_test" msgid="9168641926186071947">"Test"</string> <string name="profile_label_communal" msgid="8743921499944800427">"Kommunal"</string> - <!-- no translation found for profile_label_supervising (5649312778545745371) --> - <skip /> + <string name="profile_label_supervising" msgid="5649312778545745371">"Nəzarət edilir"</string> <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"İş profili"</string> <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Məxfi sahə"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string> diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml index d1889b84e90f..8b742006075f 100644 --- a/core/res/res/values-b+sr+Latn/strings.xml +++ b/core/res/res/values-b+sr+Latn/strings.xml @@ -2274,18 +2274,12 @@ <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Skrolujte"</string> <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Pauziraj"</string> <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Pozicija"</string> - <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) --> - <skip /> + <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Skroluj nagore"</string> + <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Skroluj nadole"</string> + <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Skroluj ulevo"</string> + <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Skroluj udesno"</string> + <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Izađi iz režima skrolovanja"</string> + <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Okno za skrolovanje"</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"Paket <xliff:g id="PACKAGE_NAME">%1$s</xliff:g> je dodat u segment OGRANIČENO"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"je poslao/la sliku"</string> @@ -2493,8 +2487,7 @@ <string name="profile_label_work_3" msgid="4834572253956798917">"Posao 3"</string> <string name="profile_label_test" msgid="9168641926186071947">"Test"</string> <string name="profile_label_communal" msgid="8743921499944800427">"Zajedničko"</string> - <!-- no translation found for profile_label_supervising (5649312778545745371) --> - <skip /> + <string name="profile_label_supervising" msgid="5649312778545745371">"Nadzire se"</string> <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Poslovni profil"</string> <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privatan prostor"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klonirano"</string> diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml index 68fe703644a9..cbdbc23e0028 100644 --- a/core/res/res/values-be/strings.xml +++ b/core/res/res/values-be/strings.xml @@ -2275,18 +2275,12 @@ <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Гартанне"</string> <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Прыпыніць"</string> <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Пазіцыя"</string> - <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) --> - <skip /> + <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Прагартаць уверх"</string> + <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Прагартаць уніз"</string> + <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Прагартаць улева"</string> + <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Прагартаць управа"</string> + <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Выйсці з рэжыму гартання"</string> + <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Панэль прагортвання"</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"Пакет \"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g>\" дададзены ў АБМЕЖАВАНУЮ групу"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"адпраўлены відарыс"</string> @@ -2494,8 +2488,7 @@ <string name="profile_label_work_3" msgid="4834572253956798917">"Працоўны 3"</string> <string name="profile_label_test" msgid="9168641926186071947">"Тэставы"</string> <string name="profile_label_communal" msgid="8743921499944800427">"Супольны"</string> - <!-- no translation found for profile_label_supervising (5649312778545745371) --> - <skip /> + <string name="profile_label_supervising" msgid="5649312778545745371">"Бацькоўскі кантроль"</string> <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Працоўны профіль"</string> <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Прыватная прастора"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Клон"</string> diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml index 67e711f115df..a1140b930ec1 100644 --- a/core/res/res/values-bg/strings.xml +++ b/core/res/res/values-bg/strings.xml @@ -2486,8 +2486,7 @@ <string name="profile_label_work_3" msgid="4834572253956798917">"Служебни 3"</string> <string name="profile_label_test" msgid="9168641926186071947">"Тестване"</string> <string name="profile_label_communal" msgid="8743921499944800427">"Общи"</string> - <!-- no translation found for profile_label_supervising (5649312778545745371) --> - <skip /> + <string name="profile_label_supervising" msgid="5649312778545745371">"Контролиране"</string> <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Служебен потребителски профил"</string> <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Частно пространство"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Клониране"</string> diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml index 12bf5dab6561..426c711adbf1 100644 --- a/core/res/res/values-bn/strings.xml +++ b/core/res/res/values-bn/strings.xml @@ -2273,18 +2273,12 @@ <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"স্ক্রল করুন"</string> <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"পজ করুন"</string> <string name="accessibility_autoclick_position" msgid="2933660969907663545">"পজিশন"</string> - <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) --> - <skip /> + <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"উপর দিকে স্ক্রল করুন"</string> + <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"নিচে স্ক্রল করুন"</string> + <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"বাঁদিকে স্ক্রল করুন"</string> + <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"ডানদিকে স্ক্রল করুন"</string> + <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"স্ক্রল মোড থেকে বেরিয়ে আসুন"</string> + <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"স্ক্রল প্যানেল"</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> সীমাবদ্ধ গ্রুপে অন্তর্ভুক্ত করা হয়েছে"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"একটি ছবি পাঠানো হয়েছে"</string> @@ -2492,8 +2486,7 @@ <string name="profile_label_work_3" msgid="4834572253956798917">"৩য় অফিস"</string> <string name="profile_label_test" msgid="9168641926186071947">"পরীক্ষা"</string> <string name="profile_label_communal" msgid="8743921499944800427">"কমিউনাল"</string> - <!-- no translation found for profile_label_supervising (5649312778545745371) --> - <skip /> + <string name="profile_label_supervising" msgid="5649312778545745371">"তত্ত্বাবধান করা"</string> <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"অফিস প্রোফাইল"</string> <string name="accessibility_label_private_profile" msgid="1436459319135548969">"প্রাইভেট স্পেস"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"ক্লোন"</string> diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml index 441d51461f08..947f6bacafc7 100644 --- a/core/res/res/values-bs/strings.xml +++ b/core/res/res/values-bs/strings.xml @@ -2274,18 +2274,12 @@ <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Klizanje"</string> <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Pauziraj"</string> <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Položaj"</string> - <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) --> - <skip /> + <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Klizanje nagore"</string> + <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Klizanje nadolje"</string> + <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Klizanje ulijevo"</string> + <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Klizanje udesno"</string> + <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Izlaz iz načina rada za klizanje"</string> + <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Ploča za klizanje"</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"Paket <xliff:g id="PACKAGE_NAME">%1$s</xliff:g> je stavljen u odjeljak OGRANIČENO"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"je poslao/la sliku"</string> @@ -2493,8 +2487,7 @@ <string name="profile_label_work_3" msgid="4834572253956798917">"3. poslovno"</string> <string name="profile_label_test" msgid="9168641926186071947">"Testno"</string> <string name="profile_label_communal" msgid="8743921499944800427">"Opće"</string> - <!-- no translation found for profile_label_supervising (5649312778545745371) --> - <skip /> + <string name="profile_label_supervising" msgid="5649312778545745371">"Nadzor"</string> <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Radni profil"</string> <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privatni prostor"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string> diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml index efde1506e7be..8dcf67195842 100644 --- a/core/res/res/values-ca/strings.xml +++ b/core/res/res/values-ca/strings.xml @@ -2274,18 +2274,12 @@ <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Desplaça"</string> <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Posa en pausa"</string> <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Posició"</string> - <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) --> - <skip /> + <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Desplaça\'t cap amunt"</string> + <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Desplaça\'t cap avall"</string> + <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Desplaça\'t cap a l\'esquerra"</string> + <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Desplaça\'t cap a la dreta"</string> + <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Surt del mode de desplaçament"</string> + <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Tauler de desplaçament"</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> s\'ha transferit al segment RESTRINGIT"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"ha enviat una imatge"</string> @@ -2493,8 +2487,7 @@ <string name="profile_label_work_3" msgid="4834572253956798917">"Treball 3"</string> <string name="profile_label_test" msgid="9168641926186071947">"Prova"</string> <string name="profile_label_communal" msgid="8743921499944800427">"Compartit"</string> - <!-- no translation found for profile_label_supervising (5649312778545745371) --> - <skip /> + <string name="profile_label_supervising" msgid="5649312778545745371">"En supervisió"</string> <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Perfil de treball"</string> <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Espai privat"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clon"</string> diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml index 59de93743290..61d9249e7774 100644 --- a/core/res/res/values-cs/strings.xml +++ b/core/res/res/values-cs/strings.xml @@ -2275,18 +2275,12 @@ <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Posunutí"</string> <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Pozastavit"</string> <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Pozice"</string> - <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) --> - <skip /> + <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Posunout nahoru"</string> + <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Posunout dolů"</string> + <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Posunout doleva"</string> + <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Posunout doprava"</string> + <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Ukončit režim posouvání"</string> + <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Panel posouvání"</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"Balíček <xliff:g id="PACKAGE_NAME">%1$s</xliff:g> byl vložen do sekce OMEZENO"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"posílá obrázek"</string> @@ -2494,8 +2488,7 @@ <string name="profile_label_work_3" msgid="4834572253956798917">"Práce 3"</string> <string name="profile_label_test" msgid="9168641926186071947">"Test"</string> <string name="profile_label_communal" msgid="8743921499944800427">"Komunální"</string> - <!-- no translation found for profile_label_supervising (5649312778545745371) --> - <skip /> + <string name="profile_label_supervising" msgid="5649312778545745371">"Dohled"</string> <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Pracovní profil"</string> <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Soukromý prostor"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string> diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml index 1038ff0bcf8f..8d8918fbf16e 100644 --- a/core/res/res/values-da/strings.xml +++ b/core/res/res/values-da/strings.xml @@ -2273,18 +2273,12 @@ <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Rul"</string> <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Sæt på pause"</string> <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Placering"</string> - <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) --> - <skip /> + <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Rul op"</string> + <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Rul ned"</string> + <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Rul til venstre"</string> + <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Rul til højre"</string> + <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Afslut rulletilstand"</string> + <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Rullepanel"</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> er blevet placeret i samlingen BEGRÆNSET"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"sendte et billede"</string> @@ -2492,8 +2486,7 @@ <string name="profile_label_work_3" msgid="4834572253956798917">"Arbejde 3"</string> <string name="profile_label_test" msgid="9168641926186071947">"Test"</string> <string name="profile_label_communal" msgid="8743921499944800427">"Fælles"</string> - <!-- no translation found for profile_label_supervising (5649312778545745371) --> - <skip /> + <string name="profile_label_supervising" msgid="5649312778545745371">"Forældrestyring"</string> <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Arbejdsprofil"</string> <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privat område"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string> diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml index c13dacad4dea..15edddee0dbe 100644 --- a/core/res/res/values-de/strings.xml +++ b/core/res/res/values-de/strings.xml @@ -2273,18 +2273,12 @@ <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Scrollen"</string> <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Pausieren"</string> <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Position"</string> - <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) --> - <skip /> + <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Nach oben scrollen"</string> + <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Nach unten scrollen"</string> + <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Nach links scrollen"</string> + <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Nach rechts scrollen"</string> + <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Scrollmodus beenden"</string> + <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Bildlaufleiste"</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> wurde in den BESCHRÄNKT-Bucket gelegt"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"hat ein Bild gesendet"</string> @@ -2492,8 +2486,7 @@ <string name="profile_label_work_3" msgid="4834572253956798917">"Geschäftlich 3"</string> <string name="profile_label_test" msgid="9168641926186071947">"Test"</string> <string name="profile_label_communal" msgid="8743921499944800427">"Gemeinsam genutzt"</string> - <!-- no translation found for profile_label_supervising (5649312778545745371) --> - <skip /> + <string name="profile_label_supervising" msgid="5649312778545745371">"Elternaufsicht"</string> <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Arbeitsprofil"</string> <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Vertrauliches Profil"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string> diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml index 50b06dfbc8b6..5d83f7890d6b 100644 --- a/core/res/res/values-el/strings.xml +++ b/core/res/res/values-el/strings.xml @@ -2273,18 +2273,12 @@ <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Κύλιση"</string> <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Παύση"</string> <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Θέση"</string> - <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) --> - <skip /> + <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Κύλιση προς τα επάνω"</string> + <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Κύλιση προς τα κάτω"</string> + <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Κύλιση προς τα αριστερά"</string> + <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Κύλιση προς τα δεξιά"</string> + <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Έξοδος από τη λειτουργία κύλισης"</string> + <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Πλαίσιο κύλισης"</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"Το πακέτο <xliff:g id="PACKAGE_NAME">%1$s</xliff:g> τοποθετήθηκε στον κάδο ΠΕΡΙΟΡΙΣΜΕΝΗΣ ΠΡΟΣΒΑΣΗΣ."</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"έστειλε μια εικόνα"</string> @@ -2492,8 +2486,7 @@ <string name="profile_label_work_3" msgid="4834572253956798917">"Εργασία 3"</string> <string name="profile_label_test" msgid="9168641926186071947">"Δοκιμή"</string> <string name="profile_label_communal" msgid="8743921499944800427">"Κοινόχρηστο"</string> - <!-- no translation found for profile_label_supervising (5649312778545745371) --> - <skip /> + <string name="profile_label_supervising" msgid="5649312778545745371">"Επίβλεψη"</string> <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Προφίλ εργασίας"</string> <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Ιδιωτικός χώρος"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Κλώνος"</string> diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml index 74462e996b5b..532fee688acd 100644 --- a/core/res/res/values-en-rAU/strings.xml +++ b/core/res/res/values-en-rAU/strings.xml @@ -2273,18 +2273,12 @@ <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Scroll"</string> <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Pause"</string> <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Position"</string> - <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) --> - <skip /> + <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Scroll up"</string> + <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Scroll down"</string> + <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Scroll left"</string> + <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Scroll right"</string> + <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Exit scroll mode"</string> + <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Scroll panel"</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> has been put into the RESTRICTED bucket"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"sent an image"</string> @@ -2492,8 +2486,7 @@ <string name="profile_label_work_3" msgid="4834572253956798917">"Work 3"</string> <string name="profile_label_test" msgid="9168641926186071947">"Test"</string> <string name="profile_label_communal" msgid="8743921499944800427">"Communal"</string> - <!-- no translation found for profile_label_supervising (5649312778545745371) --> - <skip /> + <string name="profile_label_supervising" msgid="5649312778545745371">"Supervising"</string> <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Work profile"</string> <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Private space"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string> diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml index 9fa268338d4a..c079a4804057 100644 --- a/core/res/res/values-en-rGB/strings.xml +++ b/core/res/res/values-en-rGB/strings.xml @@ -2273,18 +2273,12 @@ <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Scroll"</string> <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Pause"</string> <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Position"</string> - <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) --> - <skip /> + <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Scroll up"</string> + <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Scroll down"</string> + <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Scroll left"</string> + <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Scroll right"</string> + <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Exit scroll mode"</string> + <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Scroll panel"</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> has been put into the RESTRICTED bucket"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"sent an image"</string> @@ -2492,8 +2486,7 @@ <string name="profile_label_work_3" msgid="4834572253956798917">"Work 3"</string> <string name="profile_label_test" msgid="9168641926186071947">"Test"</string> <string name="profile_label_communal" msgid="8743921499944800427">"Communal"</string> - <!-- no translation found for profile_label_supervising (5649312778545745371) --> - <skip /> + <string name="profile_label_supervising" msgid="5649312778545745371">"Supervising"</string> <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Work profile"</string> <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Private space"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string> diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml index e162a6094719..62daeffa9043 100644 --- a/core/res/res/values-en-rIN/strings.xml +++ b/core/res/res/values-en-rIN/strings.xml @@ -2273,18 +2273,12 @@ <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Scroll"</string> <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Pause"</string> <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Position"</string> - <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) --> - <skip /> + <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Scroll up"</string> + <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Scroll down"</string> + <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Scroll left"</string> + <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Scroll right"</string> + <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Exit scroll mode"</string> + <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Scroll panel"</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> has been put into the RESTRICTED bucket"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"sent an image"</string> @@ -2492,8 +2486,7 @@ <string name="profile_label_work_3" msgid="4834572253956798917">"Work 3"</string> <string name="profile_label_test" msgid="9168641926186071947">"Test"</string> <string name="profile_label_communal" msgid="8743921499944800427">"Communal"</string> - <!-- no translation found for profile_label_supervising (5649312778545745371) --> - <skip /> + <string name="profile_label_supervising" msgid="5649312778545745371">"Supervising"</string> <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Work profile"</string> <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Private space"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string> diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml index 330039740ea1..b5a5e8f0deeb 100644 --- a/core/res/res/values-es/strings.xml +++ b/core/res/res/values-es/strings.xml @@ -2274,18 +2274,12 @@ <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Desplazarse"</string> <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Pausar"</string> <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Posición"</string> - <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) --> - <skip /> + <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Desplazarse hacia arriba"</string> + <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Desplazarse hacia abajo"</string> + <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Desplazarse a la izquierda"</string> + <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Desplazarse a la derecha"</string> + <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Salir del modo de desplazamiento"</string> + <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Panel de desplazamiento"</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> se ha incluido en el grupo de restringidos"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"ha enviado una imagen"</string> @@ -2493,8 +2487,7 @@ <string name="profile_label_work_3" msgid="4834572253956798917">"Trabajo 3"</string> <string name="profile_label_test" msgid="9168641926186071947">"Prueba"</string> <string name="profile_label_communal" msgid="8743921499944800427">"Común"</string> - <!-- no translation found for profile_label_supervising (5649312778545745371) --> - <skip /> + <string name="profile_label_supervising" msgid="5649312778545745371">"Supervisando"</string> <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Perfil de trabajo"</string> <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Espacio privado"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clon"</string> diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml index c172de8671c9..99f9791e6ff1 100644 --- a/core/res/res/values-et/strings.xml +++ b/core/res/res/values-et/strings.xml @@ -2273,18 +2273,12 @@ <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Keri"</string> <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Peata"</string> <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Asukoht"</string> - <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) --> - <skip /> + <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Keri üles"</string> + <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Keri alla"</string> + <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Keri vasakule"</string> + <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Keri paremale"</string> + <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Välju kerimisrežiimist"</string> + <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Keri paneelil"</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> on lisatud salve PIIRANGUTEGA"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"saatis kujutise"</string> @@ -2492,8 +2486,7 @@ <string name="profile_label_work_3" msgid="4834572253956798917">"Töö 3"</string> <string name="profile_label_test" msgid="9168641926186071947">"Test"</string> <string name="profile_label_communal" msgid="8743921499944800427">"Jagatud"</string> - <!-- no translation found for profile_label_supervising (5649312778545745371) --> - <skip /> + <string name="profile_label_supervising" msgid="5649312778545745371">"Järelevalve"</string> <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Tööprofiil"</string> <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privaatne ruum"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Kloon"</string> diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml index 24dbe4bb580c..7a49905cb250 100644 --- a/core/res/res/values-eu/strings.xml +++ b/core/res/res/values-eu/strings.xml @@ -2492,8 +2492,7 @@ <string name="profile_label_work_3" msgid="4834572253956798917">"Lanekoa 3"</string> <string name="profile_label_test" msgid="9168641926186071947">"Probakoa"</string> <string name="profile_label_communal" msgid="8743921499944800427">"Partekatua"</string> - <!-- no translation found for profile_label_supervising (5649312778545745371) --> - <skip /> + <string name="profile_label_supervising" msgid="5649312778545745371">"Gainbegiratzea"</string> <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Laneko profila"</string> <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Eremu pribatua"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klona"</string> diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml index f31bb1044c96..820a80c1f411 100644 --- a/core/res/res/values-fa/strings.xml +++ b/core/res/res/values-fa/strings.xml @@ -2486,8 +2486,7 @@ <string name="profile_label_work_3" msgid="4834572253956798917">"کار ۳"</string> <string name="profile_label_test" msgid="9168641926186071947">"آزمایش"</string> <string name="profile_label_communal" msgid="8743921499944800427">"عمومی"</string> - <!-- no translation found for profile_label_supervising (5649312778545745371) --> - <skip /> + <string name="profile_label_supervising" msgid="5649312778545745371">"نظارت"</string> <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"نمایه کاری"</string> <string name="accessibility_label_private_profile" msgid="1436459319135548969">"فضای خصوصی"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"همسانهسازی"</string> diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml index f1a5fbcdbcc4..dce063d0b033 100644 --- a/core/res/res/values-fi/strings.xml +++ b/core/res/res/values-fi/strings.xml @@ -2273,18 +2273,12 @@ <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Vieritä"</string> <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Keskeytä"</string> <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Sijainti"</string> - <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) --> - <skip /> + <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Vieritys ylös"</string> + <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Vieritä alas"</string> + <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Vieritä vasemmalle"</string> + <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Vieritä oikealle"</string> + <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Poistu vieritystilasta"</string> + <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Vierityspaneeli"</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> on nyt rajoitettujen ryhmässä"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"lähetti kuvan"</string> @@ -2492,8 +2486,7 @@ <string name="profile_label_work_3" msgid="4834572253956798917">"Työ 3"</string> <string name="profile_label_test" msgid="9168641926186071947">"Testi"</string> <string name="profile_label_communal" msgid="8743921499944800427">"Jaettu"</string> - <!-- no translation found for profile_label_supervising (5649312778545745371) --> - <skip /> + <string name="profile_label_supervising" msgid="5649312778545745371">"Valvotaan"</string> <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Työprofiili"</string> <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Yksityinen tila"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klooni"</string> diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml index a7b12891d7b1..127e6fcc97f7 100644 --- a/core/res/res/values-fr-rCA/strings.xml +++ b/core/res/res/values-fr-rCA/strings.xml @@ -2487,8 +2487,7 @@ <string name="profile_label_work_3" msgid="4834572253956798917">"Professionnel 3"</string> <string name="profile_label_test" msgid="9168641926186071947">"Test"</string> <string name="profile_label_communal" msgid="8743921499944800427">"Commun"</string> - <!-- no translation found for profile_label_supervising (5649312778545745371) --> - <skip /> + <string name="profile_label_supervising" msgid="5649312778545745371">"Supervision"</string> <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Profil professionnel"</string> <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Espace privé"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string> diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml index 213c25696ec0..a30c414038af 100644 --- a/core/res/res/values-fr/strings.xml +++ b/core/res/res/values-fr/strings.xml @@ -2274,18 +2274,12 @@ <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Faire défiler"</string> <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Pause"</string> <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Position"</string> - <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) --> - <skip /> + <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Faire défiler vers le haut"</string> + <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Faire défiler vers le bas"</string> + <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Faire défiler vers la gauche"</string> + <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Faire défiler vers la droite"</string> + <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Quitter le mode défilement"</string> + <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Panneau de défilement"</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> a été placé dans le bucket RESTRICTED"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g> :"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"a envoyé une image"</string> @@ -2493,8 +2487,7 @@ <string name="profile_label_work_3" msgid="4834572253956798917">"Professionnel 3"</string> <string name="profile_label_test" msgid="9168641926186071947">"Test"</string> <string name="profile_label_communal" msgid="8743921499944800427">"Commun"</string> - <!-- no translation found for profile_label_supervising (5649312778545745371) --> - <skip /> + <string name="profile_label_supervising" msgid="5649312778545745371">"Supervision"</string> <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Profil professionnel"</string> <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Espace privé"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string> diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml index 8c265b02b93e..d79e3c63e084 100644 --- a/core/res/res/values-gl/strings.xml +++ b/core/res/res/values-gl/strings.xml @@ -2273,18 +2273,12 @@ <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Desprazar"</string> <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Pausa"</string> <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Posición"</string> - <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) --> - <skip /> + <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Desprazarse cara arriba"</string> + <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Desprazarse cara abaixo"</string> + <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Desprazar cara á esquerda"</string> + <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Desprazar cara á dereita"</string> + <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Saír do modo de desprazamento"</string> + <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Panel de desprazamento"</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> incluíuse no grupo RESTRINXIDO"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"enviouse unha imaxe"</string> @@ -2492,8 +2486,7 @@ <string name="profile_label_work_3" msgid="4834572253956798917">"Traballo 3"</string> <string name="profile_label_test" msgid="9168641926186071947">"Proba"</string> <string name="profile_label_communal" msgid="8743921499944800427">"Compartido"</string> - <!-- no translation found for profile_label_supervising (5649312778545745371) --> - <skip /> + <string name="profile_label_supervising" msgid="5649312778545745371">"Supervisión"</string> <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Perfil de traballo"</string> <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Espazo privado"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clonado"</string> diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml index c4da34989482..6b1e2840522a 100644 --- a/core/res/res/values-gu/strings.xml +++ b/core/res/res/values-gu/strings.xml @@ -1803,8 +1803,7 @@ <string name="one_handed_mode_feature_name" msgid="2334330034828094891">"એક-હાથે વાપરો મોડ"</string> <string name="reduce_bright_colors_feature_name" msgid="3222994553174604132">"એક્સ્ટ્રા ડિમ"</string> <string name="hearing_aids_feature_name" msgid="1125892105105852542">"સાંભળવામાં સહાય કરતા ડિવાઇસ"</string> - <!-- no translation found for autoclick_feature_name (8149248738736949630) --> - <skip /> + <string name="autoclick_feature_name" msgid="8149248738736949630">"ઑટોક્લિક"</string> <string name="hearing_device_status_disconnected" msgid="497547752953543832">"ડિસ્કનેક્ટેડ છે"</string> <string name="hearing_device_status_connected" msgid="2149385149669918764">"કનેક્ટેડ છે"</string> <string name="hearing_device_status_active" msgid="4770378695482566032">"સક્રિય"</string> @@ -2274,18 +2273,12 @@ <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"સ્ક્રોલ કરો"</string> <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"થોભાવો"</string> <string name="accessibility_autoclick_position" msgid="2933660969907663545">"સ્થિતિ"</string> - <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) --> - <skip /> + <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"ઉપર સ્ક્રોલ કરો"</string> + <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"નીચે સ્ક્રોલ કરો"</string> + <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"ડાબે સ્ક્રોલ કરો"</string> + <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"જમણે સ્ક્રોલ કરો"</string> + <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"સ્ક્રોલ મોડમાંથી બહાર નીકળો"</string> + <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"સ્ક્રોલ પૅનલ"</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g>ને પ્રતિબંધિત સમૂહમાં મૂકવામાં આવ્યું છે"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"છબી મોકલી"</string> diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml index 01e5c7786cb7..b8be4057d480 100644 --- a/core/res/res/values-hi/strings.xml +++ b/core/res/res/values-hi/strings.xml @@ -2273,18 +2273,12 @@ <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"स्क्रोल करें"</string> <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"रोकें"</string> <string name="accessibility_autoclick_position" msgid="2933660969907663545">"पोज़िशन"</string> - <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) --> - <skip /> + <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"ऊपर की ओर स्क्रोल करें"</string> + <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"नीचे की ओर स्क्रोल करें"</string> + <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"बाईं ओर स्क्रोल करें"</string> + <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"दाईं ओर स्क्रोल करें"</string> + <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"स्क्रोल मोड को बंद करें"</string> + <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"पैनल को स्क्रोल करें"</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> को प्रतिबंधित बकेट में रखा गया है"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"एक इमेज भेजी गई"</string> @@ -2492,8 +2486,7 @@ <string name="profile_label_work_3" msgid="4834572253956798917">"ऑफ़िस 3"</string> <string name="profile_label_test" msgid="9168641926186071947">"टेस्ट"</string> <string name="profile_label_communal" msgid="8743921499944800427">"कम्यूनिटी"</string> - <!-- no translation found for profile_label_supervising (5649312778545745371) --> - <skip /> + <string name="profile_label_supervising" msgid="5649312778545745371">"निगरानी की जा रही है"</string> <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"वर्क प्रोफ़ाइल"</string> <string name="accessibility_label_private_profile" msgid="1436459319135548969">"प्राइवेट स्पेस"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"क्लोन"</string> diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml index 826b860dfc2b..77c9a6049f0d 100644 --- a/core/res/res/values-hr/strings.xml +++ b/core/res/res/values-hr/strings.xml @@ -2274,18 +2274,12 @@ <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Pomakni se"</string> <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Pauziraj"</string> <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Pozicija"</string> - <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) --> - <skip /> + <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Pomakni prema gore"</string> + <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Pomakni prema dolje"</string> + <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Pomakni ulijevo"</string> + <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Pomakni udesno"</string> + <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Izađi iz načina pomicanja"</string> + <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Ploča za pomicanje"</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"Paket <xliff:g id="PACKAGE_NAME">%1$s</xliff:g> premješten je u spremnik OGRANIČENO"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"šalje sliku"</string> @@ -2493,8 +2487,7 @@ <string name="profile_label_work_3" msgid="4834572253956798917">"Posao 3"</string> <string name="profile_label_test" msgid="9168641926186071947">"Test"</string> <string name="profile_label_communal" msgid="8743921499944800427">"Zajedničko"</string> - <!-- no translation found for profile_label_supervising (5649312778545745371) --> - <skip /> + <string name="profile_label_supervising" msgid="5649312778545745371">"Nadzire se"</string> <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Radni profil"</string> <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privatni prostor"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string> diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml index 1519326cdb6b..24babe3305e5 100644 --- a/core/res/res/values-hu/strings.xml +++ b/core/res/res/values-hu/strings.xml @@ -2273,18 +2273,12 @@ <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Görgetés"</string> <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Szüneteltetés"</string> <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Pozíció"</string> - <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) --> - <skip /> + <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Görgetés felfelé"</string> + <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Görgetés lefelé"</string> + <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Görgetés balra"</string> + <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Görgetés jobbra"</string> + <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Kilépés a görgetési módból"</string> + <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Görgetési panel"</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"A következő csomag a KORLÁTOZOTT csoportba került: <xliff:g id="PACKAGE_NAME">%1$s</xliff:g>"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"képet küldött"</string> @@ -2492,8 +2486,7 @@ <string name="profile_label_work_3" msgid="4834572253956798917">"3. munkahelyi"</string> <string name="profile_label_test" msgid="9168641926186071947">"Teszt"</string> <string name="profile_label_communal" msgid="8743921499944800427">"Közös"</string> - <!-- no translation found for profile_label_supervising (5649312778545745371) --> - <skip /> + <string name="profile_label_supervising" msgid="5649312778545745371">"Aktív felügyelet"</string> <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Munkaprofil"</string> <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privát terület"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klón"</string> diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml index 2fd68c11ba7b..7f78454e950c 100644 --- a/core/res/res/values-hy/strings.xml +++ b/core/res/res/values-hy/strings.xml @@ -2486,8 +2486,7 @@ <string name="profile_label_work_3" msgid="4834572253956798917">"Աշխատանքային 3"</string> <string name="profile_label_test" msgid="9168641926186071947">"Փորձնական"</string> <string name="profile_label_communal" msgid="8743921499944800427">"Ընդհանուր"</string> - <!-- no translation found for profile_label_supervising (5649312778545745371) --> - <skip /> + <string name="profile_label_supervising" msgid="5649312778545745371">"Վերահսկում"</string> <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Աշխատանքային պրոֆիլ"</string> <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Մասնավոր տարածք"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Կլոն"</string> diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml index 6e686b4263dd..fbbf2855565d 100644 --- a/core/res/res/values-in/strings.xml +++ b/core/res/res/values-in/strings.xml @@ -2273,18 +2273,12 @@ <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Scroll"</string> <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Jeda"</string> <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Posisi"</string> - <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) --> - <skip /> + <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Scroll ke Atas"</string> + <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Scroll ke Bawah"</string> + <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Scroll ke Kiri"</string> + <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Scroll ke Kanan"</string> + <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Keluar dari Mode Scroll"</string> + <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Panel Scroll"</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> telah dimasukkan ke dalam bucket DIBATASI"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"mengirim gambar"</string> @@ -2492,8 +2486,7 @@ <string name="profile_label_work_3" msgid="4834572253956798917">"Kerja 3"</string> <string name="profile_label_test" msgid="9168641926186071947">"Pengujian"</string> <string name="profile_label_communal" msgid="8743921499944800427">"Umum"</string> - <!-- no translation found for profile_label_supervising (5649312778545745371) --> - <skip /> + <string name="profile_label_supervising" msgid="5649312778545745371">"Mengawasi"</string> <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Profil kerja"</string> <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Ruang privasi"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string> diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml index 35b102e42933..d148f5d6de08 100644 --- a/core/res/res/values-is/strings.xml +++ b/core/res/res/values-is/strings.xml @@ -2273,18 +2273,12 @@ <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Fletta"</string> <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Hlé"</string> <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Staðsetning"</string> - <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) --> - <skip /> + <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Fletta upp"</string> + <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Fletta niður"</string> + <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Fletta til vinstri"</string> + <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Fletta til hægri"</string> + <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Loka flettistillingu"</string> + <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Flettisvæði"</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> var sett í flokkinn TAKMARKAÐ"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"sendi mynd"</string> diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml index 28aee0fbba0c..7fe1bdc6762d 100644 --- a/core/res/res/values-it/strings.xml +++ b/core/res/res/values-it/strings.xml @@ -2274,18 +2274,12 @@ <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Scorri"</string> <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Metti in pausa"</string> <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Posizione"</string> - <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) --> - <skip /> + <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Scorri verso l\'alto"</string> + <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Scorri verso il basso"</string> + <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Scorri verso sinistra"</string> + <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Scorri verso destra"</string> + <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Disattiva la modalità di scorrimento"</string> + <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Riquadro di scorrimento"</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> è stato inserito nel bucket RESTRICTED"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"ha inviato un\'immagine"</string> diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml index 5eadbd413f2c..c32bcdda3692 100644 --- a/core/res/res/values-iw/strings.xml +++ b/core/res/res/values-iw/strings.xml @@ -2274,18 +2274,12 @@ <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"גלילה"</string> <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"השהיה"</string> <string name="accessibility_autoclick_position" msgid="2933660969907663545">"מיקום"</string> - <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) --> - <skip /> + <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"גלילה למעלה"</string> + <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"גלילה למטה"</string> + <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"גלילה שמאלה"</string> + <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"גלילה ימינה"</string> + <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"יציאה ממצב גלילה"</string> + <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"גלילה בחלונית"</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> התווספה לקטגוריה \'מוגבל\'"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"נשלחה תמונה"</string> @@ -2493,8 +2487,7 @@ <string name="profile_label_work_3" msgid="4834572253956798917">"פרופיל עבודה 3"</string> <string name="profile_label_test" msgid="9168641926186071947">"בדיקה"</string> <string name="profile_label_communal" msgid="8743921499944800427">"שיתופי"</string> - <!-- no translation found for profile_label_supervising (5649312778545745371) --> - <skip /> + <string name="profile_label_supervising" msgid="5649312778545745371">"פרופיל מפקח"</string> <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"פרופיל העבודה"</string> <string name="accessibility_label_private_profile" msgid="1436459319135548969">"המרחב הפרטי"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"שכפול"</string> diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml index 24e45df6d1ac..97e550d9e4db 100644 --- a/core/res/res/values-kk/strings.xml +++ b/core/res/res/values-kk/strings.xml @@ -2273,18 +2273,12 @@ <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Айналдыру"</string> <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Кідірту"</string> <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Орналастыру"</string> - <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) --> - <skip /> + <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Жоғары айналдыру"</string> + <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Төменге айналдыру"</string> + <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Солға айналдыру"</string> + <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Оңға айналдыру"</string> + <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Айналдыру режимінен шығу"</string> + <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Айналдыру панелі"</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> ШЕКТЕЛГЕН себетке салынды."</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"сурет жіберілді"</string> @@ -2492,8 +2486,7 @@ <string name="profile_label_work_3" msgid="4834572253956798917">"Жұмыс 3"</string> <string name="profile_label_test" msgid="9168641926186071947">"Сынақ"</string> <string name="profile_label_communal" msgid="8743921499944800427">"Жалпы"</string> - <!-- no translation found for profile_label_supervising (5649312778545745371) --> - <skip /> + <string name="profile_label_supervising" msgid="5649312778545745371">"Қадағалау"</string> <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Жұмыс профилі"</string> <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Құпия кеңістік"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Клон"</string> diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml index c9a0f211a4e8..316955f0717b 100644 --- a/core/res/res/values-kn/strings.xml +++ b/core/res/res/values-kn/strings.xml @@ -2486,8 +2486,7 @@ <string name="profile_label_work_3" msgid="4834572253956798917">"ಕೆಲಸ 3"</string> <string name="profile_label_test" msgid="9168641926186071947">"ಪರೀಕ್ಷೆ"</string> <string name="profile_label_communal" msgid="8743921499944800427">"ಸಮುದಾಯ"</string> - <!-- no translation found for profile_label_supervising (5649312778545745371) --> - <skip /> + <string name="profile_label_supervising" msgid="5649312778545745371">"ಮೇಲ್ವಿಚಾರಣೆಯಾಗುತ್ತಿದೆ"</string> <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"ಉದ್ಯೋಗ ಪ್ರೊಫೈಲ್"</string> <string name="accessibility_label_private_profile" msgid="1436459319135548969">"ಪ್ರೈವೆಟ್ ಸ್ಪೇಸ್"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"ಕ್ಲೋನ್"</string> diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml index 852051f0d8e2..bd48cb3e3bf8 100644 --- a/core/res/res/values-ko/strings.xml +++ b/core/res/res/values-ko/strings.xml @@ -2273,18 +2273,12 @@ <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"스크롤"</string> <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"일시중지"</string> <string name="accessibility_autoclick_position" msgid="2933660969907663545">"위치"</string> - <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) --> - <skip /> + <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"위로 스크롤"</string> + <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"아래로 스크롤"</string> + <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"왼쪽으로 스크롤"</string> + <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"오른쪽으로 스크롤"</string> + <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"스크롤 모드 종료"</string> + <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"패널 스크롤"</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> 항목이 RESTRICTED 버킷으로 이동함"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"이미지 보냄"</string> @@ -2492,8 +2486,7 @@ <string name="profile_label_work_3" msgid="4834572253956798917">"직장 3"</string> <string name="profile_label_test" msgid="9168641926186071947">"테스트"</string> <string name="profile_label_communal" msgid="8743921499944800427">"공동"</string> - <!-- no translation found for profile_label_supervising (5649312778545745371) --> - <skip /> + <string name="profile_label_supervising" msgid="5649312778545745371">"감독 중"</string> <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"직장 프로필"</string> <string name="accessibility_label_private_profile" msgid="1436459319135548969">"비공개 스페이스"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"클론"</string> diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml index 8115a000a42a..906d4a3ed0fa 100644 --- a/core/res/res/values-ky/strings.xml +++ b/core/res/res/values-ky/strings.xml @@ -2273,18 +2273,12 @@ <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Сыдыруу"</string> <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Тындыруу"</string> <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Орду"</string> - <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) --> - <skip /> + <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Жогору сыдыруу"</string> + <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Ылдый сыдыруу"</string> + <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Солго сыдырып кароо"</string> + <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Оңго сыдырып кароо"</string> + <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Сыдыруу режиминен чыгуу"</string> + <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Сыдыруу панели"</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> ЧЕКТЕЛГЕН чакага коюлган"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"сүрөт жөнөттү"</string> @@ -2492,8 +2486,7 @@ <string name="profile_label_work_3" msgid="4834572253956798917">"Жумуш 3"</string> <string name="profile_label_test" msgid="9168641926186071947">"Сыноо"</string> <string name="profile_label_communal" msgid="8743921499944800427">"Жалпы"</string> - <!-- no translation found for profile_label_supervising (5649312778545745371) --> - <skip /> + <string name="profile_label_supervising" msgid="5649312778545745371">"Көзөмөлдөнүүдө"</string> <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Жумуш профили"</string> <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Жеке мейкиндик"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Клон"</string> diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml index b9868d1f82f6..e6222da7af40 100644 --- a/core/res/res/values-lo/strings.xml +++ b/core/res/res/values-lo/strings.xml @@ -2486,8 +2486,7 @@ <string name="profile_label_work_3" msgid="4834572253956798917">"ວຽກ 3"</string> <string name="profile_label_test" msgid="9168641926186071947">"ທົດສອບ"</string> <string name="profile_label_communal" msgid="8743921499944800427">"ສ່ວນກາງ"</string> - <!-- no translation found for profile_label_supervising (5649312778545745371) --> - <skip /> + <string name="profile_label_supervising" msgid="5649312778545745371">"ການເບິ່ງແຍງກວດກາ"</string> <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"ໂປຣໄຟລ໌ບ່ອນເຮັດວຽກ"</string> <string name="accessibility_label_private_profile" msgid="1436459319135548969">"ພື້ນທີ່ສ່ວນບຸກຄົນ"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"ໂຄລນ"</string> diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml index c71c2b8e612e..3a2836833ea4 100644 --- a/core/res/res/values-lt/strings.xml +++ b/core/res/res/values-lt/strings.xml @@ -2488,8 +2488,7 @@ <string name="profile_label_work_3" msgid="4834572253956798917">"Darbas (3)"</string> <string name="profile_label_test" msgid="9168641926186071947">"Bandymas"</string> <string name="profile_label_communal" msgid="8743921499944800427">"Bendruomenės"</string> - <!-- no translation found for profile_label_supervising (5649312778545745371) --> - <skip /> + <string name="profile_label_supervising" msgid="5649312778545745371">"Prižiūrima"</string> <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Darbo profilis"</string> <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privati erdvė"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klonuoti"</string> diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml index 252343724908..b0f6313c214d 100644 --- a/core/res/res/values-lv/strings.xml +++ b/core/res/res/values-lv/strings.xml @@ -2274,18 +2274,12 @@ <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Ritināt"</string> <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Pārtraukt"</string> <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Pozīcija"</string> - <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) --> - <skip /> + <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Ritināt augšup"</string> + <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Ritināt lejup"</string> + <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Ritināt pa kreisi"</string> + <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Ritināt pa labi"</string> + <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Izslēgt ritināšanas režīmu"</string> + <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Ritināšanas panelis"</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"Pakotne “<xliff:g id="PACKAGE_NAME">%1$s</xliff:g>” ir ievietota ierobežotā kopā."</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"nosūtīts attēls"</string> @@ -2493,8 +2487,7 @@ <string name="profile_label_work_3" msgid="4834572253956798917">"Darbam (3.)"</string> <string name="profile_label_test" msgid="9168641926186071947">"Testēšanai"</string> <string name="profile_label_communal" msgid="8743921499944800427">"Kopīgs"</string> - <!-- no translation found for profile_label_supervising (5649312778545745371) --> - <skip /> + <string name="profile_label_supervising" msgid="5649312778545745371">"Uzraudzība"</string> <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Darba profils"</string> <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privātā telpa"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klons"</string> diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml index c6cb1faa850b..8b389a0e5a1b 100644 --- a/core/res/res/values-mk/strings.xml +++ b/core/res/res/values-mk/strings.xml @@ -2273,18 +2273,12 @@ <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Лизгање"</string> <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Паузирај"</string> <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Позиционирај"</string> - <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) --> - <skip /> + <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Лизгај нагоре"</string> + <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Лизгај надолу"</string> + <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Лизгај налево"</string> + <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Лизгај надесно"</string> + <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Излези од „Режим на лизгање“"</string> + <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Табла за лизгање"</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> е ставен во корпата ОГРАНИЧЕНИ"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"испрати слика"</string> @@ -2492,8 +2486,7 @@ <string name="profile_label_work_3" msgid="4834572253956798917">"Работен профил 3"</string> <string name="profile_label_test" msgid="9168641926186071947">"Профил за тестирање"</string> <string name="profile_label_communal" msgid="8743921499944800427">"Профил на заедницата"</string> - <!-- no translation found for profile_label_supervising (5649312778545745371) --> - <skip /> + <string name="profile_label_supervising" msgid="5649312778545745371">"Вршење надзор"</string> <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Работен профил"</string> <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Приватен простор"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Клониран профил"</string> diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml index fca58a113c7b..825da1894522 100644 --- a/core/res/res/values-ml/strings.xml +++ b/core/res/res/values-ml/strings.xml @@ -2273,18 +2273,12 @@ <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"സ്ക്രോൾ ചെയ്യുക"</string> <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"താൽക്കാലികമായി നിർത്തുക"</string> <string name="accessibility_autoclick_position" msgid="2933660969907663545">"സ്ഥാനം"</string> - <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) --> - <skip /> + <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"മുകളിലേക്ക് സ്ക്രോൾ ചെയ്യുക"</string> + <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"താഴേക്ക് സ്ക്രോൾ ചെയ്യുക"</string> + <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"ഇടത്തേക്ക് സ്ക്രോൾ ചെയ്യുക"</string> + <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"വലത്തേക്ക് സ്ക്രോൾ ചെയ്യുക"</string> + <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"സ്ക്രോൾ മോഡിൽ നിന്ന് പുറത്തുകടക്കുക"</string> + <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"സ്ക്രോൾ പാനൽ"</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> നിയന്ത്രിത ബക്കറ്റിലേക്ക് നീക്കി"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"ചിത്രം അയച്ചു"</string> @@ -2492,8 +2486,7 @@ <string name="profile_label_work_3" msgid="4834572253956798917">"ഔദ്യോഗികം 3"</string> <string name="profile_label_test" msgid="9168641926186071947">"ടെസ്റ്റ്"</string> <string name="profile_label_communal" msgid="8743921499944800427">"കമ്മ്യൂണൽ"</string> - <!-- no translation found for profile_label_supervising (5649312778545745371) --> - <skip /> + <string name="profile_label_supervising" msgid="5649312778545745371">"മേൽനോട്ടമുണ്ട്"</string> <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"ഔദ്യോഗിക പ്രൊഫൈൽ"</string> <string name="accessibility_label_private_profile" msgid="1436459319135548969">"സ്വകാര്യ സ്പേസ്"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"ക്ലോൺ ചെയ്യുക"</string> diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml index 740c78386c3a..4222ce1df348 100644 --- a/core/res/res/values-mn/strings.xml +++ b/core/res/res/values-mn/strings.xml @@ -2273,18 +2273,12 @@ <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Гүйлгэх"</string> <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Түр зогсоох"</string> <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Байрлал"</string> - <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) --> - <skip /> + <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Дээш гүйлгэх"</string> + <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Доош гүйлгэх"</string> + <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Зүүн тийш гүйлгэх"</string> + <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Баруун тийш гүйлгэх"</string> + <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Гүйлгэх горимоос гарах"</string> + <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Гүйлгэх үйлдлийн түр зуурын самбар"</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g>-г ХЯЗГААРЛАСАН сагс руу орууллаа"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"зураг илгээсэн"</string> @@ -2492,8 +2486,7 @@ <string name="profile_label_work_3" msgid="4834572253956798917">"Ажил 3"</string> <string name="profile_label_test" msgid="9168641926186071947">"Туршилт"</string> <string name="profile_label_communal" msgid="8743921499944800427">"Нийтийн"</string> - <!-- no translation found for profile_label_supervising (5649312778545745371) --> - <skip /> + <string name="profile_label_supervising" msgid="5649312778545745371">"Хянаж байна"</string> <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Ажлын профайл"</string> <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Хаалттай орон зай"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Клон"</string> diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml index 6f741d7b77b9..73fc1b657c97 100644 --- a/core/res/res/values-mr/strings.xml +++ b/core/res/res/values-mr/strings.xml @@ -2273,18 +2273,12 @@ <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"स्क्रोल करा"</string> <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"थांबवा"</string> <string name="accessibility_autoclick_position" msgid="2933660969907663545">"स्थिती"</string> - <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) --> - <skip /> + <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"वर स्क्रोल करा"</string> + <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"खाली स्क्रोल करा"</string> + <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"डावीकडे स्क्रोल करा"</string> + <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"उजवीकडे स्क्रोल करा"</string> + <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"स्क्रोल करा मोड मधून बाहेर पडा"</string> + <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"स्क्रोल करा पॅनल"</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> हे प्रतिबंधित बादलीमध्ये ठेवण्यात आले आहे"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"इमेज पाठवली आहे"</string> @@ -2492,8 +2486,7 @@ <string name="profile_label_work_3" msgid="4834572253956798917">"ऑफिस ३"</string> <string name="profile_label_test" msgid="9168641926186071947">"चाचणी"</string> <string name="profile_label_communal" msgid="8743921499944800427">"सामुदायिक"</string> - <!-- no translation found for profile_label_supervising (5649312778545745371) --> - <skip /> + <string name="profile_label_supervising" msgid="5649312778545745371">"पर्यवेक्षण करत आहे"</string> <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"कार्य प्रोफाइल"</string> <string name="accessibility_label_private_profile" msgid="1436459319135548969">"खाजगी स्पेस"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"क्लोन"</string> diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml index 52421557f296..f3160d85e97b 100644 --- a/core/res/res/values-my/strings.xml +++ b/core/res/res/values-my/strings.xml @@ -2486,8 +2486,7 @@ <string name="profile_label_work_3" msgid="4834572253956798917">"အလုပ် ၃"</string> <string name="profile_label_test" msgid="9168641926186071947">"စမ်းသပ်မှု"</string> <string name="profile_label_communal" msgid="8743921499944800427">"အများသုံး"</string> - <!-- no translation found for profile_label_supervising (5649312778545745371) --> - <skip /> + <string name="profile_label_supervising" msgid="5649312778545745371">"ကြီးကြပ်နေသည်"</string> <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"အလုပ်ပရိုဖိုင်"</string> <string name="accessibility_label_private_profile" msgid="1436459319135548969">"သီးသန့်နေရာ"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"ပုံတူပွားရန်"</string> diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml index 3dca9b927c10..a4026db705ce 100644 --- a/core/res/res/values-nb/strings.xml +++ b/core/res/res/values-nb/strings.xml @@ -2273,18 +2273,12 @@ <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Rull"</string> <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Sett på pause"</string> <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Plassér"</string> - <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) --> - <skip /> + <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Rull opp"</string> + <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Rull ned"</string> + <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Rull til venstre"</string> + <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Rull til høyre"</string> + <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Avslutt rullemodus"</string> + <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Rullepanel"</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> er blitt plassert i TILGANGSBEGRENSET-toppmappen"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"har sendt et bilde"</string> @@ -2492,8 +2486,7 @@ <string name="profile_label_work_3" msgid="4834572253956798917">"Jobb 3"</string> <string name="profile_label_test" msgid="9168641926186071947">"Test"</string> <string name="profile_label_communal" msgid="8743921499944800427">"Felles"</string> - <!-- no translation found for profile_label_supervising (5649312778545745371) --> - <skip /> + <string name="profile_label_supervising" msgid="5649312778545745371">"Med tilsyn"</string> <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Jobbprofil"</string> <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privat område"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string> diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml index 13c98551b8ca..5e32b967ec71 100644 --- a/core/res/res/values-ne/strings.xml +++ b/core/res/res/values-ne/strings.xml @@ -2273,18 +2273,12 @@ <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"स्क्रोल गर्नुहोस्"</string> <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"पज गर्नुहोस्"</string> <string name="accessibility_autoclick_position" msgid="2933660969907663545">"स्थिति"</string> - <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) --> - <skip /> + <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"माथितिर स्क्रोल गर्नुहोस्"</string> + <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"तलतिर स्क्रोल गर्नुहोस्"</string> + <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"बायाँतिर स्क्रोल गर्नुहोस्"</string> + <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"दायाँतिर स्क्रोल गर्नुहोस्"</string> + <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"स्क्रोल गर्नुहोस् मोडबाट बाहिरिनुहोस्"</string> + <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"प्यानल स्क्रोल गर्नुहोस्"</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> लाई प्रतिबन्धित बाल्टीमा राखियो"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"फोटो पठाइयो"</string> diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml index f5e3831a6032..ebdd4c65db20 100644 --- a/core/res/res/values-nl/strings.xml +++ b/core/res/res/values-nl/strings.xml @@ -2273,18 +2273,12 @@ <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Scrollen"</string> <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Pauzeren"</string> <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Positie"</string> - <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) --> - <skip /> + <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Omhoog scrollen"</string> + <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Omlaag scrollen"</string> + <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Naar links scrollen"</string> + <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Naar rechts scrollen"</string> + <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Scrollmodus sluiten"</string> + <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Deelvenster scrollen"</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> is in de bucket RESTRICTED geplaatst"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"heeft een afbeelding gestuurd"</string> @@ -2492,8 +2486,7 @@ <string name="profile_label_work_3" msgid="4834572253956798917">"Werk 3"</string> <string name="profile_label_test" msgid="9168641926186071947">"Test"</string> <string name="profile_label_communal" msgid="8743921499944800427">"Gemeenschappelijk"</string> - <!-- no translation found for profile_label_supervising (5649312778545745371) --> - <skip /> + <string name="profile_label_supervising" msgid="5649312778545745371">"Toezicht"</string> <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Werkprofiel"</string> <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privégedeelte"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Kloon"</string> diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml index eee243f3f47a..2b91cff7f216 100644 --- a/core/res/res/values-or/strings.xml +++ b/core/res/res/values-or/strings.xml @@ -2273,18 +2273,12 @@ <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"ସ୍କ୍ରୋଲ କରନ୍ତୁ"</string> <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"ବିରତ କରନ୍ତୁ"</string> <string name="accessibility_autoclick_position" msgid="2933660969907663545">"ସ୍ଥିତି"</string> - <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) --> - <skip /> + <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"ଉପରକୁ ସ୍କ୍ରୋଲ କରନ୍ତୁ"</string> + <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"ତଳକୁ ସ୍କ୍ରୋଲ କରନ୍ତୁ"</string> + <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"ବାମକୁ ସ୍କ୍ରୋଲ କରନ୍ତୁ"</string> + <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"ଡାହାଣକୁ ସ୍କ୍ରୋଲ କରନ୍ତୁ"</string> + <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"ସ୍କ୍ରୋଲ ମୋଡରୁ ବାହାରି ଯାଆନ୍ତୁ"</string> + <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"ସ୍କ୍ରୋଲ ପେନେଲ"</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g>କୁ ପ୍ରତିବନ୍ଧିତ ବକେଟରେ ରଖାଯାଇଛି"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"ଏକ ଛବି ପଠାଯାଇଛି"</string> @@ -2492,8 +2486,7 @@ <string name="profile_label_work_3" msgid="4834572253956798917">"ୱାର୍କ 3"</string> <string name="profile_label_test" msgid="9168641926186071947">"ଟେଷ୍ଟ"</string> <string name="profile_label_communal" msgid="8743921499944800427">"କମ୍ୟୁନାଲ"</string> - <!-- no translation found for profile_label_supervising (5649312778545745371) --> - <skip /> + <string name="profile_label_supervising" msgid="5649312778545745371">"ନିରୀକ୍ଷଣ କରାଯାଉଛି"</string> <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"ୱାର୍କ ପ୍ରୋଫାଇଲ"</string> <string name="accessibility_label_private_profile" msgid="1436459319135548969">"ପ୍ରାଇଭେଟ ସ୍ପେସ"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"କ୍ଲୋନ"</string> diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml index 6632fb0f1354..5a9f227f6772 100644 --- a/core/res/res/values-pa/strings.xml +++ b/core/res/res/values-pa/strings.xml @@ -2273,18 +2273,12 @@ <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"ਸਕ੍ਰੋਲ ਕਰੋ"</string> <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"ਰੋਕੋ"</string> <string name="accessibility_autoclick_position" msgid="2933660969907663545">"ਸਥਿਤੀ"</string> - <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) --> - <skip /> + <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"ਉੱਪਰ ਵੱਲ ਸਕ੍ਰੋਲ ਕਰੋ"</string> + <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"ਹੇਠਾਂ ਵੱਲ ਸਕ੍ਰੋਲ ਕਰੋ"</string> + <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"ਖੱਬੇ ਪਾਸੇ ਵੱਲ ਸਕ੍ਰੋਲ ਕਰੋ"</string> + <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"ਸੱਜੇ ਪਾਸੇ ਵੱਲ ਸਕ੍ਰੋਲ ਕਰੋ"</string> + <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"ਸਕ੍ਰੋਲ ਮੋਡ ਤੋਂ ਬਾਹਰ ਜਾਓ"</string> + <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"ਸਕ੍ਰੋਲ ਪੈਨਲ"</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> ਨੂੰ ਪ੍ਰਤਿਬੰਧਿਤ ਖਾਨੇ ਵਿੱਚ ਪਾਇਆ ਗਿਆ ਹੈ"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"ਚਿੱਤਰ ਭੇਜਿਆ ਗਿਆ"</string> @@ -2492,8 +2486,7 @@ <string name="profile_label_work_3" msgid="4834572253956798917">"ਕੰਮ ਸੰਬੰਧੀ 3"</string> <string name="profile_label_test" msgid="9168641926186071947">"ਜਾਂਚ"</string> <string name="profile_label_communal" msgid="8743921499944800427">"ਭਾਈਚਾਰਕ"</string> - <!-- no translation found for profile_label_supervising (5649312778545745371) --> - <skip /> + <string name="profile_label_supervising" msgid="5649312778545745371">"ਨਿਗਰਾਨੀ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ"</string> <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ"</string> <string name="accessibility_label_private_profile" msgid="1436459319135548969">"ਪ੍ਰਾਈਵੇਟ ਸਪੇਸ"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"ਕਲੋਨ"</string> diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml index 2cdac419ab0d..5f02c1fcc532 100644 --- a/core/res/res/values-pl/strings.xml +++ b/core/res/res/values-pl/strings.xml @@ -2275,18 +2275,12 @@ <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Przewijanie"</string> <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Wstrzymaj"</string> <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Pozycja"</string> - <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) --> - <skip /> + <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Przewiń w górę"</string> + <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Przewiń w dół"</string> + <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Przewiń w lewo"</string> + <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Przewiń w prawo"</string> + <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Wyłącz tryb przewijania"</string> + <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Przewiń panel"</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"Umieszczono pakiet <xliff:g id="PACKAGE_NAME">%1$s</xliff:g> w zasobniku danych RESTRICTED"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"wysłano obraz"</string> @@ -2494,8 +2488,7 @@ <string name="profile_label_work_3" msgid="4834572253956798917">"Służbowy 3"</string> <string name="profile_label_test" msgid="9168641926186071947">"Testowy"</string> <string name="profile_label_communal" msgid="8743921499944800427">"Wspólny"</string> - <!-- no translation found for profile_label_supervising (5649312778545745371) --> - <skip /> + <string name="profile_label_supervising" msgid="5649312778545745371">"Nadzorujesz"</string> <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Profil służbowy"</string> <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Przestrzeń prywatna"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string> diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml index 52dc72c2c280..32d883ab7c29 100644 --- a/core/res/res/values-pt-rBR/strings.xml +++ b/core/res/res/values-pt-rBR/strings.xml @@ -2274,18 +2274,12 @@ <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Rolar"</string> <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Pausar"</string> <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Posição"</string> - <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) --> - <skip /> + <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Rolar para cima"</string> + <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Rolar para baixo"</string> + <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Rolar para a esquerda"</string> + <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Rolar para a direita"</string> + <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Sair do modo de rolagem"</string> + <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Painel de rolagem"</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> foi colocado no intervalo \"RESTRITO\""</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"enviou uma imagem"</string> @@ -2493,8 +2487,7 @@ <string name="profile_label_work_3" msgid="4834572253956798917">"Trabalho 3"</string> <string name="profile_label_test" msgid="9168641926186071947">"Teste"</string> <string name="profile_label_communal" msgid="8743921499944800427">"Público"</string> - <!-- no translation found for profile_label_supervising (5649312778545745371) --> - <skip /> + <string name="profile_label_supervising" msgid="5649312778545745371">"Supervisionando"</string> <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Perfil de trabalho"</string> <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Espaço privado"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string> diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml index a8400e2837a4..57507df2370a 100644 --- a/core/res/res/values-pt-rPT/strings.xml +++ b/core/res/res/values-pt-rPT/strings.xml @@ -2487,8 +2487,7 @@ <string name="profile_label_work_3" msgid="4834572253956798917">"Trabalho 3"</string> <string name="profile_label_test" msgid="9168641926186071947">"Teste"</string> <string name="profile_label_communal" msgid="8743921499944800427">"Comum"</string> - <!-- no translation found for profile_label_supervising (5649312778545745371) --> - <skip /> + <string name="profile_label_supervising" msgid="5649312778545745371">"A supervisionar"</string> <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Perfil de trabalho"</string> <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Espaço privado"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string> diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml index 52dc72c2c280..32d883ab7c29 100644 --- a/core/res/res/values-pt/strings.xml +++ b/core/res/res/values-pt/strings.xml @@ -2274,18 +2274,12 @@ <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Rolar"</string> <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Pausar"</string> <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Posição"</string> - <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) --> - <skip /> + <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Rolar para cima"</string> + <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Rolar para baixo"</string> + <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Rolar para a esquerda"</string> + <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Rolar para a direita"</string> + <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Sair do modo de rolagem"</string> + <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Painel de rolagem"</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> foi colocado no intervalo \"RESTRITO\""</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"enviou uma imagem"</string> @@ -2493,8 +2487,7 @@ <string name="profile_label_work_3" msgid="4834572253956798917">"Trabalho 3"</string> <string name="profile_label_test" msgid="9168641926186071947">"Teste"</string> <string name="profile_label_communal" msgid="8743921499944800427">"Público"</string> - <!-- no translation found for profile_label_supervising (5649312778545745371) --> - <skip /> + <string name="profile_label_supervising" msgid="5649312778545745371">"Supervisionando"</string> <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Perfil de trabalho"</string> <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Espaço privado"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string> diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml index 8ad6d02e6113..2e6f58dca1df 100644 --- a/core/res/res/values-ro/strings.xml +++ b/core/res/res/values-ro/strings.xml @@ -2274,18 +2274,12 @@ <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Derulează"</string> <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Întrerupe"</string> <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Poziție"</string> - <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) --> - <skip /> + <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Derulează în sus"</string> + <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Derulează în jos"</string> + <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Derulează la stânga"</string> + <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Derulează la dreapta"</string> + <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Ieși din modul de derulare"</string> + <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Panou de derulare"</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> a fost adăugat la grupul RESTRICȚIONATE"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"a trimis o imagine"</string> @@ -2493,8 +2487,7 @@ <string name="profile_label_work_3" msgid="4834572253956798917">"Serviciu 3"</string> <string name="profile_label_test" msgid="9168641926186071947">"Test"</string> <string name="profile_label_communal" msgid="8743921499944800427">"Comun"</string> - <!-- no translation found for profile_label_supervising (5649312778545745371) --> - <skip /> + <string name="profile_label_supervising" msgid="5649312778545745371">"Supraveghere activă"</string> <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Profil de serviciu"</string> <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Spațiu privat"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clonă"</string> diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml index 0ee25426bbf6..2beebd7569fc 100644 --- a/core/res/res/values-ru/strings.xml +++ b/core/res/res/values-ru/strings.xml @@ -2275,18 +2275,12 @@ <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Прокрутить"</string> <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Приостановить"</string> <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Положение"</string> - <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) --> - <skip /> + <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Прокрутка вверх"</string> + <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Прокрутка вниз"</string> + <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Прокрутка влево"</string> + <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Прокрутка вправо"</string> + <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Отключить режим прокрутки"</string> + <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Панель прокрутки"</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"Приложение \"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g>\" помещено в категорию с ограниченным доступом."</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"Отправлено изображение"</string> @@ -2494,8 +2488,7 @@ <string name="profile_label_work_3" msgid="4834572253956798917">"Рабочий 3"</string> <string name="profile_label_test" msgid="9168641926186071947">"Тестовый"</string> <string name="profile_label_communal" msgid="8743921499944800427">"Совместный"</string> - <!-- no translation found for profile_label_supervising (5649312778545745371) --> - <skip /> + <string name="profile_label_supervising" msgid="5649312778545745371">"Управляющий профиль"</string> <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Рабочий профиль"</string> <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Частное пространство"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Клонированный"</string> diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml index 0d3cd7e4979a..a59187cf62c6 100644 --- a/core/res/res/values-si/strings.xml +++ b/core/res/res/values-si/strings.xml @@ -2273,18 +2273,12 @@ <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"අනුචලනය කරන්න"</string> <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"විරාම කරන්න"</string> <string name="accessibility_autoclick_position" msgid="2933660969907663545">"ස්ථානය"</string> - <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) --> - <skip /> + <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"ඉහළට අනුචලනය කරන්න"</string> + <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"පහළට අනුචලනය කරන්න"</string> + <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"වමට අනුචලනය කරන්න"</string> + <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"දකුණට අනුචලනය කරන්න"</string> + <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"අනුචලන ප්රකාරයෙන් පිටවන්න"</string> + <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"අනුචලන පැනලය"</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> අවහිර කළ බාල්දියට දමා ඇත"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"රූපයක් එව්වා"</string> @@ -2492,8 +2486,7 @@ <string name="profile_label_work_3" msgid="4834572253956798917">"කාර්යාලය 3"</string> <string name="profile_label_test" msgid="9168641926186071947">"පරීක්ෂණය"</string> <string name="profile_label_communal" msgid="8743921499944800427">"වාර්ගික"</string> - <!-- no translation found for profile_label_supervising (5649312778545745371) --> - <skip /> + <string name="profile_label_supervising" msgid="5649312778545745371">"අධීක්ෂණය කිරීම"</string> <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"කාර්යාල පැතිකඩ"</string> <string name="accessibility_label_private_profile" msgid="1436459319135548969">"රහසිගත අවකාශය"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"ක්ලෝන කරන්න"</string> diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml index e3855d1a6790..999000e6e490 100644 --- a/core/res/res/values-sk/strings.xml +++ b/core/res/res/values-sk/strings.xml @@ -2275,18 +2275,12 @@ <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Posúvať"</string> <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Pozastaviť"</string> <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Pozícia"</string> - <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) --> - <skip /> + <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Posunúť nahor"</string> + <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Posunúť nadol"</string> + <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Posunúť doľava"</string> + <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Posunúť doprava"</string> + <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Ukončiť režim posúvania"</string> + <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Posúvateľný panel"</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"Balík <xliff:g id="PACKAGE_NAME">%1$s</xliff:g> bol vložený do kontajnera OBMEDZENÉ"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"odoslal(a) obrázok"</string> @@ -2494,8 +2488,7 @@ <string name="profile_label_work_3" msgid="4834572253956798917">"3. pracovný"</string> <string name="profile_label_test" msgid="9168641926186071947">"Test"</string> <string name="profile_label_communal" msgid="8743921499944800427">"Spoločné"</string> - <!-- no translation found for profile_label_supervising (5649312778545745371) --> - <skip /> + <string name="profile_label_supervising" msgid="5649312778545745371">"Dohľad"</string> <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Pracovný profil"</string> <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Súkromný priestor"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string> diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml index b0ebb5f073b7..4751cb9cfb87 100644 --- a/core/res/res/values-sl/strings.xml +++ b/core/res/res/values-sl/strings.xml @@ -2488,8 +2488,7 @@ <string name="profile_label_work_3" msgid="4834572253956798917">"Delo 3"</string> <string name="profile_label_test" msgid="9168641926186071947">"Preizkus"</string> <string name="profile_label_communal" msgid="8743921499944800427">"Skupno"</string> - <!-- no translation found for profile_label_supervising (5649312778545745371) --> - <skip /> + <string name="profile_label_supervising" msgid="5649312778545745371">"Nadzor"</string> <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Delovni profil"</string> <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Zasebni prostor"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string> diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml index 0f99307da7e3..a15cf4ea3b5c 100644 --- a/core/res/res/values-sq/strings.xml +++ b/core/res/res/values-sq/strings.xml @@ -2492,8 +2492,7 @@ <string name="profile_label_work_3" msgid="4834572253956798917">"Puna 3"</string> <string name="profile_label_test" msgid="9168641926186071947">"Test"</string> <string name="profile_label_communal" msgid="8743921499944800427">"I përbashkët"</string> - <!-- no translation found for profile_label_supervising (5649312778545745371) --> - <skip /> + <string name="profile_label_supervising" msgid="5649312778545745371">"Në mbikëqyrje"</string> <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Profili i punës"</string> <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Hapësira private"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string> diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml index 4827c0cc3265..112173ac13ad 100644 --- a/core/res/res/values-sr/strings.xml +++ b/core/res/res/values-sr/strings.xml @@ -2274,18 +2274,12 @@ <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Скролујте"</string> <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Паузирај"</string> <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Позиција"</string> - <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) --> - <skip /> + <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Скролуј нагоре"</string> + <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Скролуј надоле"</string> + <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Скролуј улево"</string> + <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Скролуј удесно"</string> + <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Изађи из режима скроловања"</string> + <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Окно за скроловање"</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"Пакет <xliff:g id="PACKAGE_NAME">%1$s</xliff:g> је додат у сегмент ОГРАНИЧЕНО"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"је послао/ла слику"</string> @@ -2493,8 +2487,7 @@ <string name="profile_label_work_3" msgid="4834572253956798917">"Посао 3"</string> <string name="profile_label_test" msgid="9168641926186071947">"Тест"</string> <string name="profile_label_communal" msgid="8743921499944800427">"Заједничко"</string> - <!-- no translation found for profile_label_supervising (5649312778545745371) --> - <skip /> + <string name="profile_label_supervising" msgid="5649312778545745371">"Надзире се"</string> <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Пословни профил"</string> <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Приватан простор"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Клонирано"</string> diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml index c471f065b377..5cb528b5c555 100644 --- a/core/res/res/values-sv/strings.xml +++ b/core/res/res/values-sv/strings.xml @@ -2273,18 +2273,12 @@ <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Scrolla"</string> <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Pausa"</string> <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Position"</string> - <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) --> - <skip /> + <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Scrolla uppåt"</string> + <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Scrolla nedåt"</string> + <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Scrolla åt vänster"</string> + <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Scrolla åt höger"</string> + <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Avsluta scrollningsläget"</string> + <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Scrollningspanel"</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> har placerats i hinken RESTRICTED"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"har skickat en bild"</string> @@ -2492,8 +2486,7 @@ <string name="profile_label_work_3" msgid="4834572253956798917">"Arbete 3"</string> <string name="profile_label_test" msgid="9168641926186071947">"Test"</string> <string name="profile_label_communal" msgid="8743921499944800427">"Allmän"</string> - <!-- no translation found for profile_label_supervising (5649312778545745371) --> - <skip /> + <string name="profile_label_supervising" msgid="5649312778545745371">"Kontrollerar"</string> <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Jobbprofil"</string> <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privat utrymme"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klona"</string> diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml index 31f5592ecaad..5207e032fc30 100644 --- a/core/res/res/values-sw/strings.xml +++ b/core/res/res/values-sw/strings.xml @@ -2273,18 +2273,12 @@ <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Sogeza"</string> <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Sitisha"</string> <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Nafasi"</string> - <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) --> - <skip /> + <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Sogeza Juu"</string> + <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Sogeza Chini"</string> + <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Sogeza Kushoto"</string> + <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Sogeza Kulia"</string> + <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Funga Hali ya Kusogeza"</string> + <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Sogeza Kidirisha"</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> kimewekwa katika kikundi KILICHODHIBITIWA"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"alituma picha"</string> @@ -2492,8 +2486,7 @@ <string name="profile_label_work_3" msgid="4834572253956798917">"Wa 3 wa Kazini"</string> <string name="profile_label_test" msgid="9168641926186071947">"Jaribio"</string> <string name="profile_label_communal" msgid="8743921499944800427">"Unaoshirikiwa"</string> - <!-- no translation found for profile_label_supervising (5649312778545745371) --> - <skip /> + <string name="profile_label_supervising" msgid="5649312778545745371">"Unasimamia"</string> <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Wasifu wa kazini"</string> <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Sehemu ya faragha"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Nakala"</string> diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml index 36b82d99aded..015ce084355b 100644 --- a/core/res/res/values-ta/strings.xml +++ b/core/res/res/values-ta/strings.xml @@ -2273,18 +2273,12 @@ <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"நகர்த்தும்"</string> <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"இடைநிறுத்து"</string> <string name="accessibility_autoclick_position" msgid="2933660969907663545">"நிலை"</string> - <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) --> - <skip /> + <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"மேலே நகர்த்து"</string> + <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"கீழே நகர்த்து"</string> + <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"இடப்புறம் நகர்த்து"</string> + <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"வலப்புறம் நகர்த்து"</string> + <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"நகர்த்துதல் பயன்முறையில் இருந்து வெளியேறு"</string> + <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"பேனலை நகர்த்து"</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> என்பதை வரம்பிடப்பட்ட பக்கெட்திற்குள் சேர்க்கப்பட்டது"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"படம் அனுப்பப்பட்டது"</string> @@ -2492,8 +2486,7 @@ <string name="profile_label_work_3" msgid="4834572253956798917">"பணி 3"</string> <string name="profile_label_test" msgid="9168641926186071947">"பரிசோதனை"</string> <string name="profile_label_communal" msgid="8743921499944800427">"பொது"</string> - <!-- no translation found for profile_label_supervising (5649312778545745371) --> - <skip /> + <string name="profile_label_supervising" msgid="5649312778545745371">"கண்காணிக்கப்படுகிறது"</string> <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"பணிக் கணக்கு"</string> <string name="accessibility_label_private_profile" msgid="1436459319135548969">"ரகசிய இடம்"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"குளோன்"</string> diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml index b67f3828ab72..224ac78c5f92 100644 --- a/core/res/res/values-th/strings.xml +++ b/core/res/res/values-th/strings.xml @@ -2273,18 +2273,12 @@ <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"เลื่อน"</string> <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"หยุดชั่วคราว"</string> <string name="accessibility_autoclick_position" msgid="2933660969907663545">"วางตำแหน่ง"</string> - <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) --> - <skip /> + <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"เลื่อนขึ้น"</string> + <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"เลื่อนลง"</string> + <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"เลื่อนไปทางซ้าย"</string> + <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"เลื่อนไปทางขวา"</string> + <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"ออกจากโหมดเลื่อน"</string> + <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"เลื่อนแผง"</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"ใส่ <xliff:g id="PACKAGE_NAME">%1$s</xliff:g> ในที่เก็บข้อมูลที่ถูกจำกัดแล้ว"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"ส่งรูปภาพ"</string> @@ -2492,8 +2486,7 @@ <string name="profile_label_work_3" msgid="4834572253956798917">"งาน 3"</string> <string name="profile_label_test" msgid="9168641926186071947">"ทดสอบ"</string> <string name="profile_label_communal" msgid="8743921499944800427">"ส่วนกลาง"</string> - <!-- no translation found for profile_label_supervising (5649312778545745371) --> - <skip /> + <string name="profile_label_supervising" msgid="5649312778545745371">"ผู้ควบคุมดูแล"</string> <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"โปรไฟล์งาน"</string> <string name="accessibility_label_private_profile" msgid="1436459319135548969">"พื้นที่ส่วนตัว"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"โคลน"</string> diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml index 3c380884ceea..9b6ecd30b233 100644 --- a/core/res/res/values-tl/strings.xml +++ b/core/res/res/values-tl/strings.xml @@ -2273,18 +2273,12 @@ <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Mag-scroll"</string> <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"I-pause"</string> <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Posisyon"</string> - <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) --> - <skip /> + <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Mag-scroll Pataas"</string> + <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Mag-scroll Pababa"</string> + <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Mag-scroll Pakaliwa"</string> + <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Mag-scroll Pakanan"</string> + <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Umalis sa Scroll Mode"</string> + <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Scroll Panel"</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"Inilagay ang <xliff:g id="PACKAGE_NAME">%1$s</xliff:g> sa PINAGHIHIGPITANG bucket"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"nagpadala ng larawan"</string> @@ -2492,8 +2486,7 @@ <string name="profile_label_work_3" msgid="4834572253956798917">"Trabaho 3"</string> <string name="profile_label_test" msgid="9168641926186071947">"Test"</string> <string name="profile_label_communal" msgid="8743921499944800427">"Communal"</string> - <!-- no translation found for profile_label_supervising (5649312778545745371) --> - <skip /> + <string name="profile_label_supervising" msgid="5649312778545745371">"Namamahala"</string> <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Profile sa trabaho"</string> <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Pribadong space"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string> diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml index 8119d7d2bb2a..ea4cfa644bf3 100644 --- a/core/res/res/values-tr/strings.xml +++ b/core/res/res/values-tr/strings.xml @@ -2273,18 +2273,12 @@ <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Kaydırma"</string> <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Duraklatma"</string> <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Konum"</string> - <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) --> - <skip /> + <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Yukarı Kaydır"</string> + <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Aşağı Kaydır"</string> + <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Sola Kaydır"</string> + <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Sağa Kaydır"</string> + <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Kaydırma Modundan Çık"</string> + <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Paneli Kaydır"</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> KISITLANMIŞ gruba yerleştirildi"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"bir resim gönderildi"</string> @@ -2492,8 +2486,7 @@ <string name="profile_label_work_3" msgid="4834572253956798917">"İş 3"</string> <string name="profile_label_test" msgid="9168641926186071947">"Test"</string> <string name="profile_label_communal" msgid="8743921499944800427">"Paylaşılan"</string> - <!-- no translation found for profile_label_supervising (5649312778545745371) --> - <skip /> + <string name="profile_label_supervising" msgid="5649312778545745371">"Gözetim"</string> <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"İş profili"</string> <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Özel alan"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string> diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml index cda2af43ef12..693b16e88a4c 100644 --- a/core/res/res/values-uk/strings.xml +++ b/core/res/res/values-uk/strings.xml @@ -2275,18 +2275,12 @@ <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Прокрутити"</string> <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Призупинити"</string> <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Змінити позицію"</string> - <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) --> - <skip /> + <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Прокрутити вгору"</string> + <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Прокрутити вниз"</string> + <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Прокрутити ліворуч"</string> + <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Прокрутити праворуч"</string> + <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Вимкнути режим прокручування"</string> + <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Панель прокручування"</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"Пакет \"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g>\" додано в сегмент з обмеженнями"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"надіслано зображення"</string> @@ -2494,8 +2488,7 @@ <string name="profile_label_work_3" msgid="4834572253956798917">"Робочий профіль 3"</string> <string name="profile_label_test" msgid="9168641926186071947">"Тестовий профіль"</string> <string name="profile_label_communal" msgid="8743921499944800427">"Спільний профіль"</string> - <!-- no translation found for profile_label_supervising (5649312778545745371) --> - <skip /> + <string name="profile_label_supervising" msgid="5649312778545745371">"Батьківськ. контроль"</string> <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Робочий профіль"</string> <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Приватний простір"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Копія профілю"</string> diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml index fc2bd4b605d1..f7b10bff74eb 100644 --- a/core/res/res/values-uz/strings.xml +++ b/core/res/res/values-uz/strings.xml @@ -2486,8 +2486,7 @@ <string name="profile_label_work_3" msgid="4834572253956798917">"Ish 3"</string> <string name="profile_label_test" msgid="9168641926186071947">"Test"</string> <string name="profile_label_communal" msgid="8743921499944800427">"Umumiy"</string> - <!-- no translation found for profile_label_supervising (5649312778545745371) --> - <skip /> + <string name="profile_label_supervising" msgid="5649312778545745371">"Nazorat ostida"</string> <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Ish profili"</string> <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Maxfiy makon"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Nusxalash"</string> diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml index 4141af07dc8b..4ee534c6ecfe 100644 --- a/core/res/res/values-vi/strings.xml +++ b/core/res/res/values-vi/strings.xml @@ -2273,18 +2273,12 @@ <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Cuộn"</string> <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Tạm dừng"</string> <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Vị trí"</string> - <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) --> - <skip /> + <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Cuộn lên"</string> + <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Cuộn xuống"</string> + <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Cuộn sang trái"</string> + <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Cuộn sang phải"</string> + <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Thoát khỏi chế độ cuộn"</string> + <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Cuộn bảng điều khiển"</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"Đã đưa <xliff:g id="PACKAGE_NAME">%1$s</xliff:g> vào bộ chứa BỊ HẠN CHẾ"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"đã gửi hình ảnh"</string> @@ -2492,8 +2486,7 @@ <string name="profile_label_work_3" msgid="4834572253956798917">"Công việc 3"</string> <string name="profile_label_test" msgid="9168641926186071947">"Kiểm thử"</string> <string name="profile_label_communal" msgid="8743921499944800427">"Dùng chung"</string> - <!-- no translation found for profile_label_supervising (5649312778545745371) --> - <skip /> + <string name="profile_label_supervising" msgid="5649312778545745371">"Giám sát"</string> <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Hồ sơ công việc"</string> <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> diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml index 95dd35fcb0dd..98069c65d235 100644 --- a/core/res/res/values-zh-rCN/strings.xml +++ b/core/res/res/values-zh-rCN/strings.xml @@ -2273,18 +2273,12 @@ <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"滚动"</string> <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"暂停"</string> <string name="accessibility_autoclick_position" msgid="2933660969907663545">"位置"</string> - <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) --> - <skip /> + <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"向上滚动"</string> + <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"向下滚动"</string> + <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"向左滚动"</string> + <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"向右滚动"</string> + <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"退出滚动模式"</string> + <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"滚动面板"</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> 已被放入受限存储分区"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"发送了一张图片"</string> @@ -2492,8 +2486,7 @@ <string name="profile_label_work_3" msgid="4834572253956798917">"工作 3"</string> <string name="profile_label_test" msgid="9168641926186071947">"测试"</string> <string name="profile_label_communal" msgid="8743921499944800427">"共用"</string> - <!-- no translation found for profile_label_supervising (5649312778545745371) --> - <skip /> + <string name="profile_label_supervising" msgid="5649312778545745371">"监管"</string> <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"工作资料"</string> <string name="accessibility_label_private_profile" msgid="1436459319135548969">"私密空间"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"克隆"</string> diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml index b956da1e856e..038973d760dd 100644 --- a/core/res/res/values-zh-rHK/strings.xml +++ b/core/res/res/values-zh-rHK/strings.xml @@ -2273,18 +2273,12 @@ <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"捲動"</string> <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"暫停"</string> <string name="accessibility_autoclick_position" msgid="2933660969907663545">"位置"</string> - <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) --> - <skip /> + <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"向上捲動"</string> + <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"向下捲動"</string> + <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"向左捲動"</string> + <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"向右捲動"</string> + <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"退出捲動模式"</string> + <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"捲動面板"</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> 已納入受限制的儲存區"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"已傳送圖片"</string> @@ -2492,8 +2486,7 @@ <string name="profile_label_work_3" msgid="4834572253956798917">"工作 3"</string> <string name="profile_label_test" msgid="9168641926186071947">"測試"</string> <string name="profile_label_communal" msgid="8743921499944800427">"共用"</string> - <!-- no translation found for profile_label_supervising (5649312778545745371) --> - <skip /> + <string name="profile_label_supervising" msgid="5649312778545745371">"監管"</string> <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"工作設定檔"</string> <string name="accessibility_label_private_profile" msgid="1436459319135548969">"私人空間"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"複製"</string> diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml index f9abe0e69a46..8ff587bf1e55 100644 --- a/core/res/res/values-zh-rTW/strings.xml +++ b/core/res/res/values-zh-rTW/strings.xml @@ -2273,18 +2273,12 @@ <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"捲動"</string> <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"暫停"</string> <string name="accessibility_autoclick_position" msgid="2933660969907663545">"位置"</string> - <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) --> - <skip /> + <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"向上捲動"</string> + <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"向下捲動"</string> + <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"向左捲動"</string> + <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"向右捲動"</string> + <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"退出捲動模式"</string> + <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"捲動面板"</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"已將「<xliff:g id="PACKAGE_NAME">%1$s</xliff:g>」移入受限制的值區"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"傳送了一張圖片"</string> @@ -2492,8 +2486,7 @@ <string name="profile_label_work_3" msgid="4834572253956798917">"工作 3"</string> <string name="profile_label_test" msgid="9168641926186071947">"測試"</string> <string name="profile_label_communal" msgid="8743921499944800427">"通用"</string> - <!-- no translation found for profile_label_supervising (5649312778545745371) --> - <skip /> + <string name="profile_label_supervising" msgid="5649312778545745371">"監督中"</string> <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"工作資料夾"</string> <string name="accessibility_label_private_profile" msgid="1436459319135548969">"私人空間"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"複製"</string> diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml index 4292eab29a16..3fc910cca43a 100644 --- a/core/res/res/values-zu/strings.xml +++ b/core/res/res/values-zu/strings.xml @@ -2273,18 +2273,12 @@ <string name="accessibility_autoclick_scroll" msgid="3499385943728726933">"Skrola"</string> <string name="accessibility_autoclick_pause" msgid="3272200156172573568">"Misa"</string> <string name="accessibility_autoclick_position" msgid="2933660969907663545">"Indawo"</string> - <!-- no translation found for accessibility_autoclick_scroll_up (2044948780797117443) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_down (3733401063292018116) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_left (8564421367992824198) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_right (8932417330753984265) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_exit (3788610039146769696) --> - <skip /> - <!-- no translation found for accessibility_autoclick_scroll_panel_title (7120598166296447036) --> - <skip /> + <string name="accessibility_autoclick_scroll_up" msgid="2044948780797117443">"Skrolela Phezulu"</string> + <string name="accessibility_autoclick_scroll_down" msgid="3733401063292018116">"Skrolela Phansi"</string> + <string name="accessibility_autoclick_scroll_left" msgid="8564421367992824198">"Skrolela Ngakwesokunxele"</string> + <string name="accessibility_autoclick_scroll_right" msgid="8932417330753984265">"Skrolela Ngakwesokudla"</string> + <string name="accessibility_autoclick_scroll_exit" msgid="3788610039146769696">"Phuma Kumodi Yokuskrola"</string> + <string name="accessibility_autoclick_scroll_panel_title" msgid="7120598166296447036">"Iphaneli Yokuskrola"</string> <string name="as_app_forced_to_restricted_bucket" msgid="8233871289353898964">"I-<xliff:g id="PACKAGE_NAME">%1$s</xliff:g> ifakwe kubhakede LOKUKHAWULELWE"</string> <string name="conversation_single_line_name_display" msgid="8958948312915255999">"<xliff:g id="SENDER_NAME">%1$s</xliff:g>:"</string> <string name="conversation_single_line_image_placeholder" msgid="6983271082911936900">"uthumele isithombe"</string> @@ -2492,8 +2486,7 @@ <string name="profile_label_work_3" msgid="4834572253956798917">"Umsebenzi 3"</string> <string name="profile_label_test" msgid="9168641926186071947">"Hlola"</string> <string name="profile_label_communal" msgid="8743921499944800427">"Okomphakathi"</string> - <!-- no translation found for profile_label_supervising (5649312778545745371) --> - <skip /> + <string name="profile_label_supervising" msgid="5649312778545745371">"Ukugada"</string> <string name="accessibility_label_managed_profile" msgid="3366526886209832641">"Iphrofayela yomsebenzi"</string> <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Indawo engasese"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Yenza i-Clone"</string> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index e47adc90fc7a..1a74fe6719e3 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -1219,6 +1219,8 @@ 6 - Lock if keyguard enabled or go to sleep (doze) 7 - Dream if possible or go to sleep (doze) 8 - Go to glanceable hub or dream if possible, or sleep if neither is available (doze) + 9 - Go to dream if device is not dreaming, stop dream if device is dreaming, or sleep if + neither is available (doze) --> <integer name="config_shortPressOnPowerBehavior">1</integer> diff --git a/core/res/res/values/config_telephony.xml b/core/res/res/values/config_telephony.xml index ef6b9188532e..849ca2882889 100644 --- a/core/res/res/values/config_telephony.xml +++ b/core/res/res/values/config_telephony.xml @@ -86,7 +86,7 @@ CarrierConfigManager#KEY_AUTO_DATA_SWITCH_RAT_SIGNAL_SCORE_STRING_ARRAY. If 0, the device always switch to the higher score SIM. If < 0, the network type and signal strength based auto switch is disabled. --> - <integer name="auto_data_switch_score_tolerance">4000</integer> + <integer name="auto_data_switch_score_tolerance">7000</integer> <java-symbol type="integer" name="auto_data_switch_score_tolerance" /> <!-- Boolean indicating whether the Iwlan data service supports persistence of iwlan ipsec @@ -261,7 +261,9 @@ to identify providers that should be ignored if the carrier config carrier_supported_satellite_services_per_provider_bundle does not support them. --> - <string-array name="config_satellite_providers" translatable="false"></string-array> + <string-array name="config_satellite_providers" translatable="false"> + <item>"310830"</item> + </string-array> <java-symbol type="array" name="config_satellite_providers" /> <!-- The identifier of the satellite's SIM profile. The identifier is composed of MCC and MNC diff --git a/core/tests/FileSystemUtilsTest/OWNERS b/core/tests/FileSystemUtilsTest/OWNERS new file mode 100644 index 000000000000..74eeacfeb973 --- /dev/null +++ b/core/tests/FileSystemUtilsTest/OWNERS @@ -0,0 +1,2 @@ +waghpawan@google.com +kaleshsingh@google.com diff --git a/core/tests/FileSystemUtilsTest/src/com/android/internal/content/FileSystemUtilsTest.java b/core/tests/FileSystemUtilsTest/src/com/android/internal/content/FileSystemUtilsTest.java index 208d74e49afe..dbfd3e8ccdaa 100644 --- a/core/tests/FileSystemUtilsTest/src/com/android/internal/content/FileSystemUtilsTest.java +++ b/core/tests/FileSystemUtilsTest/src/com/android/internal/content/FileSystemUtilsTest.java @@ -38,6 +38,8 @@ public class FileSystemUtilsTest extends BaseHostJUnit4Test { private static final String PAGE_SIZE_COMPAT_ENABLED_BY_PLATFORM = "app_with_4kb_elf_no_override.apk"; + private static final int DEVICE_WAIT_TIMEOUT = 120000; + @Test @AppModeFull public void runPunchedApp_embeddedNativeLibs() throws DeviceNotAvailableException { @@ -98,8 +100,20 @@ public class FileSystemUtilsTest extends BaseHostJUnit4Test { @AppModeFull public void runAppWith4KbLib_compatByAlignmentChecks() throws DeviceNotAvailableException, TargetSetupError { + // make sure that device is available for UI test + prepareDevice(); // This test is expected to fail since compat is disabled in manifest runPageSizeCompatTest(PAGE_SIZE_COMPAT_ENABLED_BY_PLATFORM, "testPageSizeCompat_compatByAlignmentChecks"); } + + private void prepareDevice() throws DeviceNotAvailableException { + // Verify that device is online before running test and enable root + getDevice().waitForDeviceAvailable(DEVICE_WAIT_TIMEOUT); + getDevice().enableAdbRoot(); + getDevice().waitForDeviceAvailable(DEVICE_WAIT_TIMEOUT); + + getDevice().executeShellCommand("input keyevent KEYCODE_WAKEUP"); + getDevice().executeShellCommand("wm dismiss-keyguard"); + } } diff --git a/core/tests/coretests/src/android/content/IntentTest.java b/core/tests/coretests/src/android/content/IntentTest.java index fa1948d9786c..1dbe7f5f245b 100644 --- a/core/tests/coretests/src/android/content/IntentTest.java +++ b/core/tests/coretests/src/android/content/IntentTest.java @@ -22,6 +22,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import android.net.Uri; import android.os.Binder; import android.os.Bundle; import android.os.IBinder; @@ -32,14 +33,21 @@ import android.platform.test.flag.junit.CheckFlagsRule; import android.platform.test.flag.junit.DeviceFlagsValueProvider; import android.security.Flags; import android.util.ArraySet; +import android.util.Xml; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; +import com.android.internal.util.XmlUtils; +import com.android.modules.utils.TypedXmlPullParser; +import com.android.modules.utils.TypedXmlSerializer; + import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -277,4 +285,40 @@ public class IntentTest { assertThat(b2.getBundle("bundle").getClassLoader()).isEqualTo(cl); } + @Test + @RequiresFlagsEnabled(android.content.flags.Flags.FLAG_INTENT_SAVE_TO_XML_PACKAGE) + public void testSaveToXmlAndRestore() throws Exception { + // Create an intent and set fields. + Intent original = new Intent(); + original.setAction(Intent.ACTION_MAIN); + original.setComponent(ComponentName.createRelative("com.intent.test", "IntentTest")); + original.setData(Uri.parse("content://path/to/file.txt")); + original.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); + original.setIdentifier("unique_identifier"); + original.setPackage("com.intent.test"); + original.addCategory(Intent.CATEGORY_LAUNCHER); + original.putExtra("Name", "Some really important data"); + + String tag = "intent"; + + // Write to xml. + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + TypedXmlSerializer serializer = Xml.resolveSerializer(byteArrayOutputStream); + serializer.startDocument(null, true); + serializer.startTag(null, tag); + original.saveToXml(serializer); + serializer.endTag(null, tag); + serializer.endDocument(); + + // Restore from xml. + ByteArrayInputStream byteArrayInputStream = + new ByteArrayInputStream(byteArrayOutputStream.toByteArray()); + TypedXmlPullParser parser = Xml.resolvePullParser(byteArrayInputStream); + XmlUtils.beginDocument(parser, tag); + Intent restored = Intent.restoreFromXml(parser); + + // Verify that the restored intent passed filterEquals on the original. + assertTrue(original.filterEquals(restored)); + } + } diff --git a/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheUnitTest.java b/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheUnitTest.java index 8349659517c5..b63fcdc8362f 100644 --- a/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheUnitTest.java +++ b/core/tests/coretests/src/android/content/pm/RegisteredServicesCacheUnitTest.java @@ -68,6 +68,7 @@ import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; /** * Unit tests for {@link android.content.pm.RegisteredServicesCache} @@ -84,8 +85,8 @@ public class RegisteredServicesCacheUnitTest { @Rule public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule(); - private final ResolveInfo mResolveInfo1 = new ResolveInfo(); - private final ResolveInfo mResolveInfo2 = new ResolveInfo(); + private final TestResolveInfo mResolveInfo1 = new TestResolveInfo(); + private final TestResolveInfo mResolveInfo2 = new TestResolveInfo(); private final TestServiceType mTestServiceType1 = new TestServiceType("t1", "value1"); private final TestServiceType mTestServiceType2 = new TestServiceType("t2", "value2"); @Mock @@ -195,13 +196,13 @@ public class RegisteredServicesCacheUnitTest { reset(testServicesCache); - testServicesCache.clearServicesForQuerying(); int u1uid = UserHandle.getUid(U1, UID1); assertThat(u1uid).isNotEqualTo(UID1); final RegisteredServicesCache.ServiceInfo<TestServiceType> serviceInfo2 = newServiceInfo( mTestServiceType1, u1uid, mResolveInfo1.serviceInfo.getComponentName(), 1000L /* lastUpdateTime */); + mResolveInfo1.setResolveInfoId(U1); testServicesCache.addServiceForQuerying(U1, mResolveInfo1, serviceInfo2); testServicesCache.getAllServices(U1); @@ -286,7 +287,7 @@ public class RegisteredServicesCacheUnitTest { } @Test - public void testClearServiceInfoCachesAfterTimeout() throws Exception { + public void testClearServiceInfoCachesForSingleUserAfterTimeout() throws Exception { PackageInfo packageInfo1 = createPackageInfo(1000L /* lastUpdateTime */); when(mMockPackageManager.getPackageInfoAsUser(eq(mResolveInfo1.serviceInfo.packageName), anyInt(), eq(U0))).thenReturn(packageInfo1); @@ -316,6 +317,58 @@ public class RegisteredServicesCacheUnitTest { verify(testServicesCache, times(1)).parseServiceInfo(eq(mResolveInfo1), eq(1000L)); } + @Test + public void testClearServiceInfoCachesForMultiUserAfterTimeout() throws Exception { + PackageInfo packageInfo1 = createPackageInfo(1000L /* lastUpdateTime */); + when(mMockPackageManager.getPackageInfoAsUser(eq(mResolveInfo1.serviceInfo.packageName), + anyInt(), eq(U0))).thenReturn(packageInfo1); + PackageInfo packageInfo2 = createPackageInfo(2000L /* lastUpdateTime */); + when(mMockPackageManager.getPackageInfoAsUser(eq(mResolveInfo2.serviceInfo.packageName), + anyInt(), eq(U1))).thenReturn(packageInfo2); + + TestRegisteredServicesCache testServicesCache = spy( + new TestRegisteredServicesCache(mMockInjector, null /* serializerAndParser */)); + final RegisteredServicesCache.ServiceInfo<TestServiceType> serviceInfo1 = newServiceInfo( + mTestServiceType1, UID1, mResolveInfo1.serviceInfo.getComponentName(), + 1000L /* lastUpdateTime */); + testServicesCache.addServiceForQuerying(U0, mResolveInfo1, serviceInfo1); + + int u1uid = UserHandle.getUid(U1, UID1); + final RegisteredServicesCache.ServiceInfo<TestServiceType> serviceInfo2 = newServiceInfo( + mTestServiceType2, u1uid, mResolveInfo2.serviceInfo.getComponentName(), + 2000L /* lastUpdateTime */); + testServicesCache.addServiceForQuerying(U1, mResolveInfo2, serviceInfo2); + + // Don't invoke run on the Runnable for U0 user, and it will not clear the service info of + // U0 user. Invoke run on the Runnable for U1 user, and it will just clear the service info + // of U1 user. + doAnswer(invocation -> { + Message message = invocation.getArgument(0); + if (!message.obj.equals(Integer.valueOf(U0))) { + message.getCallback().run(); + } + return true; + }).when(mMockBackgroundHandler).sendMessageAtTime(any(Message.class), anyLong()); + + // It will generate the service info of U0 user into cache. + testServicesCache.getAllServices(U0); + verify(testServicesCache, times(1)).parseServiceInfo(eq(mResolveInfo1), eq(1000L)); + // It will generate the service info of U1 user into cache. + testServicesCache.getAllServices(U1); + verify(testServicesCache, times(1)).parseServiceInfo(eq(mResolveInfo2), eq(2000L)); + verify(mMockBackgroundHandler, times(2)).sendMessageAtTime(any(Message.class), anyLong()); + + reset(testServicesCache); + + testServicesCache.invalidateCache(U0); + testServicesCache.getAllServices(U0); + verify(testServicesCache, never()).parseServiceInfo(eq(mResolveInfo1), eq(1000L)); + + testServicesCache.invalidateCache(U1); + testServicesCache.getAllServices(U1); + verify(testServicesCache, times(1)).parseServiceInfo(eq(mResolveInfo2), eq(2000L)); + } + private static RegisteredServicesCache.ServiceInfo<TestServiceType> newServiceInfo( TestServiceType type, int uid, ComponentName componentName, long lastUpdateTime) { final ComponentInfo info = new ComponentInfo(); @@ -324,7 +377,7 @@ public class RegisteredServicesCacheUnitTest { return new RegisteredServicesCache.ServiceInfo<>(type, info, componentName, lastUpdateTime); } - private void addServiceInfoIntoResolveInfo(ResolveInfo resolveInfo, String packageName, + private void addServiceInfoIntoResolveInfo(TestResolveInfo resolveInfo, String packageName, String serviceName) { final ServiceInfo serviceInfo = new ServiceInfo(); serviceInfo.packageName = packageName; @@ -345,7 +398,7 @@ public class RegisteredServicesCacheUnitTest { static final String SERVICE_INTERFACE = "RegisteredServicesCacheUnitTest"; static final String SERVICE_META_DATA = "RegisteredServicesCacheUnitTest"; static final String ATTRIBUTES_NAME = "test"; - private SparseArray<Map<ResolveInfo, ServiceInfo<TestServiceType>>> mServices = + private SparseArray<Map<TestResolveInfo, ServiceInfo<TestServiceType>>> mServices = new SparseArray<>(); public TestRegisteredServicesCache(Injector<TestServiceType> injector, @@ -362,14 +415,14 @@ public class RegisteredServicesCacheUnitTest { @Override protected List<ResolveInfo> queryIntentServices(int userId) { - Map<ResolveInfo, ServiceInfo<TestServiceType>> map = mServices.get(userId, - new HashMap<ResolveInfo, ServiceInfo<TestServiceType>>()); + Map<TestResolveInfo, ServiceInfo<TestServiceType>> map = mServices.get(userId, + new HashMap<TestResolveInfo, ServiceInfo<TestServiceType>>()); return new ArrayList<>(map.keySet()); } - void addServiceForQuerying(int userId, ResolveInfo resolveInfo, + void addServiceForQuerying(int userId, TestResolveInfo resolveInfo, ServiceInfo<TestServiceType> serviceInfo) { - Map<ResolveInfo, ServiceInfo<TestServiceType>> map = mServices.get(userId); + Map<TestResolveInfo, ServiceInfo<TestServiceType>> map = mServices.get(userId); if (map == null) { map = new HashMap<>(); mServices.put(userId, map); @@ -377,16 +430,12 @@ public class RegisteredServicesCacheUnitTest { map.put(resolveInfo, serviceInfo); } - void clearServicesForQuerying() { - mServices.clear(); - } - @Override protected ServiceInfo<TestServiceType> parseServiceInfo(ResolveInfo resolveInfo, long lastUpdateTime) throws XmlPullParserException, IOException { int size = mServices.size(); for (int i = 0; i < size; i++) { - Map<ResolveInfo, ServiceInfo<TestServiceType>> map = mServices.valueAt(i); + Map<TestResolveInfo, ServiceInfo<TestServiceType>> map = mServices.valueAt(i); ServiceInfo<TestServiceType> serviceInfo = map.get(resolveInfo); if (serviceInfo != null) { return serviceInfo; @@ -400,4 +449,20 @@ public class RegisteredServicesCacheUnitTest { super.onUserRemoved(userId); } } + + /** + * Create different hash code with the same {@link android.content.pm.ResolveInfo} for testing. + */ + public static class TestResolveInfo extends ResolveInfo { + int mResolveInfoId = 0; + + @Override + public int hashCode() { + return Objects.hash(mResolveInfoId, serviceInfo); + } + + public void setResolveInfoId(int resolveInfoId) { + mResolveInfoId = resolveInfoId; + } + } } diff --git a/core/tests/coretests/src/android/security/advancedprotection/AdvancedProtectionManagerTest.java b/core/tests/coretests/src/android/security/advancedprotection/AdvancedProtectionManagerTest.java index 45864b01c795..e06ff98b0384 100644 --- a/core/tests/coretests/src/android/security/advancedprotection/AdvancedProtectionManagerTest.java +++ b/core/tests/coretests/src/android/security/advancedprotection/AdvancedProtectionManagerTest.java @@ -16,10 +16,14 @@ package android.security.advancedprotection; +import static android.os.UserManager.DISALLOW_CELLULAR_2G; +import static android.os.UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY; import static android.security.advancedprotection.AdvancedProtectionManager.ACTION_SHOW_ADVANCED_PROTECTION_SUPPORT_DIALOG; import static android.security.advancedprotection.AdvancedProtectionManager.EXTRA_SUPPORT_DIALOG_FEATURE; import static android.security.advancedprotection.AdvancedProtectionManager.EXTRA_SUPPORT_DIALOG_TYPE; import static android.security.advancedprotection.AdvancedProtectionManager.FEATURE_ID_DISALLOW_CELLULAR_2G; +import static android.security.advancedprotection.AdvancedProtectionManager.FEATURE_ID_DISALLOW_INSTALL_UNKNOWN_SOURCES; +import static android.security.advancedprotection.AdvancedProtectionManager.FEATURE_ID_ENABLE_MTE; import static android.security.advancedprotection.AdvancedProtectionManager.SUPPORT_DIALOG_TYPE_BLOCKED_INTERACTION; import static android.security.advancedprotection.AdvancedProtectionManager.SUPPORT_DIALOG_TYPE_DISABLED_SETTING; import static android.security.advancedprotection.AdvancedProtectionManager.SUPPORT_DIALOG_TYPE_UNKNOWN; @@ -37,6 +41,9 @@ import org.junit.runners.JUnit4; public class AdvancedProtectionManagerTest { private static final int FEATURE_ID_INVALID = -1; private static final int SUPPORT_DIALOG_TYPE_INVALID = -1; + //TODO(b/378931989): Switch to android.app.admin.DevicePolicyIdentifiers.MEMORY_TAGGING_POLICY + //when the appropriate flag is launched. + private static final String MEMORY_TAGGING_POLICY = "memoryTagging"; @Test public void testCreateSupportIntent_validFeature_validTypeUnknown_createsIntent() { @@ -94,4 +101,44 @@ public class AdvancedProtectionManagerTest { AdvancedProtectionManager.createSupportIntent(FEATURE_ID_INVALID, SUPPORT_DIALOG_TYPE_INVALID)); } + + @Test + public void testCreateSupportIntentForPolicy_2g_typeUnknown_createsIntentForDisabledSetting() { + Intent intent = AdvancedProtectionManager + .createSupportIntentForPolicyIdentifierOrRestriction( + DISALLOW_CELLULAR_2G, SUPPORT_DIALOG_TYPE_UNKNOWN); + + assertEquals(ACTION_SHOW_ADVANCED_PROTECTION_SUPPORT_DIALOG, intent.getAction()); + assertEquals(FEATURE_ID_DISALLOW_CELLULAR_2G, intent.getIntExtra( + EXTRA_SUPPORT_DIALOG_FEATURE, FEATURE_ID_INVALID)); + assertEquals(SUPPORT_DIALOG_TYPE_DISABLED_SETTING, intent.getIntExtra( + EXTRA_SUPPORT_DIALOG_TYPE, SUPPORT_DIALOG_TYPE_INVALID)); + } + + @Test + public void testCreateSupportIntentForPolicy_mte_typeUnknown_createsIntentForDisabledSetting() { + Intent intent = AdvancedProtectionManager + .createSupportIntentForPolicyIdentifierOrRestriction( + MEMORY_TAGGING_POLICY, SUPPORT_DIALOG_TYPE_UNKNOWN); + + assertEquals(ACTION_SHOW_ADVANCED_PROTECTION_SUPPORT_DIALOG, intent.getAction()); + assertEquals(FEATURE_ID_ENABLE_MTE, intent.getIntExtra( + EXTRA_SUPPORT_DIALOG_FEATURE, FEATURE_ID_INVALID)); + assertEquals(SUPPORT_DIALOG_TYPE_DISABLED_SETTING, intent.getIntExtra( + EXTRA_SUPPORT_DIALOG_TYPE, SUPPORT_DIALOG_TYPE_INVALID)); + } + + @Test + public void + testCreateSupportIntentForPolicy_unknownSources_typeUnknown_createsIntentForUnknown() { + Intent intent = AdvancedProtectionManager + .createSupportIntentForPolicyIdentifierOrRestriction( + DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY, SUPPORT_DIALOG_TYPE_UNKNOWN); + + assertEquals(ACTION_SHOW_ADVANCED_PROTECTION_SUPPORT_DIALOG, intent.getAction()); + assertEquals(FEATURE_ID_DISALLOW_INSTALL_UNKNOWN_SOURCES, intent.getIntExtra( + EXTRA_SUPPORT_DIALOG_FEATURE, FEATURE_ID_INVALID)); + assertEquals(SUPPORT_DIALOG_TYPE_UNKNOWN, intent.getIntExtra( + EXTRA_SUPPORT_DIALOG_TYPE, SUPPORT_DIALOG_TYPE_INVALID)); + } } diff --git a/core/tests/coretests/src/android/window/BackTouchTrackerTest.kt b/core/tests/coretests/src/android/window/BackTouchTrackerTest.kt index ad68e385459e..381b566018c7 100644 --- a/core/tests/coretests/src/android/window/BackTouchTrackerTest.kt +++ b/core/tests/coretests/src/android/window/BackTouchTrackerTest.kt @@ -37,7 +37,7 @@ class BackTouchTrackerTest { fun generatesProgress_onStart() { val linearTracker = linearTouchTracker() linearTracker.setGestureStartLocation(INITIAL_X_LEFT_EDGE, 0f, BackEvent.EDGE_LEFT) - val event = linearTracker.createStartEvent() + val event = linearTracker.createStartEvent(null) assertEquals(0f, event.progress, 0f) } diff --git a/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java b/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java index 66524d1c1d2a..215c1623a530 100644 --- a/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java +++ b/core/tests/coretests/src/android/window/WindowOnBackInvokedDispatcherTest.java @@ -695,7 +695,8 @@ public class WindowOnBackInvokedDispatcherTest { /* frameTimeMillis = */ 0, /* progress = */ progress, /* triggerBack = */ false, - /* swipeEdge = */ BackEvent.EDGE_LEFT); + /* swipeEdge = */ BackEvent.EDGE_LEFT, + /* departingAnimationTarget = */ null); } private void verifyImeCallackRegistrations() throws RemoteException { diff --git a/core/tests/coretests/src/com/android/internal/app/MediaRouteDialogPresenterTest.kt b/core/tests/coretests/src/com/android/internal/app/MediaRouteDialogPresenterTest.kt new file mode 100644 index 000000000000..e80d3a6e625f --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/app/MediaRouteDialogPresenterTest.kt @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.app + +import android.content.Context +import android.media.MediaRouter +import android.testing.TestableLooper.RunWithLooper +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.google.common.truth.Truth.assertThat +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers.anyInt +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.mock +import org.mockito.kotlin.stub + +@SmallTest +@RunWithLooper(setAsMainLooper = true) +@RunWith(AndroidJUnit4::class) +class MediaRouteDialogPresenterTest { + private var selectedRoute: MediaRouter.RouteInfo = mock() + private var mediaRouter: MediaRouter = mock<MediaRouter> { + on { selectedRoute } doReturn selectedRoute + } + private var context: Context = mock<Context> { + on { getSystemServiceName(MediaRouter::class.java) } doReturn Context.MEDIA_ROUTER_SERVICE + on { getSystemService(MediaRouter::class.java) } doReturn mediaRouter + } + + @Test + fun shouldShowChooserDialog_routeNotDefault_returnsFalse() { + selectedRoute.stub { + on { isDefault } doReturn false + on { matchesTypes(anyInt()) } doReturn true + } + + assertThat(MediaRouteDialogPresenter.shouldShowChooserDialog( + context, MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY)) + .isEqualTo(false) + } + + @Test + fun shouldShowChooserDialog_routeDefault_returnsTrue() { + selectedRoute.stub { + on { isDefault } doReturn true + on { matchesTypes(anyInt()) } doReturn true + } + + assertThat(MediaRouteDialogPresenter.shouldShowChooserDialog( + context, MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY)) + .isEqualTo(true) + } + + @Test + fun shouldShowChooserDialog_routeNotMatch_returnsTrue() { + selectedRoute.stub { + on { isDefault } doReturn false + on { matchesTypes(anyInt()) } doReturn false + } + + assertThat(MediaRouteDialogPresenter.shouldShowChooserDialog( + context, MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY)) + .isEqualTo(true) + } + + @Test + fun shouldShowChooserDialog_routeDefaultAndNotMatch_returnsTrue() { + selectedRoute.stub { + on { isDefault } doReturn true + on { matchesTypes(anyInt()) } doReturn false + } + + assertThat(MediaRouteDialogPresenter.shouldShowChooserDialog( + context, MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY)) + .isEqualTo(true) + } +}
\ No newline at end of file diff --git a/core/tests/coretests/src/com/android/internal/statusbar/DisableStatesTest.java b/core/tests/coretests/src/com/android/internal/statusbar/DisableStatesTest.java new file mode 100644 index 000000000000..5b82696b81c3 --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/statusbar/DisableStatesTest.java @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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.statusbar; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import android.os.Parcel; +import android.util.Pair; + +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.filters.SmallTest; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import java.util.HashMap; +import java.util.Map; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public class DisableStatesTest { + + @Test + public void testParcelable() { + Map<Integer, Pair<Integer, Integer>> displaysWithStates = new HashMap<>(); + displaysWithStates.put(1, new Pair<>(10, 20)); + displaysWithStates.put(2, new Pair<>(30, 40)); + boolean animate = true; + DisableStates original = new DisableStates(displaysWithStates, animate); + + Parcel parcel = Parcel.obtain(); + original.writeToParcel(parcel, 0); + parcel.setDataPosition(0); + DisableStates restored = DisableStates.CREATOR.createFromParcel(parcel); + + assertNotNull(restored); + assertEquals(original.displaysWithStates.size(), restored.displaysWithStates.size()); + for (Map.Entry<Integer, Pair<Integer, Integer>> entry : + original.displaysWithStates.entrySet()) { + int displayId = entry.getKey(); + Pair<Integer, Integer> originalDisplayStates = entry.getValue(); + Pair<Integer, Integer> restoredDisplayStates = restored.displaysWithStates.get( + displayId); + assertEquals(originalDisplayStates.first, restoredDisplayStates.first); + assertEquals(originalDisplayStates.second, restoredDisplayStates.second); + } + assertEquals(original.animate, restored.animate); + } +} diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java index d1aca34c7b8d..39cd4a89aae6 100644 --- a/graphics/java/android/graphics/Typeface.java +++ b/graphics/java/android/graphics/Typeface.java @@ -80,6 +80,7 @@ import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.function.BiConsumer; /** * The Typeface class specifies the typeface and intrinsic style of a font. @@ -1550,14 +1551,21 @@ public class Typeface { setDefault(defaults.get(0)); ArrayList<Typeface> oldGenerics = new ArrayList<>(); - oldGenerics.add(sSystemFontMap.get("sans-serif")); - sSystemFontMap.put("sans-serif", genericFamilies.get(0)); + BiConsumer<Typeface, String> swapTypeface = (typeface, key) -> { + oldGenerics.add(sSystemFontMap.get(key)); + sSystemFontMap.put(key, typeface); + }; - oldGenerics.add(sSystemFontMap.get("serif")); - sSystemFontMap.put("serif", genericFamilies.get(1)); + Typeface sansSerif = genericFamilies.get(0); + swapTypeface.accept(sansSerif, "sans-serif"); + swapTypeface.accept(Typeface.create(sansSerif, 100, false), "sans-serif-thin"); + swapTypeface.accept(Typeface.create(sansSerif, 300, false), "sans-serif-light"); + swapTypeface.accept(Typeface.create(sansSerif, 500, false), "sans-serif-medium"); + swapTypeface.accept(Typeface.create(sansSerif, 700, false), "sans-serif-bold"); + swapTypeface.accept(Typeface.create(sansSerif, 900, false), "sans-serif-black"); - oldGenerics.add(sSystemFontMap.get("monospace")); - sSystemFontMap.put("monospace", genericFamilies.get(2)); + swapTypeface.accept(genericFamilies.get(1), "serif"); + swapTypeface.accept(genericFamilies.get(2), "monospace"); return new Pair<>(oldDefaults, oldGenerics); } diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml index bfaa40771894..c33669636be4 100644 --- a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml +++ b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu.xml @@ -186,14 +186,13 @@ <ImageButton android:id="@+id/open_by_default_button" - android:layout_width="20dp" - android:layout_height="20dp" android:layout_gravity="end|center_vertical" - android:layout_marginStart="8dp" - android:layout_marginEnd="16dp" + android:paddingStart="12dp" + android:paddingEnd="16dp" android:contentDescription="@string/open_by_default_settings_text" android:src="@drawable/desktop_mode_ic_handle_menu_open_by_default_settings" - android:tint="@androidprv:color/materialColorOnSurface"/> + android:tint="@androidprv:color/materialColorOnSurface" + style="@style/DesktopModeHandleMenuWindowingButton"/> </LinearLayout> </LinearLayout> diff --git a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_action_button.xml b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_action_button.xml index 35e7de0e7c1e..0e5843f3e592 100644 --- a/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_action_button.xml +++ b/libs/WindowManager/Shell/res/layout/desktop_mode_window_decor_handle_menu_action_button.xml @@ -14,18 +14,7 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License. --> -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:app="http://schemas.android.com/apk/res-auto" - xmlns:tools="http://schemas.android.com/tools" - android:id="@+id/action_button" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:gravity="start|center_vertical" - android:paddingHorizontal="16dp" - android:importantForAccessibility="yes" - android:orientation="horizontal" - android:background="?android:attr/selectableItemBackground"> - +<merge xmlns:android="http://schemas.android.com/apk/res/android"> <ImageView android:id="@+id/image" android:importantForAccessibility="no" @@ -35,4 +24,4 @@ android:id="@+id/label" android:importantForAccessibility="no" style="@style/DesktopModeHandleMenuActionButtonTextView"/> -</LinearLayout> +</merge> diff --git a/libs/WindowManager/Shell/res/values-af/strings.xml b/libs/WindowManager/Shell/res/values-af/strings.xml index 1491c70023e7..05c4c56a5c81 100644 --- a/libs/WindowManager/Shell/res/values-af/strings.xml +++ b/libs/WindowManager/Shell/res/values-af/strings.xml @@ -100,8 +100,7 @@ <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nie opgelos nie?\nTik om terug te stel"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Geen kamerakwessies nie? Tik om toe te maak."</string> <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Die appkieslys kan hier gevind word"</string> - <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) --> - <skip /> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Maak werkskermvensters oop om verskeie apps terselfdertyd oop te maak"</string> <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Keer enige tyd terug na volskerm vanaf die appkieslys"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Sien en doen meer"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Sleep ’n ander app in vir verdeelde skerm"</string> @@ -122,8 +121,7 @@ <string name="handle_text" msgid="4419667835599523257">"Apphandvatsel"</string> <string name="app_icon_text" msgid="2823268023931811747">"Appikoon"</string> <string name="fullscreen_text" msgid="1162316685217676079">"Volskerm"</string> - <!-- no translation found for desktop_text (9058641752519570266) --> - <skip /> + <string name="desktop_text" msgid="9058641752519570266">"Werkskermvensters"</string> <string name="split_screen_text" msgid="1396336058129570886">"Verdeelde skerm"</string> <string name="more_button_text" msgid="3655388105592893530">"Meer"</string> <string name="float_button_text" msgid="9221657008391364581">"Sweef"</string> @@ -136,8 +134,7 @@ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Verander aspekverhouding"</string> <string name="close_text" msgid="4986518933445178928">"Maak toe"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Maak kieslys toe"</string> - <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) --> - <skip /> + <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (werkskermvensters)"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimeer skerm"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Verander grootte"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"App kan nie hierheen geskuif word nie"</string> diff --git a/libs/WindowManager/Shell/res/values-am/strings.xml b/libs/WindowManager/Shell/res/values-am/strings.xml index 8bd602c08508..450419dcc40d 100644 --- a/libs/WindowManager/Shell/res/values-am/strings.xml +++ b/libs/WindowManager/Shell/res/values-am/strings.xml @@ -100,8 +100,7 @@ <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"አልተስተካከለም?\nለማህደር መታ ያድርጉ"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ምንም የካሜራ ችግሮች የሉም? ለማሰናበት መታ ያድርጉ።"</string> <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"የመተግበሪያ ምናሌው እዚህ መገኘት ይችላል"</string> - <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) --> - <skip /> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"በርካታ መተግበሪያዎችን በአንድ ላይ ለመክፈት ወደ የዴስክቶፕ መስኮት ይግቡ"</string> <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"በማንኛውም ጊዜ ከመተግበሪያ ምናሌው ላይ ወደ ሙሉ ገጽ እይታ ይመለሱ"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"ተጨማሪ ይመልከቱ እና ያድርጉ"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"ለተከፈለ ማያ ገፅ ሌላ መተግበሪያ ይጎትቱ"</string> @@ -122,8 +121,7 @@ <string name="handle_text" msgid="4419667835599523257">"የመተግበሪያ መያዣ"</string> <string name="app_icon_text" msgid="2823268023931811747">"የመተግበሪያ አዶ"</string> <string name="fullscreen_text" msgid="1162316685217676079">"ሙሉ ማያ"</string> - <!-- no translation found for desktop_text (9058641752519570266) --> - <skip /> + <string name="desktop_text" msgid="9058641752519570266">"ዴስክቶፕ መስኮት"</string> <string name="split_screen_text" msgid="1396336058129570886">"የተከፈለ ማያ ገፅ"</string> <string name="more_button_text" msgid="3655388105592893530">"ተጨማሪ"</string> <string name="float_button_text" msgid="9221657008391364581">"ተንሳፋፊ"</string> @@ -136,8 +134,7 @@ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"ምጥጥነ ገፅታ ለውጥ"</string> <string name="close_text" msgid="4986518933445178928">"ዝጋ"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"ምናሌ ዝጋ"</string> - <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) --> - <skip /> + <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ዴስክቶፕ መስኮት)"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"የማያ ገጹ መጠን አሳድግ"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"መጠን ቀይር"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"መተግበሪያ ወደዚህ መንቀሳቀስ አይችልም"</string> diff --git a/libs/WindowManager/Shell/res/values-as/strings.xml b/libs/WindowManager/Shell/res/values-as/strings.xml index 6872df6acbc9..b1826db57a2c 100644 --- a/libs/WindowManager/Shell/res/values-as/strings.xml +++ b/libs/WindowManager/Shell/res/values-as/strings.xml @@ -100,8 +100,7 @@ <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"এইটো সমাধান কৰা নাই নেকি?\nপূৰ্বাৱস্থালৈ নিবলৈ টিপক"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"কেমেৰাৰ কোনো সমস্যা নাই নেকি? অগ্ৰাহ্য কৰিবলৈ টিপক।"</string> <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"এপৰ মেনু ইয়াত বিচাৰি পোৱা যাব"</string> - <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) --> - <skip /> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"একেলগে একাধিক এপ্ খুলিবলৈ ডেস্কটপ ৱিণ্ড’ৱিঙলৈ যাওক"</string> <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"এপৰ মেনুৰ পৰা যিকোনো সময়তে পূৰ্ণ স্ক্ৰীনলৈ উভতি যাওক"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"চাওক আৰু অধিক কৰক"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"বিভাজিত স্ক্ৰীনৰ বাবে অন্য এটা এপ্ টানি আনি এৰক"</string> @@ -122,8 +121,7 @@ <string name="handle_text" msgid="4419667835599523257">"এপৰ হেণ্ডেল"</string> <string name="app_icon_text" msgid="2823268023931811747">"এপৰ চিহ্ন"</string> <string name="fullscreen_text" msgid="1162316685217676079">"সম্পূৰ্ণ স্ক্ৰীন"</string> - <!-- no translation found for desktop_text (9058641752519570266) --> - <skip /> + <string name="desktop_text" msgid="9058641752519570266">"ডেস্কটপ ৱিণ্ড’ৱিং"</string> <string name="split_screen_text" msgid="1396336058129570886">"বিভাজিত স্ক্ৰীন"</string> <string name="more_button_text" msgid="3655388105592893530">"অধিক"</string> <string name="float_button_text" msgid="9221657008391364581">"ওপঙা"</string> @@ -136,8 +134,7 @@ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"আকাৰৰ অনুপাত সলনি কৰক"</string> <string name="close_text" msgid="4986518933445178928">"বন্ধ কৰক"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"মেনু বন্ধ কৰক"</string> - <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) --> - <skip /> + <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ডেস্কটপ ৱিণ্ড’ৱিং)"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"স্ক্ৰীন মেক্সিমাইজ কৰক"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"আকাৰ সলনি কৰক"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"ইয়ালৈ এপ্টো আনিব নোৱাৰি"</string> diff --git a/libs/WindowManager/Shell/res/values-az/strings.xml b/libs/WindowManager/Shell/res/values-az/strings.xml index 46309411d0c9..c5493b573d0f 100644 --- a/libs/WindowManager/Shell/res/values-az/strings.xml +++ b/libs/WindowManager/Shell/res/values-az/strings.xml @@ -100,8 +100,7 @@ <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Düzəltməmisiniz?\nGeri qaytarmaq üçün toxunun"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Kamera problemi yoxdur? Qapatmaq üçün toxunun."</string> <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Tətbiq menyusunu burada tapa bilərsiniz"</string> - <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) --> - <skip /> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Bir neçə tətbiqi birlikdə açmaq üçün masaüstü pəncərə rejiminə daxil olun"</string> <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"İstənilən vaxt tətbiq menyusundan tam ekrana qayıdın"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Ardını görün və edin"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Bölünmüş ekran üçün başqa tətbiq sürüşdürün"</string> @@ -122,8 +121,7 @@ <string name="handle_text" msgid="4419667835599523257">"Tətbiq ləqəbi"</string> <string name="app_icon_text" msgid="2823268023931811747">"Tətbiq ikonası"</string> <string name="fullscreen_text" msgid="1162316685217676079">"Tam Ekran"</string> - <!-- no translation found for desktop_text (9058641752519570266) --> - <skip /> + <string name="desktop_text" msgid="9058641752519570266">"Masaüstü pəncərə rejimi"</string> <string name="split_screen_text" msgid="1396336058129570886">"Bölünmüş Ekran"</string> <string name="more_button_text" msgid="3655388105592893530">"Ardı"</string> <string name="float_button_text" msgid="9221657008391364581">"Üzən pəncərə"</string> @@ -136,8 +134,7 @@ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Tərəflər nisbətini dəyişin"</string> <string name="close_text" msgid="4986518933445178928">"Bağlayın"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Menyunu bağlayın"</string> - <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) --> - <skip /> + <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Masaüstü pəncərə rejimi)"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Ekranı maksimum böyüdün"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Ölçüsünü dəyişin"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Tətbiqi bura köçürmək mümkün deyil"</string> diff --git a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml index 4af564833ba1..307c47ab48eb 100644 --- a/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml +++ b/libs/WindowManager/Shell/res/values-b+sr+Latn/strings.xml @@ -100,8 +100,7 @@ <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Problem nije rešen?\nDodirnite da biste vratili"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nemate problema sa kamerom? Dodirnite da biste odbacili."</string> <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Meni aplikacije možete da pronađete ovde"</string> - <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) --> - <skip /> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Uđite u prozorski prikaz za računare da biste istovremeno otvorili više aplikacija"</string> <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Vratite se na ceo ekran bilo kada iz menija aplikacije"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Vidite i uradite više"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Prevucite drugu aplikaciju da biste koristili podeljeni ekran"</string> @@ -122,8 +121,7 @@ <string name="handle_text" msgid="4419667835599523257">"Identifikator aplikacije"</string> <string name="app_icon_text" msgid="2823268023931811747">"Ikona aplikacije"</string> <string name="fullscreen_text" msgid="1162316685217676079">"Preko celog ekrana"</string> - <!-- no translation found for desktop_text (9058641752519570266) --> - <skip /> + <string name="desktop_text" msgid="9058641752519570266">"Prozorski prikaz za računare"</string> <string name="split_screen_text" msgid="1396336058129570886">"Podeljeni ekran"</string> <string name="more_button_text" msgid="3655388105592893530">"Još"</string> <string name="float_button_text" msgid="9221657008391364581">"Plutajuće"</string> @@ -136,8 +134,7 @@ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Promeni razmeru"</string> <string name="close_text" msgid="4986518933445178928">"Zatvorite"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Zatvorite meni"</string> - <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) --> - <skip /> + <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (prozorski prikaz za računare)"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Povećaj ekran"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Prilagodi"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Aplikacija ne može da se premesti ovde"</string> diff --git a/libs/WindowManager/Shell/res/values-be/strings.xml b/libs/WindowManager/Shell/res/values-be/strings.xml index 7719396b01d1..c53e37c67cfc 100644 --- a/libs/WindowManager/Shell/res/values-be/strings.xml +++ b/libs/WindowManager/Shell/res/values-be/strings.xml @@ -100,8 +100,7 @@ <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Не ўдалося выправіць?\nНацісніце, каб аднавіць"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Ніякіх праблем з камерай? Націсніце, каб адхіліць."</string> <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Меню праграмы шукайце тут"</string> - <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) --> - <skip /> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Каб адкрыць некалькі праграм адначасова, увайдзіце ў рэжым вокнаў працоўнага стала"</string> <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Вы можаце вярнуцца ў поўнаэкранны рэжым у любы час з меню праграмы"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Адначасова выконвайце розныя задачы"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Перацягніце іншую праграму, каб выкарыстоўваць падзелены экран"</string> @@ -122,8 +121,7 @@ <string name="handle_text" msgid="4419667835599523257">"Маркер праграмы"</string> <string name="app_icon_text" msgid="2823268023931811747">"Значок праграмы"</string> <string name="fullscreen_text" msgid="1162316685217676079">"На ўвесь экран"</string> - <!-- no translation found for desktop_text (9058641752519570266) --> - <skip /> + <string name="desktop_text" msgid="9058641752519570266">"Рэжым вокнаў працоўнага стала"</string> <string name="split_screen_text" msgid="1396336058129570886">"Падзяліць экран"</string> <string name="more_button_text" msgid="3655388105592893530">"Яшчэ"</string> <string name="float_button_text" msgid="9221657008391364581">"Зрабіць рухомым акном"</string> @@ -136,8 +134,7 @@ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Змяніць суадносіны бакоў"</string> <string name="close_text" msgid="4986518933445178928">"Закрыць"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Закрыць меню"</string> - <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) --> - <skip /> + <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (рэжым вокнаў працоўнага стала)"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Разгарнуць на ўвесь экран"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Змяніць памер"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Нельга перамясціць сюды праграму"</string> diff --git a/libs/WindowManager/Shell/res/values-bg/strings.xml b/libs/WindowManager/Shell/res/values-bg/strings.xml index 514556e30fe0..29af2ed1c38b 100644 --- a/libs/WindowManager/Shell/res/values-bg/strings.xml +++ b/libs/WindowManager/Shell/res/values-bg/strings.xml @@ -100,8 +100,7 @@ <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Проблемът не се отстрани?\nДокоснете за връщане в предишното състояние"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Нямате проблеми с камерата? Докоснете, за да отхвърлите."</string> <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Можете да намерите менюто на приложението тук"</string> - <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) --> - <skip /> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Активирайте режима за настолни компютри, за да отворите няколко приложения едновременно"</string> <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Преминете към цял екран по всяко време от менюто на приложението"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Преглеждайте и правете повече неща"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Преместете друго приложение с плъзгане, за да преминете в режим за разделен екран"</string> @@ -122,8 +121,7 @@ <string name="handle_text" msgid="4419667835599523257">"Манипулатор за приложението"</string> <string name="app_icon_text" msgid="2823268023931811747">"Икона на приложението"</string> <string name="fullscreen_text" msgid="1162316685217676079">"Цял екран"</string> - <!-- no translation found for desktop_text (9058641752519570266) --> - <skip /> + <string name="desktop_text" msgid="9058641752519570266">"Режим за настолни компютри"</string> <string name="split_screen_text" msgid="1396336058129570886">"Разделяне на екрана"</string> <string name="more_button_text" msgid="3655388105592893530">"Още"</string> <string name="float_button_text" msgid="9221657008391364581">"Плаващо"</string> @@ -136,8 +134,7 @@ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Промяна на съотношението"</string> <string name="close_text" msgid="4986518933445178928">"Затваряне"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Затваряне на менюто"</string> - <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) --> - <skip /> + <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (режим за настолни компютри)"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Увеличаване на екрана"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Нов размер"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Приложението не може да бъде преместено тук"</string> diff --git a/libs/WindowManager/Shell/res/values-bs/strings.xml b/libs/WindowManager/Shell/res/values-bs/strings.xml index 23c467c0b4ba..537afdcc6de4 100644 --- a/libs/WindowManager/Shell/res/values-bs/strings.xml +++ b/libs/WindowManager/Shell/res/values-bs/strings.xml @@ -100,8 +100,7 @@ <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nije popravljeno?\nDodirnite da vratite"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nema problema s kamerom? Dodirnite da odbacite."</string> <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Ovdje možete pronaći meni aplikacije"</string> - <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) --> - <skip /> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Otvorite prikaz u prozorima na računalu da biste otvorili više aplikacija zajedno"</string> <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Povratak na prikaz preko cijelog ekrana bilo kada putem menija aplikacije"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Pogledajte i učinite više"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Prevucite još jednu aplikaciju za podijeljeni ekran"</string> @@ -122,8 +121,7 @@ <string name="handle_text" msgid="4419667835599523257">"Ručica aplikacije"</string> <string name="app_icon_text" msgid="2823268023931811747">"Ikona aplikacije"</string> <string name="fullscreen_text" msgid="1162316685217676079">"Cijeli ekran"</string> - <!-- no translation found for desktop_text (9058641752519570266) --> - <skip /> + <string name="desktop_text" msgid="9058641752519570266">"Prikaz u prozorima na računalu"</string> <string name="split_screen_text" msgid="1396336058129570886">"Podijeljeni ekran"</string> <string name="more_button_text" msgid="3655388105592893530">"Više"</string> <string name="float_button_text" msgid="9221657008391364581">"Lebdeći"</string> @@ -136,8 +134,7 @@ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Promjena formata slike"</string> <string name="close_text" msgid="4986518933445178928">"Zatvaranje"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Zatvaranje menija"</string> - <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) --> - <skip /> + <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (prikaz u prozorima na računalu)"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimiziraj ekran"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Promijeni veličinu"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Ne možete premjestiti aplikaciju ovdje"</string> diff --git a/libs/WindowManager/Shell/res/values-ca/strings.xml b/libs/WindowManager/Shell/res/values-ca/strings.xml index 893d16e6155e..42b07ef3d049 100644 --- a/libs/WindowManager/Shell/res/values-ca/strings.xml +++ b/libs/WindowManager/Shell/res/values-ca/strings.xml @@ -100,8 +100,7 @@ <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"El problema no s\'ha resolt?\nToca per desfer els canvis"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"No tens cap problema amb la càmera? Toca per ignorar."</string> <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Pots trobar el menú de l\'aplicació aquí"</string> - <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) --> - <skip /> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Accedeix a l\'enfinestrament d\'escriptori per obrir diverses aplicacions alhora"</string> <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Torna a la pantalla completa en qualsevol moment des del menú de l\'aplicació"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Consulta i fes més coses"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Arrossega una altra aplicació per utilitzar la pantalla dividida"</string> @@ -122,8 +121,7 @@ <string name="handle_text" msgid="4419667835599523257">"Identificador de l\'aplicació"</string> <string name="app_icon_text" msgid="2823268023931811747">"Icona de l\'aplicació"</string> <string name="fullscreen_text" msgid="1162316685217676079">"Pantalla completa"</string> - <!-- no translation found for desktop_text (9058641752519570266) --> - <skip /> + <string name="desktop_text" msgid="9058641752519570266">"Enfinestrament d\'escriptori"</string> <string name="split_screen_text" msgid="1396336058129570886">"Pantalla dividida"</string> <string name="more_button_text" msgid="3655388105592893530">"Més"</string> <string name="float_button_text" msgid="9221657008391364581">"Flotant"</string> @@ -136,8 +134,7 @@ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Canvia la relació d\'aspecte"</string> <string name="close_text" msgid="4986518933445178928">"Tanca"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Tanca el menú"</string> - <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) --> - <skip /> + <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (enfinestrament d\'escriptori)"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximitza la pantalla"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Canvia la mida"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"L\'aplicació no es pot moure aquí"</string> diff --git a/libs/WindowManager/Shell/res/values-cs/strings.xml b/libs/WindowManager/Shell/res/values-cs/strings.xml index a96344a0a365..44548682cbbc 100644 --- a/libs/WindowManager/Shell/res/values-cs/strings.xml +++ b/libs/WindowManager/Shell/res/values-cs/strings.xml @@ -100,8 +100,7 @@ <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nepomohlo to?\nKlepnutím se vrátíte"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Žádné problémy s fotoaparátem? Klepnutím zavřete."</string> <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Najdete tu nabídku aplikace"</string> - <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) --> - <skip /> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Pokud chcete otevřít několik aplikací současně, přejděte do režimu s okny na ploše"</string> <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Na celou obrazovku se můžete kdykoli vrátit z nabídky aplikace"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Lepší zobrazení a více možností"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Přetáhnutím druhé aplikace použijete rozdělenou obrazovku"</string> @@ -122,8 +121,7 @@ <string name="handle_text" msgid="4419667835599523257">"Popisovač aplikace"</string> <string name="app_icon_text" msgid="2823268023931811747">"Ikona aplikace"</string> <string name="fullscreen_text" msgid="1162316685217676079">"Celá obrazovka"</string> - <!-- no translation found for desktop_text (9058641752519570266) --> - <skip /> + <string name="desktop_text" msgid="9058641752519570266">"Okna na ploše"</string> <string name="split_screen_text" msgid="1396336058129570886">"Rozdělená obrazovka"</string> <string name="more_button_text" msgid="3655388105592893530">"Více"</string> <string name="float_button_text" msgid="9221657008391364581">"Plovoucí"</string> @@ -136,8 +134,7 @@ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Změnit poměr stran"</string> <string name="close_text" msgid="4986518933445178928">"Zavřít"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Zavřít nabídku"</string> - <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) --> - <skip /> + <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (okna na ploše)"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximalizovat obrazovku"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Změnit velikost"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Aplikaci sem nelze přesunout"</string> diff --git a/libs/WindowManager/Shell/res/values-da/strings.xml b/libs/WindowManager/Shell/res/values-da/strings.xml index 7d28fc842e59..4d14f93d7b77 100644 --- a/libs/WindowManager/Shell/res/values-da/strings.xml +++ b/libs/WindowManager/Shell/res/values-da/strings.xml @@ -100,8 +100,7 @@ <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Løste det ikke problemet?\nTryk for at fortryde"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Har du ingen problemer med dit kamera? Tryk for at afvise."</string> <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Appmenuen kan findes her"</string> - <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) --> - <skip /> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Brug vinduer på skrivebordet for at åbne flere apps på én gang"</string> <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Gå tilbage til fuld skærm når som helst via appmenuen"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Se og gør mere"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Træk en anden app hertil for at bruge opdelt skærm"</string> @@ -122,8 +121,7 @@ <string name="handle_text" msgid="4419667835599523257">"Apphåndtag"</string> <string name="app_icon_text" msgid="2823268023931811747">"Appikon"</string> <string name="fullscreen_text" msgid="1162316685217676079">"Fuld skærm"</string> - <!-- no translation found for desktop_text (9058641752519570266) --> - <skip /> + <string name="desktop_text" msgid="9058641752519570266">"Vinduer på skrivebordet"</string> <string name="split_screen_text" msgid="1396336058129570886">"Opdelt skærm"</string> <string name="more_button_text" msgid="3655388105592893530">"Mere"</string> <string name="float_button_text" msgid="9221657008391364581">"Svævende"</string> @@ -136,8 +134,7 @@ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Skift billedformat"</string> <string name="close_text" msgid="4986518933445178928">"Luk"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Luk menu"</string> - <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) --> - <skip /> + <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (vinduer på skrivebordet)"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimér skærm"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Tilpas størrelse"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Apps kan ikke flyttes hertil"</string> diff --git a/libs/WindowManager/Shell/res/values-de/strings.xml b/libs/WindowManager/Shell/res/values-de/strings.xml index 4cc11243b8bd..82bbfc4eff29 100644 --- a/libs/WindowManager/Shell/res/values-de/strings.xml +++ b/libs/WindowManager/Shell/res/values-de/strings.xml @@ -100,8 +100,7 @@ <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Das Problem ist nicht behoben?\nZum Rückgängigmachen tippen."</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Keine Probleme mit der Kamera? Zum Schließen tippen."</string> <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Das App-Menü findest du hier"</string> - <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) --> - <skip /> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Über ein Desktop-Freiform-Fenster kannst du mehrere Apps gleichzeitig öffnen"</string> <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Über das App-Menü kannst du jederzeit zum Vollbildmodus zurückkehren"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Mehr sehen und erledigen"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Für Splitscreen-Modus weitere App hineinziehen"</string> @@ -122,8 +121,7 @@ <string name="handle_text" msgid="4419667835599523257">"App-Ziehpunkt"</string> <string name="app_icon_text" msgid="2823268023931811747">"App-Symbol"</string> <string name="fullscreen_text" msgid="1162316685217676079">"Vollbild"</string> - <!-- no translation found for desktop_text (9058641752519570266) --> - <skip /> + <string name="desktop_text" msgid="9058641752519570266">"Desktop-Freiform-Fenster"</string> <string name="split_screen_text" msgid="1396336058129570886">"Splitscreen"</string> <string name="more_button_text" msgid="3655388105592893530">"Mehr"</string> <string name="float_button_text" msgid="9221657008391364581">"Frei schwebend"</string> @@ -136,8 +134,7 @@ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Seitenverhältnis ändern"</string> <string name="close_text" msgid="4986518933445178928">"Schließen"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Menü schließen"</string> - <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) --> - <skip /> + <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Desktop-Freiform-Fenster)"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Bildschirm maximieren"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Größe ändern"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Die App kann nicht hierher verschoben werden"</string> diff --git a/libs/WindowManager/Shell/res/values-el/strings.xml b/libs/WindowManager/Shell/res/values-el/strings.xml index 0fb17ecdb278..a8696aff1f0c 100644 --- a/libs/WindowManager/Shell/res/values-el/strings.xml +++ b/libs/WindowManager/Shell/res/values-el/strings.xml @@ -100,8 +100,7 @@ <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Δεν διορθώθηκε;\nΠατήστε για επαναφορά."</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Δεν αντιμετωπίζετε προβλήματα με την κάμερα; Πατήστε για παράβλεψη."</string> <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Μπορείτε να βρείτε το μενού εφαρμογών εδώ"</string> - <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) --> - <skip /> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Μεταβείτε στην προσαρμογή σε παράθυρο στην επιφάνεια εργασίας, για να ανοίξετε πολλές εφαρμογές μαζί"</string> <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Επιστρέψτε στην πλήρη οθόνη ανά πάσα στιγμή από το μενού της εφαρμογής"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Δείτε και κάντε περισσότερα"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Σύρετε σε μια άλλη εφαρμογή για διαχωρισμό οθόνης."</string> @@ -122,8 +121,7 @@ <string name="handle_text" msgid="4419667835599523257">"Λαβή εφαρμογής"</string> <string name="app_icon_text" msgid="2823268023931811747">"Εικονίδιο εφαρμογής"</string> <string name="fullscreen_text" msgid="1162316685217676079">"Πλήρης οθόνη"</string> - <!-- no translation found for desktop_text (9058641752519570266) --> - <skip /> + <string name="desktop_text" msgid="9058641752519570266">"Προσαρμογή σε παράθυρο στην επιφάνεια εργασίας"</string> <string name="split_screen_text" msgid="1396336058129570886">"Διαχωρισμός οθόνης"</string> <string name="more_button_text" msgid="3655388105592893530">"Περισσότερα"</string> <string name="float_button_text" msgid="9221657008391364581">"Κινούμενο"</string> @@ -136,8 +134,7 @@ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Αλλαγή λόγου διαστάσεων"</string> <string name="close_text" msgid="4986518933445178928">"Κλείσιμο"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Κλείσιμο μενού"</string> - <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) --> - <skip /> + <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Προσαρμογή σε παράθυρο στην επιφάνεια εργασίας)"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Μεγιστοποίηση οθόνης"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Αλλαγή μεγέθους"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Δεν είναι δυνατή η μετακίνηση της εφαρμογής εδώ"</string> diff --git a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml index 2087bb4ad579..61b68a2b2515 100644 --- a/libs/WindowManager/Shell/res/values-en-rAU/strings.xml +++ b/libs/WindowManager/Shell/res/values-en-rAU/strings.xml @@ -100,8 +100,7 @@ <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Didn’t fix it?\nTap to revert"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"No camera issues? Tap to dismiss."</string> <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"The app menu can be found here"</string> - <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) --> - <skip /> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Enter desktop windowing to open multiple apps together"</string> <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Return to full screen at any time from the app menu"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"See and do more"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Drag in another app for split screen"</string> @@ -122,8 +121,7 @@ <string name="handle_text" msgid="4419667835599523257">"App handle"</string> <string name="app_icon_text" msgid="2823268023931811747">"App icon"</string> <string name="fullscreen_text" msgid="1162316685217676079">"Full screen"</string> - <!-- no translation found for desktop_text (9058641752519570266) --> - <skip /> + <string name="desktop_text" msgid="9058641752519570266">"Desktop windowing"</string> <string name="split_screen_text" msgid="1396336058129570886">"Split screen"</string> <string name="more_button_text" msgid="3655388105592893530">"More"</string> <string name="float_button_text" msgid="9221657008391364581">"Float"</string> @@ -136,8 +134,7 @@ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Change aspect ratio"</string> <string name="close_text" msgid="4986518933445178928">"Close"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Close menu"</string> - <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) --> - <skip /> + <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (desktop windowing)"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximise screen"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Resize"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"App can\'t be moved here"</string> diff --git a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml index 2087bb4ad579..61b68a2b2515 100644 --- a/libs/WindowManager/Shell/res/values-en-rGB/strings.xml +++ b/libs/WindowManager/Shell/res/values-en-rGB/strings.xml @@ -100,8 +100,7 @@ <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Didn’t fix it?\nTap to revert"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"No camera issues? Tap to dismiss."</string> <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"The app menu can be found here"</string> - <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) --> - <skip /> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Enter desktop windowing to open multiple apps together"</string> <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Return to full screen at any time from the app menu"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"See and do more"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Drag in another app for split screen"</string> @@ -122,8 +121,7 @@ <string name="handle_text" msgid="4419667835599523257">"App handle"</string> <string name="app_icon_text" msgid="2823268023931811747">"App icon"</string> <string name="fullscreen_text" msgid="1162316685217676079">"Full screen"</string> - <!-- no translation found for desktop_text (9058641752519570266) --> - <skip /> + <string name="desktop_text" msgid="9058641752519570266">"Desktop windowing"</string> <string name="split_screen_text" msgid="1396336058129570886">"Split screen"</string> <string name="more_button_text" msgid="3655388105592893530">"More"</string> <string name="float_button_text" msgid="9221657008391364581">"Float"</string> @@ -136,8 +134,7 @@ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Change aspect ratio"</string> <string name="close_text" msgid="4986518933445178928">"Close"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Close menu"</string> - <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) --> - <skip /> + <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (desktop windowing)"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximise screen"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Resize"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"App can\'t be moved here"</string> diff --git a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml index 2087bb4ad579..61b68a2b2515 100644 --- a/libs/WindowManager/Shell/res/values-en-rIN/strings.xml +++ b/libs/WindowManager/Shell/res/values-en-rIN/strings.xml @@ -100,8 +100,7 @@ <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Didn’t fix it?\nTap to revert"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"No camera issues? Tap to dismiss."</string> <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"The app menu can be found here"</string> - <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) --> - <skip /> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Enter desktop windowing to open multiple apps together"</string> <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Return to full screen at any time from the app menu"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"See and do more"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Drag in another app for split screen"</string> @@ -122,8 +121,7 @@ <string name="handle_text" msgid="4419667835599523257">"App handle"</string> <string name="app_icon_text" msgid="2823268023931811747">"App icon"</string> <string name="fullscreen_text" msgid="1162316685217676079">"Full screen"</string> - <!-- no translation found for desktop_text (9058641752519570266) --> - <skip /> + <string name="desktop_text" msgid="9058641752519570266">"Desktop windowing"</string> <string name="split_screen_text" msgid="1396336058129570886">"Split screen"</string> <string name="more_button_text" msgid="3655388105592893530">"More"</string> <string name="float_button_text" msgid="9221657008391364581">"Float"</string> @@ -136,8 +134,7 @@ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Change aspect ratio"</string> <string name="close_text" msgid="4986518933445178928">"Close"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Close menu"</string> - <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) --> - <skip /> + <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (desktop windowing)"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximise screen"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Resize"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"App can\'t be moved here"</string> diff --git a/libs/WindowManager/Shell/res/values-et/strings.xml b/libs/WindowManager/Shell/res/values-et/strings.xml index 36086578dd4d..c0e4eb36b541 100644 --- a/libs/WindowManager/Shell/res/values-et/strings.xml +++ b/libs/WindowManager/Shell/res/values-et/strings.xml @@ -100,8 +100,7 @@ <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Kas probleemi ei lahendatud?\nPuudutage ennistamiseks."</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Kas kaameraprobleeme pole? Puudutage loobumiseks."</string> <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Rakenduse menüü leiate siit"</string> - <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) --> - <skip /> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Mitme rakenduse koos avamiseks kasutage töölaua aknaid"</string> <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Saate rakenduse menüüst igal ajal täisekraanile naasta"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Vaadake ja tehke rohkem"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Lohistage muusse rakendusse, et jagatud ekraanikuva kasutada"</string> @@ -122,8 +121,7 @@ <string name="handle_text" msgid="4419667835599523257">"Rakenduse element"</string> <string name="app_icon_text" msgid="2823268023931811747">"Rakenduse ikoon"</string> <string name="fullscreen_text" msgid="1162316685217676079">"Täisekraan"</string> - <!-- no translation found for desktop_text (9058641752519570266) --> - <skip /> + <string name="desktop_text" msgid="9058641752519570266">"Töölaua aknad"</string> <string name="split_screen_text" msgid="1396336058129570886">"Jagatud ekraanikuva"</string> <string name="more_button_text" msgid="3655388105592893530">"Rohkem"</string> <string name="float_button_text" msgid="9221657008391364581">"Hõljuv"</string> @@ -136,8 +134,7 @@ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Kuvasuhte muutmine"</string> <string name="close_text" msgid="4986518933445178928">"Sule"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Sule menüü"</string> - <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) --> - <skip /> + <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (töölaua aknad)"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Kuva täisekraanil"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Suuruse muutmine"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Rakendust ei saa siia teisaldada"</string> diff --git a/libs/WindowManager/Shell/res/values-fa/strings.xml b/libs/WindowManager/Shell/res/values-fa/strings.xml index bf5c8f95cfb3..4879965d1ae7 100644 --- a/libs/WindowManager/Shell/res/values-fa/strings.xml +++ b/libs/WindowManager/Shell/res/values-fa/strings.xml @@ -100,8 +100,7 @@ <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"مشکل برطرف نشد؟\nبرای برگرداندن تکضرب بزنید"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"دوربین مشکلی ندارد؟ برای بستن تکضرب بزنید."</string> <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"منو برنامه را میتوانید اینجا ببینید"</string> - <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) --> - <skip /> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"برای باز کردن همزمان چند برنامه، وارد پردازش پنجرهای رایانه شوید"</string> <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"هروقت خواستید از منو برنامه به حالت تمامصفحه برگردید"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"از چندین برنامه بهطور همزمان استفاده کنید"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"برای حالت صفحهٔ دونیمه، در برنامهای دیگر بکشید"</string> @@ -122,8 +121,7 @@ <string name="handle_text" msgid="4419667835599523257">"دستگیره برنامه"</string> <string name="app_icon_text" msgid="2823268023931811747">"نماد برنامه"</string> <string name="fullscreen_text" msgid="1162316685217676079">"تمامصفحه"</string> - <!-- no translation found for desktop_text (9058641752519570266) --> - <skip /> + <string name="desktop_text" msgid="9058641752519570266">"پردازش پنجرهای رایانه"</string> <string name="split_screen_text" msgid="1396336058129570886">"صفحهٔ دونیمه"</string> <string name="more_button_text" msgid="3655388105592893530">"بیشتر"</string> <string name="float_button_text" msgid="9221657008391364581">"شناور"</string> @@ -136,8 +134,7 @@ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"تغییر نسبت ابعادی"</string> <string name="close_text" msgid="4986518933445178928">"بستن"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"بستن منو"</string> - <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) --> - <skip /> + <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (پردازش پنجرهای رایانه)"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"بزرگ کردن صفحه"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"تغییر اندازه"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"برنامه را نمیتوان به اینجا منتقل کرد"</string> diff --git a/libs/WindowManager/Shell/res/values-fi/strings.xml b/libs/WindowManager/Shell/res/values-fi/strings.xml index a32e4fab9d81..8fd6b1bacbbe 100644 --- a/libs/WindowManager/Shell/res/values-fi/strings.xml +++ b/libs/WindowManager/Shell/res/values-fi/strings.xml @@ -100,8 +100,7 @@ <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Eikö ongelma ratkennut?\nKumoa napauttamalla"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Ei ongelmia kameran kanssa? Hylkää napauttamalla."</string> <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Sovellusvalikko löytyy täältä"</string> - <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) --> - <skip /> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Siirry työpöydän ikkunointiin, niin voit avata useita sovelluksia kerralla"</string> <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Voit palata koko näytön tilaan milloin tahansa sovellusvalikosta"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Näe ja tee enemmän"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Käytä jaettua näyttöä vetämällä tähän toinen sovellus"</string> @@ -122,8 +121,7 @@ <string name="handle_text" msgid="4419667835599523257">"Sovelluksen tunnus"</string> <string name="app_icon_text" msgid="2823268023931811747">"Sovelluskuvake"</string> <string name="fullscreen_text" msgid="1162316685217676079">"Koko näyttö"</string> - <!-- no translation found for desktop_text (9058641752519570266) --> - <skip /> + <string name="desktop_text" msgid="9058641752519570266">"Työpöydän ikkunointi"</string> <string name="split_screen_text" msgid="1396336058129570886">"Jaettu näyttö"</string> <string name="more_button_text" msgid="3655388105592893530">"Lisää"</string> <string name="float_button_text" msgid="9221657008391364581">"Kelluva ikkuna"</string> @@ -136,8 +134,7 @@ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Vaihda kuvasuhdetta"</string> <string name="close_text" msgid="4986518933445178928">"Sulje"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Sulje valikko"</string> - <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) --> - <skip /> + <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (työpöydän ikkunointi)"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Suurenna näyttö"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Muuta kokoa"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Sovellusta ei voi siirtää tänne"</string> diff --git a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml index 7037377156c6..b729ececfccd 100644 --- a/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml +++ b/libs/WindowManager/Shell/res/values-fr-rCA/strings.xml @@ -100,8 +100,7 @@ <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Problème non résolu?\nTouchez pour rétablir"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Aucun problème d\'appareil photo? Touchez pour ignorer."</string> <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Le menu de l\'appli se trouve ici"</string> - <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) --> - <skip /> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Accéder au fenêtrage du bureau pour ouvrir plusieurs applis simultanément"</string> <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Revenir au mode Plein écran à tout moment à partir du menu de l\'appli"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Voir et en faire plus"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Faites glisser une autre appli pour utiliser l\'écran partagé"</string> @@ -122,8 +121,7 @@ <string name="handle_text" msgid="4419667835599523257">"Poignée de l\'appli"</string> <string name="app_icon_text" msgid="2823268023931811747">"Icône de l\'appli"</string> <string name="fullscreen_text" msgid="1162316685217676079">"Plein écran"</string> - <!-- no translation found for desktop_text (9058641752519570266) --> - <skip /> + <string name="desktop_text" msgid="9058641752519570266">"Fenêtrage du bureau"</string> <string name="split_screen_text" msgid="1396336058129570886">"Écran divisé"</string> <string name="more_button_text" msgid="3655388105592893530">"Plus"</string> <string name="float_button_text" msgid="9221657008391364581">"Flottant"</string> @@ -136,8 +134,7 @@ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Modifier les proportions"</string> <string name="close_text" msgid="4986518933445178928">"Fermer"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Fermer le menu"</string> - <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) --> - <skip /> + <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (fenêtrage du bureau)"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Agrandir l\'écran"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Redimensionner"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Impossible de déplacer l\'appli ici"</string> diff --git a/libs/WindowManager/Shell/res/values-fr/strings.xml b/libs/WindowManager/Shell/res/values-fr/strings.xml index fb2ebfd55583..ed87a1388304 100644 --- a/libs/WindowManager/Shell/res/values-fr/strings.xml +++ b/libs/WindowManager/Shell/res/values-fr/strings.xml @@ -100,8 +100,7 @@ <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Problème non résolu ?\nAppuyez pour rétablir"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Aucun problème d\'appareil photo ? Appuyez pour ignorer."</string> <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Le menu de l\'application se trouve ici"</string> - <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) --> - <skip /> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Utiliser le fenêtrage de bureau pour ouvrir plusieurs applications simultanément"</string> <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Revenir en plein écran à tout moment depuis le menu de l\'application"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Voir et interagir plus"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Faites glisser une autre appli pour utiliser l\'écran partagé"</string> @@ -122,8 +121,7 @@ <string name="handle_text" msgid="4419667835599523257">"Poignée de l\'appli"</string> <string name="app_icon_text" msgid="2823268023931811747">"Icône d\'application"</string> <string name="fullscreen_text" msgid="1162316685217676079">"Plein écran"</string> - <!-- no translation found for desktop_text (9058641752519570266) --> - <skip /> + <string name="desktop_text" msgid="9058641752519570266">"Fenêtrage de bureau"</string> <string name="split_screen_text" msgid="1396336058129570886">"Écran partagé"</string> <string name="more_button_text" msgid="3655388105592893530">"Plus"</string> <string name="float_button_text" msgid="9221657008391364581">"Flottante"</string> @@ -136,8 +134,7 @@ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Modifier le format"</string> <string name="close_text" msgid="4986518933445178928">"Fermer"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Fermer le menu"</string> - <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) --> - <skip /> + <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (fenêtrage de bureau)"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Mettre en plein écran"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Redimensionner"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Impossible de déplacer l\'appli ici"</string> diff --git a/libs/WindowManager/Shell/res/values-gl/strings.xml b/libs/WindowManager/Shell/res/values-gl/strings.xml index 895cf47e3d53..a2b871120464 100644 --- a/libs/WindowManager/Shell/res/values-gl/strings.xml +++ b/libs/WindowManager/Shell/res/values-gl/strings.xml @@ -100,8 +100,7 @@ <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Non se solucionaron os problemas?\nToca para reverter o seu tratamento"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Non hai problemas coa cámara? Tocar para ignorar."</string> <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Aquí podes ver o menú da aplicación"</string> - <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) --> - <skip /> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Vai ao escritorio baseado en ventás se queres abrir varias aplicacións á vez"</string> <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Volve á pantalla completa en calquera momento desde o menú da aplicación"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Ver e facer máis"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Arrastra outra aplicación para usar a pantalla dividida"</string> @@ -122,8 +121,7 @@ <string name="handle_text" msgid="4419667835599523257">"Controlador da aplicación"</string> <string name="app_icon_text" msgid="2823268023931811747">"Icona de aplicación"</string> <string name="fullscreen_text" msgid="1162316685217676079">"Pantalla completa"</string> - <!-- no translation found for desktop_text (9058641752519570266) --> - <skip /> + <string name="desktop_text" msgid="9058641752519570266">"Escritorio baseado en ventás"</string> <string name="split_screen_text" msgid="1396336058129570886">"Pantalla dividida"</string> <string name="more_button_text" msgid="3655388105592893530">"Máis"</string> <string name="float_button_text" msgid="9221657008391364581">"Flotante"</string> @@ -136,8 +134,7 @@ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Cambiar a proporción"</string> <string name="close_text" msgid="4986518933445178928">"Pechar"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Pechar o menú"</string> - <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) --> - <skip /> + <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (escritorio baseado en ventás)"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximizar pantalla"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Cambiar tamaño"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Non se pode mover aquí a aplicación"</string> diff --git a/libs/WindowManager/Shell/res/values-gu/strings.xml b/libs/WindowManager/Shell/res/values-gu/strings.xml index 9c8cf96be294..ddef9e1fd07b 100644 --- a/libs/WindowManager/Shell/res/values-gu/strings.xml +++ b/libs/WindowManager/Shell/res/values-gu/strings.xml @@ -43,10 +43,8 @@ <string name="accessibility_action_divider_left_50" msgid="3488317024557521561">"ડાબે 50%"</string> <string name="accessibility_action_divider_left_30" msgid="6023611335723838727">"ડાબે 30%"</string> <string name="accessibility_action_divider_right_full" msgid="3408505054325944903">"જમણી સ્ક્રીન સ્ક્રીન"</string> - <!-- no translation found for accessibility_action_divider_swap_vertical (3644891227133372072) --> - <skip /> - <!-- no translation found for accessibility_action_divider_swap_horizontal (2722197605446631628) --> - <skip /> + <string name="accessibility_action_divider_swap_vertical" msgid="3644891227133372072">"એકદમ ઉપરની ઍપને એકદમ નીચેની સાથે સ્વૉપ કરો"</string> + <string name="accessibility_action_divider_swap_horizontal" msgid="2722197605446631628">"ડાબી ઍપને જમણી સાથે સ્વૉપ કરો"</string> <string name="accessibility_action_divider_top_full" msgid="3495871951082107594">"શીર્ષ પૂર્ણ સ્ક્રીન"</string> <string name="accessibility_action_divider_top_70" msgid="1779164068887875474">"શીર્ષ 70%"</string> <string name="accessibility_action_divider_top_50" msgid="8649582798829048946">"શીર્ષ 50%"</string> @@ -102,8 +100,7 @@ <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"સુધારો નથી થયો?\nપહેલાંના પર પાછું ફેરવવા માટે ટૅપ કરો"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"કૅમેરામાં કોઈ સમસ્યા નથી? છોડી દેવા માટે ટૅપ કરો."</string> <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"ઍપ મેનૂ અહીં જોવા મળી શકે છે"</string> - <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) --> - <skip /> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"એકથી વધુ ઍપ એકસાથે ખોલવા માટે ડેસ્કટૉપ વિન્ડોઇંગ દાખલ કરો"</string> <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"ઍપ મેનૂમાંથી કોઈપણ સમયે પૂર્ણ સ્ક્રીન પર પાછા ફરો"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"જુઓ અને બીજું ઘણું કરો"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"વિભાજિત સ્ક્રીન માટે કોઈ અન્ય ઍપમાં ખેંચો"</string> @@ -116,20 +113,15 @@ <string name="letterbox_restart_restart" msgid="8529976234412442973">"ફરી શરૂ કરો"</string> <string name="letterbox_restart_dialog_checkbox_title" msgid="5252918008140768386">"ફરીથી બતાવશો નહીં"</string> <string name="letterbox_reachability_reposition_text" msgid="3522042240665748268">"આ ઍપને ખસેડવા માટે\nબે વાર ટૅપ કરો"</string> - <!-- no translation found for maximize_button_text (8106849394538234709) --> - <skip /> - <!-- no translation found for restore_button_text (5377571986086775288) --> - <skip /> - <!-- no translation found for minimize_button_text (5213953162664451152) --> - <skip /> - <!-- no translation found for close_button_text (4544839489310949894) --> - <skip /> + <string name="maximize_button_text" msgid="8106849394538234709">"<xliff:g id="APP_NAME">%1$s</xliff:g>નું કદ મહત્તમ કરો"</string> + <string name="restore_button_text" msgid="5377571986086775288">"<xliff:g id="APP_NAME">%1$s</xliff:g>ને રિસ્ટોર કરો"</string> + <string name="minimize_button_text" msgid="5213953162664451152">"<xliff:g id="APP_NAME">%1$s</xliff:g>નું કદ ન્યૂનતમ કરો"</string> + <string name="close_button_text" msgid="4544839489310949894">"<xliff:g id="APP_NAME">%1$s</xliff:g> બંધ કરો"</string> <string name="back_button_text" msgid="1469718707134137085">"પાછળ"</string> <string name="handle_text" msgid="4419667835599523257">"ઍપનું હૅન્ડલ"</string> <string name="app_icon_text" msgid="2823268023931811747">"ઍપનું આઇકન"</string> <string name="fullscreen_text" msgid="1162316685217676079">"પૂર્ણસ્ક્રીન"</string> - <!-- no translation found for desktop_text (9058641752519570266) --> - <skip /> + <string name="desktop_text" msgid="9058641752519570266">"ડેસ્કટૉપ વિન્ડોઇંગ"</string> <string name="split_screen_text" msgid="1396336058129570886">"સ્ક્રીનને વિભાજિત કરો"</string> <string name="more_button_text" msgid="3655388105592893530">"વધુ"</string> <string name="float_button_text" msgid="9221657008391364581">"ફ્લોટિંગ વિન્ડો"</string> @@ -142,8 +134,7 @@ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"સાપેક્ષ ગુણોત્તર બદલો"</string> <string name="close_text" msgid="4986518933445178928">"બંધ કરો"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"મેનૂ બંધ કરો"</string> - <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) --> - <skip /> + <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ડેસ્કટૉપ વિન્ડોઇંગ)"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"સ્ક્રીન કરો મોટી કરો"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"કદ બદલો"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"ઍપ અહીં ખસેડી શકાતી નથી"</string> @@ -161,14 +152,10 @@ <string name="maximize_menu_talkback_action_snap_left_text" msgid="500309467459084564">"ડાબી બાજુ વિન્ડોનું કદ બદલો"</string> <string name="maximize_menu_talkback_action_snap_right_text" msgid="7010831426654467163">"જમણી બાજુ વિન્ડોનું કદ બદલો"</string> <string name="maximize_menu_talkback_action_maximize_restore_text" msgid="4942610897847934859">"વિન્ડોનું કદ મહત્તમ કરો અથવા રિસ્ટોર કરો"</string> - <!-- no translation found for app_header_talkback_action_maximize_button_text (8776156791095878638) --> - <skip /> - <!-- no translation found for app_header_talkback_action_restore_button_text (2153022340772980863) --> - <skip /> - <!-- no translation found for app_header_talkback_action_minimize_button_text (7491054416186901764) --> - <skip /> - <!-- no translation found for app_header_talkback_action_close_button_text (5159612596378268926) --> - <skip /> + <string name="app_header_talkback_action_maximize_button_text" msgid="8776156791095878638">"ઍપની વિન્ડોનું કદ મહત્તમ કરો"</string> + <string name="app_header_talkback_action_restore_button_text" msgid="2153022340772980863">"વિન્ડોનું કદ રિસ્ટોર કરો"</string> + <string name="app_header_talkback_action_minimize_button_text" msgid="7491054416186901764">"ઍપની વિન્ડોનું કદ ન્યૂનતમ કરો"</string> + <string name="app_header_talkback_action_close_button_text" msgid="5159612596378268926">"ઍપની વિન્ડો બંધ કરો"</string> <string name="open_by_default_settings_text" msgid="2526548548598185500">"\'ડિફૉલ્ટ તરીકે ખોલો\' સેટિંગ"</string> <string name="open_by_default_dialog_subheader_text" msgid="1729599730664063881">"આ ઍપ માટે વેબ લિંક ખોલવાની રીત પસંદ કરો"</string> <string name="open_by_default_dialog_in_app_text" msgid="6978022419634199806">"ઍપમાં"</string> diff --git a/libs/WindowManager/Shell/res/values-hi/strings.xml b/libs/WindowManager/Shell/res/values-hi/strings.xml index 985dafffa68a..bbe43a1727f8 100644 --- a/libs/WindowManager/Shell/res/values-hi/strings.xml +++ b/libs/WindowManager/Shell/res/values-hi/strings.xml @@ -100,8 +100,7 @@ <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"क्या समस्या ठीक नहीं हुई?\nपहले जैसा करने के लिए टैप करें"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"क्या कैमरे से जुड़ी कोई समस्या नहीं है? खारिज करने के लिए टैप करें."</string> <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"ऐप्लिकेशन मेन्यू यहां पाया जा सकता है"</string> - <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) --> - <skip /> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"एक साथ कई ऐप्लिकेशन खोलने के लिए, डेस्कटॉप विंडोविंग का इस्तेमाल करें"</string> <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"ऐप्लिकेशन मेन्यू से फ़ुल स्क्रीन मोड पर किसी भी समय वापस जाएं"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"पूरी जानकारी लेकर, बेहतर तरीके से काम करें"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"स्प्लिट स्क्रीन का इस्तेमाल करने के लिए, किसी अन्य ऐप्लिकेशन को खींचें और छोड़ें"</string> @@ -122,8 +121,7 @@ <string name="handle_text" msgid="4419667835599523257">"ऐप्लिकेशन का हैंडल"</string> <string name="app_icon_text" msgid="2823268023931811747">"ऐप्लिकेशन आइकॉन"</string> <string name="fullscreen_text" msgid="1162316685217676079">"फ़ुलस्क्रीन"</string> - <!-- no translation found for desktop_text (9058641752519570266) --> - <skip /> + <string name="desktop_text" msgid="9058641752519570266">"डेस्कटॉप विंडोविंग"</string> <string name="split_screen_text" msgid="1396336058129570886">"स्प्लिट स्क्रीन मोड"</string> <string name="more_button_text" msgid="3655388105592893530">"ज़्यादा देखें"</string> <string name="float_button_text" msgid="9221657008391364581">"फ़्लोट"</string> @@ -136,8 +134,7 @@ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"आसपेक्ट रेशियो (लंबाई-चौड़ाई का अनुपात) बदलें"</string> <string name="close_text" msgid="4986518933445178928">"बंद करें"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"मेन्यू बंद करें"</string> - <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) --> - <skip /> + <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (डेस्कटॉप विंडोविंग)"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"स्क्रीन को बड़ा करें"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"साइज़ बदलें"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"ऐप्लिकेशन को यहां मूव नहीं किया जा सकता"</string> diff --git a/libs/WindowManager/Shell/res/values-hr/strings.xml b/libs/WindowManager/Shell/res/values-hr/strings.xml index 4640bf3b8293..80ee56102cf2 100644 --- a/libs/WindowManager/Shell/res/values-hr/strings.xml +++ b/libs/WindowManager/Shell/res/values-hr/strings.xml @@ -100,8 +100,7 @@ <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Problem nije riješen?\nDodirnite za vraćanje"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nemate problema s fotoaparatom? Dodirnite za odbacivanje."</string> <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Izbornik aplikacije možete pronaći ovdje"</string> - <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) --> - <skip /> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Otvorite prikaz u prozorima na računalu da biste otvorili više aplikacija zajedno"</string> <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Vratite se na cijeli zaslon bilo kad iz izbornika aplikacije"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Gledajte i učinite više"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Povucite drugu aplikaciju unutra da biste podijelili zaslon"</string> @@ -122,8 +121,7 @@ <string name="handle_text" msgid="4419667835599523257">"Pokazivač aplikacije"</string> <string name="app_icon_text" msgid="2823268023931811747">"Ikona aplikacije"</string> <string name="fullscreen_text" msgid="1162316685217676079">"Puni zaslon"</string> - <!-- no translation found for desktop_text (9058641752519570266) --> - <skip /> + <string name="desktop_text" msgid="9058641752519570266">"Prikaz u prozorima na računalu"</string> <string name="split_screen_text" msgid="1396336058129570886">"Razdvojeni zaslon"</string> <string name="more_button_text" msgid="3655388105592893530">"Više"</string> <string name="float_button_text" msgid="9221657008391364581">"Plutajući"</string> @@ -136,8 +134,7 @@ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Promijeni omjer slike"</string> <string name="close_text" msgid="4986518933445178928">"Zatvorite"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Zatvorite izbornik"</string> - <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) --> - <skip /> + <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (prikaz u prozorima na računalu)"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimalno povećaj zaslon"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Promijeni veličinu"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Aplikacija se ne može premjestiti ovdje"</string> diff --git a/libs/WindowManager/Shell/res/values-hu/strings.xml b/libs/WindowManager/Shell/res/values-hu/strings.xml index 711e2f0bfbbe..d24e4da8c982 100644 --- a/libs/WindowManager/Shell/res/values-hu/strings.xml +++ b/libs/WindowManager/Shell/res/values-hu/strings.xml @@ -100,8 +100,7 @@ <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nem sikerült a hiba kijavítása?\nKoppintson a visszaállításhoz."</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nincsenek problémái kamerával? Koppintson az elvetéshez."</string> <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Az alkalmazásmenü itt található"</string> - <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) --> - <skip /> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Asztali ablakkezelési módba lépve több alkalmazást nyithat meg egyidejűleg"</string> <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Az alkalmazásmenüből bármikor visszatérhet a teljes képernyőre"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Több mindent láthat és tehet"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Húzzon ide egy másik alkalmazást az osztott képernyő használatához"</string> @@ -122,8 +121,7 @@ <string name="handle_text" msgid="4419667835599523257">"App fogópontja"</string> <string name="app_icon_text" msgid="2823268023931811747">"Alkalmazásikon"</string> <string name="fullscreen_text" msgid="1162316685217676079">"Teljes képernyő"</string> - <!-- no translation found for desktop_text (9058641752519570266) --> - <skip /> + <string name="desktop_text" msgid="9058641752519570266">"Asztali ablakkezelési mód"</string> <string name="split_screen_text" msgid="1396336058129570886">"Osztott képernyő"</string> <string name="more_button_text" msgid="3655388105592893530">"Továbbiak"</string> <string name="float_button_text" msgid="9221657008391364581">"Lebegő"</string> @@ -136,8 +134,7 @@ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Méretarány módosítása"</string> <string name="close_text" msgid="4986518933445178928">"Bezárás"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Menü bezárása"</string> - <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) --> - <skip /> + <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Asztali ablakkezelési mód)"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Képernyő méretének maximalizálása"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Átméretezés"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Az alkalmazás nem helyezhető át ide"</string> diff --git a/libs/WindowManager/Shell/res/values-hy/strings.xml b/libs/WindowManager/Shell/res/values-hy/strings.xml index d7b1b07b6917..6bc3c37cadf4 100644 --- a/libs/WindowManager/Shell/res/values-hy/strings.xml +++ b/libs/WindowManager/Shell/res/values-hy/strings.xml @@ -100,8 +100,7 @@ <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Չհաջողվե՞ց շտկել։\nՀպեք՝ փոփոխությունները չեղարկելու համար։"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Տեսախցիկի հետ կապված խնդիրներ չկա՞ն։ Փակելու համար հպեք։"</string> <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Հավելվածի ընտրացանկն այստեղ է"</string> - <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) --> - <skip /> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Անցեք համակարգչային պատուհաններին՝ միաժամանակ մի քանի հավելված բացելու համար"</string> <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Ցանկացած ժամանակ հավելվածի ընտրացանկից վերադարձեք լիաէկրան ռեժիմ"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Միաժամանակ կատարեք մի քանի առաջադրանք"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Քաշեք մյուս հավելվածի մեջ՝ էկրանի տրոհումն օգտագործելու համար"</string> @@ -122,8 +121,7 @@ <string name="handle_text" msgid="4419667835599523257">"Հավելվածի կեղծանուն"</string> <string name="app_icon_text" msgid="2823268023931811747">"Հավելվածի պատկերակ"</string> <string name="fullscreen_text" msgid="1162316685217676079">"Լիաէկրան"</string> - <!-- no translation found for desktop_text (9058641752519570266) --> - <skip /> + <string name="desktop_text" msgid="9058641752519570266">"Համակարգչային պատուհաններ"</string> <string name="split_screen_text" msgid="1396336058129570886">"Տրոհված էկրան"</string> <string name="more_button_text" msgid="3655388105592893530">"Ավելին"</string> <string name="float_button_text" msgid="9221657008391364581">"Լողացող պատուհան"</string> @@ -136,8 +134,7 @@ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Փոխել կողմերի հարաբերակցությունը"</string> <string name="close_text" msgid="4986518933445178928">"Փակել"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Փակել ընտրացանկը"</string> - <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) --> - <skip /> + <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (համակարգչային պատուհաններ)"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Ծավալել էկրանը"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Փոխել չափը"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Հավելվածը հնարավոր չէ տեղափոխել այստեղ"</string> diff --git a/libs/WindowManager/Shell/res/values-in/strings.xml b/libs/WindowManager/Shell/res/values-in/strings.xml index 1d1927f9dfbc..c15c2ea6600b 100644 --- a/libs/WindowManager/Shell/res/values-in/strings.xml +++ b/libs/WindowManager/Shell/res/values-in/strings.xml @@ -100,8 +100,7 @@ <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Tidak dapat diperbaiki?\nKetuk untuk mengembalikan"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Tidak ada masalah kamera? Ketuk untuk menutup."</string> <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Menu aplikasi dapat ditemukan di sini"</string> - <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) --> - <skip /> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Masuk ke mode jendela desktop untuk membuka beberapa aplikasi secara bersamaan"</string> <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Kembali ke layar penuh kapan saja dari menu aplikasi"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Lihat dan lakukan lebih banyak hal"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Tarik aplikasi lain untuk menggunakan layar terpisah"</string> @@ -122,8 +121,7 @@ <string name="handle_text" msgid="4419667835599523257">"Penanganan aplikasi"</string> <string name="app_icon_text" msgid="2823268023931811747">"Ikon Aplikasi"</string> <string name="fullscreen_text" msgid="1162316685217676079">"Layar Penuh"</string> - <!-- no translation found for desktop_text (9058641752519570266) --> - <skip /> + <string name="desktop_text" msgid="9058641752519570266">"Mode jendela desktop"</string> <string name="split_screen_text" msgid="1396336058129570886">"Layar Terpisah"</string> <string name="more_button_text" msgid="3655388105592893530">"Lainnya"</string> <string name="float_button_text" msgid="9221657008391364581">"Mengambang"</string> @@ -136,8 +134,7 @@ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Ubah rasio aspek"</string> <string name="close_text" msgid="4986518933445178928">"Tutup"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Tutup Menu"</string> - <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) --> - <skip /> + <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Mode jendela desktop)"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Perbesar Layar"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Ubah ukuran"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Aplikasi tidak dapat dipindahkan ke sini"</string> diff --git a/libs/WindowManager/Shell/res/values-is/strings.xml b/libs/WindowManager/Shell/res/values-is/strings.xml index e94a4c8a08ea..7d98d3b01fc2 100644 --- a/libs/WindowManager/Shell/res/values-is/strings.xml +++ b/libs/WindowManager/Shell/res/values-is/strings.xml @@ -100,8 +100,7 @@ <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Ennþá vesen?\nÝttu til að afturkalla"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Ekkert myndavélavesen? Ýttu til að hunsa."</string> <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Hér finnurðu forritavalmyndina"</string> - <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) --> - <skip /> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Kveiktu á gluggastillingu í tölvu til að opna mörg forrit samtímis"</string> <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Þú getur skipt aftur í allan skjáinn hvenær sem er af forritavalmyndinni"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Sjáðu og gerðu meira"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Dragðu annað forrit inn til að nota skjáskiptingu"</string> @@ -122,8 +121,7 @@ <string name="handle_text" msgid="4419667835599523257">"Handfang forrits"</string> <string name="app_icon_text" msgid="2823268023931811747">"Tákn forrits"</string> <string name="fullscreen_text" msgid="1162316685217676079">"Allur skjárinn"</string> - <!-- no translation found for desktop_text (9058641752519570266) --> - <skip /> + <string name="desktop_text" msgid="9058641752519570266">"Gluggastilling í tölvu"</string> <string name="split_screen_text" msgid="1396336058129570886">"Skjáskipting"</string> <string name="more_button_text" msgid="3655388105592893530">"Meira"</string> <string name="float_button_text" msgid="9221657008391364581">"Reikult"</string> @@ -136,8 +134,7 @@ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Breyta myndhlutfalli"</string> <string name="close_text" msgid="4986518933445178928">"Loka"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Loka valmynd"</string> - <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) --> - <skip /> + <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (gluggastilling í tölvu)"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Stækka skjá"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Breyta stærð"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Ekki er hægt að færa forritið hingað"</string> diff --git a/libs/WindowManager/Shell/res/values-it/strings.xml b/libs/WindowManager/Shell/res/values-it/strings.xml index 5f8334fa011c..72f805693146 100644 --- a/libs/WindowManager/Shell/res/values-it/strings.xml +++ b/libs/WindowManager/Shell/res/values-it/strings.xml @@ -100,8 +100,7 @@ <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Il problema non si è risolto?\nTocca per ripristinare"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nessun problema con la fotocamera? Tocca per ignorare."</string> <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Il menu dell\'app si trova qui"</string> - <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) --> - <skip /> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Attiva il windowing del desktop per aprire più app contemporaneamente"</string> <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Torna allo schermo intero in qualsiasi momento dal menu dell\'app"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Visualizza più contenuti e fai di più"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Trascina in un\'altra app per usare lo schermo diviso"</string> @@ -122,8 +121,7 @@ <string name="handle_text" msgid="4419667835599523257">"Punto di manipolazione app"</string> <string name="app_icon_text" msgid="2823268023931811747">"Icona dell\'app"</string> <string name="fullscreen_text" msgid="1162316685217676079">"Schermo intero"</string> - <!-- no translation found for desktop_text (9058641752519570266) --> - <skip /> + <string name="desktop_text" msgid="9058641752519570266">"Windowing del desktop"</string> <string name="split_screen_text" msgid="1396336058129570886">"Schermo diviso"</string> <string name="more_button_text" msgid="3655388105592893530">"Altro"</string> <string name="float_button_text" msgid="9221657008391364581">"Mobile"</string> @@ -136,8 +134,7 @@ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Cambia proporzioni"</string> <string name="close_text" msgid="4986518933445178928">"Chiudi"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Chiudi il menu"</string> - <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) --> - <skip /> + <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (windowing del desktop)"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Massimizza schermo"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Ridimensiona"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Impossibile spostare l\'app qui"</string> diff --git a/libs/WindowManager/Shell/res/values-ja/strings.xml b/libs/WindowManager/Shell/res/values-ja/strings.xml index 1a73dd3b7715..c95ec4ee25a3 100644 --- a/libs/WindowManager/Shell/res/values-ja/strings.xml +++ b/libs/WindowManager/Shell/res/values-ja/strings.xml @@ -100,8 +100,7 @@ <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"修正されなかった場合は、\nタップすると元に戻ります"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"カメラに関する問題でない場合は、タップすると閉じます。"</string> <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"アプリメニューはここにあります"</string> - <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) --> - <skip /> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"デスクトップ ウィンドウに切り替えて複数のアプリを同時に開けます"</string> <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"アプリメニューからいつでも全画面表示に戻れます"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"表示を拡大して機能を強化"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"分割画面にするにはもう 1 つのアプリをドラッグしてください"</string> @@ -122,8 +121,7 @@ <string name="handle_text" msgid="4419667835599523257">"アプリハンドル"</string> <string name="app_icon_text" msgid="2823268023931811747">"アプリのアイコン"</string> <string name="fullscreen_text" msgid="1162316685217676079">"全画面表示"</string> - <!-- no translation found for desktop_text (9058641752519570266) --> - <skip /> + <string name="desktop_text" msgid="9058641752519570266">"デスクトップ ウィンドウ"</string> <string name="split_screen_text" msgid="1396336058129570886">"分割画面"</string> <string name="more_button_text" msgid="3655388105592893530">"その他"</string> <string name="float_button_text" msgid="9221657008391364581">"フローティング"</string> @@ -136,8 +134,7 @@ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"アスペクト比を変更"</string> <string name="close_text" msgid="4986518933445178928">"閉じる"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"メニューを閉じる"</string> - <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) --> - <skip /> + <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g>(デスクトップ ウィンドウ)"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"画面の最大化"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"サイズ変更"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"アプリはここに移動できません"</string> diff --git a/libs/WindowManager/Shell/res/values-ka/strings.xml b/libs/WindowManager/Shell/res/values-ka/strings.xml index 4bbfaefb36a2..0c7264cf9ede 100644 --- a/libs/WindowManager/Shell/res/values-ka/strings.xml +++ b/libs/WindowManager/Shell/res/values-ka/strings.xml @@ -100,8 +100,7 @@ <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"არ გამოსწორდა?\nშეეხეთ წინა ვერსიის დასაბრუნებლად"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"კამერას პრობლემები არ აქვს? შეეხეთ უარყოფისთვის."</string> <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"აპის მენიუ შეგიძლიათ იხილოთ აქ"</string> - <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) --> - <skip /> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"რამდენიმე აპის ერთდროულად გასახსნელად შედით დესკტოპის ფანჯრის რეჟიმში"</string> <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"სრულ ეკრანზე ნებისმიერ დროს შეგიძლიათ დაბრუნდეთ აპის მენიუდან"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"მეტის ნახვა და გაკეთება"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"ეკრანის გასაყოფად ჩავლებით გადაიტანეთ სხვა აპში"</string> @@ -122,8 +121,7 @@ <string name="handle_text" msgid="4419667835599523257">"აპის იდენტიფიკატორი"</string> <string name="app_icon_text" msgid="2823268023931811747">"აპის ხატულა"</string> <string name="fullscreen_text" msgid="1162316685217676079">"სრულ ეკრანზე"</string> - <!-- no translation found for desktop_text (9058641752519570266) --> - <skip /> + <string name="desktop_text" msgid="9058641752519570266">"დესკტოპის ფანჯრის რეჟიმი"</string> <string name="split_screen_text" msgid="1396336058129570886">"ეკრანის გაყოფა"</string> <string name="more_button_text" msgid="3655388105592893530">"სხვა"</string> <string name="float_button_text" msgid="9221657008391364581">"ფარფატი"</string> @@ -136,8 +134,7 @@ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"თანაფარდობის შეცვლა"</string> <string name="close_text" msgid="4986518933445178928">"დახურვა"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"მენიუს დახურვა"</string> - <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) --> - <skip /> + <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (დესკტოპის ფანჯრის რეჟიმი)"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"აპლიკაციის გაშლა სრულ ეკრანზე"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"ზომის შეცვლა"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"აპის აქ გადატანა შეუძლებელია"</string> diff --git a/libs/WindowManager/Shell/res/values-kk/strings.xml b/libs/WindowManager/Shell/res/values-kk/strings.xml index 2f6404d4680d..c1085db12f08 100644 --- a/libs/WindowManager/Shell/res/values-kk/strings.xml +++ b/libs/WindowManager/Shell/res/values-kk/strings.xml @@ -100,8 +100,7 @@ <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Жөнделмеді ме?\nҚайтару үшін түртіңіз."</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Камерада қателер шықпады ма? Жабу үшін түртіңіз."</string> <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Қолданба мәзірін осы жерден табуға болады."</string> - <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) --> - <skip /> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Бірнеше қолданбаны бірге ашу үшін жұмыс үстелі көрінісіне кіріңіз."</string> <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Қолданба мәзірінен кез келген уақытта толық экранға оралыңыз."</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Қосымша ақпаратты қарап, әрекеттер жасау"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Экранды бөлу үшін басқа қолданбаға өтіңіз."</string> @@ -122,8 +121,7 @@ <string name="handle_text" msgid="4419667835599523257">"Қолданба идентификаторы"</string> <string name="app_icon_text" msgid="2823268023931811747">"Қолданба белгішесі"</string> <string name="fullscreen_text" msgid="1162316685217676079">"Толық экран"</string> - <!-- no translation found for desktop_text (9058641752519570266) --> - <skip /> + <string name="desktop_text" msgid="9058641752519570266">"Жұмыс үстелі көрінісі"</string> <string name="split_screen_text" msgid="1396336058129570886">"Экранды бөлу"</string> <string name="more_button_text" msgid="3655388105592893530">"Қосымша"</string> <string name="float_button_text" msgid="9221657008391364581">"Қалқыма"</string> @@ -136,8 +134,7 @@ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Арақатынасты өзгерту"</string> <string name="close_text" msgid="4986518933445178928">"Жабу"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Мәзірді жабу"</string> - <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) --> - <skip /> + <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (жұмыс үстелі көрінісі)"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Экранды ұлғайту"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Өлшемін өзгерту"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Қолданба бұл жерге қойылмайды."</string> diff --git a/libs/WindowManager/Shell/res/values-km/strings.xml b/libs/WindowManager/Shell/res/values-km/strings.xml index d3942fdc1e6c..afbd9e0c9422 100644 --- a/libs/WindowManager/Shell/res/values-km/strings.xml +++ b/libs/WindowManager/Shell/res/values-km/strings.xml @@ -100,8 +100,7 @@ <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"មិនបានដោះស្រាយបញ្ហានេះទេឬ?\nចុចដើម្បីត្រឡប់"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"មិនមានបញ្ហាពាក់ព័ន្ធនឹងកាមេរ៉ាទេឬ? ចុចដើម្បីច្រានចោល។"</string> <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"អាចរកឃើញម៉ឺនុយកម្មវិធីនៅទីនេះ"</string> - <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) --> - <skip /> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"ចូលមុខងារវិនដូកុំព្យូទ័រ ដើម្បីបើកកម្មវិធីច្រើនជាមួយគ្នា"</string> <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"ត្រឡប់ទៅអេក្រង់ពេញវិញនៅពេលណាក៏បានពីម៉ឺនុយកម្មវិធី"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"មើលឃើញ និងធ្វើបានកាន់តែច្រើន"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"អូសកម្មវិធីមួយទៀតចូល ដើម្បីប្រើមុខងារបំបែកអេក្រង់"</string> @@ -122,8 +121,7 @@ <string name="handle_text" msgid="4419667835599523257">"ឈ្មោះអ្នកប្រើប្រាស់កម្មវិធី"</string> <string name="app_icon_text" msgid="2823268023931811747">"រូបកម្មវិធី"</string> <string name="fullscreen_text" msgid="1162316685217676079">"អេក្រង់ពេញ"</string> - <!-- no translation found for desktop_text (9058641752519570266) --> - <skip /> + <string name="desktop_text" msgid="9058641752519570266">"មុខងារវិនដូកុំព្យូទ័រ"</string> <string name="split_screen_text" msgid="1396336058129570886">"មុខងារបំបែកអេក្រង់"</string> <string name="more_button_text" msgid="3655388105592893530">"ច្រើនទៀត"</string> <string name="float_button_text" msgid="9221657008391364581">"អណ្ដែត"</string> @@ -136,8 +134,7 @@ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"ប្ដូរសមាមាត្រ"</string> <string name="close_text" msgid="4986518933445178928">"បិទ"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"បិទម៉ឺនុយ"</string> - <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) --> - <skip /> + <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (មុខងារវិនដូកុំព្យូទ័រ)"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"ពង្រីកអេក្រង់"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"ប្ដូរទំហំ"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"មិនអាចផ្លាស់ទីកម្មវិធីមកទីនេះបានទេ"</string> diff --git a/libs/WindowManager/Shell/res/values-kn/strings.xml b/libs/WindowManager/Shell/res/values-kn/strings.xml index fb15e34d934b..34076e8ecebe 100644 --- a/libs/WindowManager/Shell/res/values-kn/strings.xml +++ b/libs/WindowManager/Shell/res/values-kn/strings.xml @@ -100,8 +100,7 @@ <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ಅದನ್ನು ಸರಿಪಡಿಸಲಿಲ್ಲವೇ?\nಹಿಂತಿರುಗಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ಕ್ಯಾಮರಾ ಸಮಸ್ಯೆಗಳಿಲ್ಲವೇ? ವಜಾಗೊಳಿಸಲು ಟ್ಯಾಪ್ ಮಾಡಿ."</string> <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"ಆ್ಯಪ್ ಮೆನುವನ್ನು ಇಲ್ಲಿ ಕಾಣಬಹುದು"</string> - <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) --> - <skip /> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"ಹಲವು ಆ್ಯಪ್ಗಳನ್ನು ಒಟ್ಟಿಗೆ ತೆರೆಯಲು ಡೆಸ್ಕ್ಟಾಪ್ ವಿಂಡೋಯಿಂಗ್ ಅನ್ನು ನಮೂದಿಸಿ"</string> <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"ಆ್ಯಪ್ ಮೆನುವಿನಿಂದ ಯಾವಾಗ ಬೇಕಾದರೂ ಫುಲ್ಸ್ಕ್ರೀನ್ಗೆ ಹಿಂತಿರುಗಿ"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"ನೋಡಿ ಮತ್ತು ಹೆಚ್ಚಿನದನ್ನು ಮಾಡಿ"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"ಸ್ಪ್ಲಿಟ್ ಸ್ಕ್ರೀನ್ಗಾಗಿ ಮತ್ತೊಂದು ಆ್ಯಪ್ನಲ್ಲಿ ಡ್ರ್ಯಾಗ್ ಮಾಡಿ"</string> @@ -122,8 +121,7 @@ <string name="handle_text" msgid="4419667835599523257">"ಆ್ಯಪ್ ಹ್ಯಾಂಡಲ್"</string> <string name="app_icon_text" msgid="2823268023931811747">"ಆ್ಯಪ್ ಐಕಾನ್"</string> <string name="fullscreen_text" msgid="1162316685217676079">"ಫುಲ್ಸ್ಕ್ರೀನ್"</string> - <!-- no translation found for desktop_text (9058641752519570266) --> - <skip /> + <string name="desktop_text" msgid="9058641752519570266">"ಡೆಸ್ಕ್ಟಾಪ್ ವಿಂಡೋಯಿಂಗ್"</string> <string name="split_screen_text" msgid="1396336058129570886">"ಸ್ಪ್ಲಿಟ್ ಸ್ಕ್ರೀನ್"</string> <string name="more_button_text" msgid="3655388105592893530">"ಇನ್ನಷ್ಟು"</string> <string name="float_button_text" msgid="9221657008391364581">"ಫ್ಲೋಟ್"</string> @@ -136,8 +134,7 @@ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"ದೃಶ್ಯಾನುಪಾತವನ್ನು ಬದಲಾಯಿಸಿ"</string> <string name="close_text" msgid="4986518933445178928">"ಮುಚ್ಚಿ"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"ಮೆನು ಮುಚ್ಚಿ"</string> - <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) --> - <skip /> + <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ಡೆಸ್ಕ್ಟಾಪ್ ವಿಂಡೋಯಿಂಗ್)"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"ಸ್ಕ್ರೀನ್ ಅನ್ನು ಮ್ಯಾಕ್ಸಿಮೈಸ್ ಮಾಡಿ"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"ಮರುಗಾತ್ರಗೊಳಿಸಿ"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"ಆ್ಯಪ್ ಅನ್ನು ಇಲ್ಲಿಗೆ ಸರಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ"</string> diff --git a/libs/WindowManager/Shell/res/values-ko/strings.xml b/libs/WindowManager/Shell/res/values-ko/strings.xml index dbfd32a7dcb1..a0fa7e480663 100644 --- a/libs/WindowManager/Shell/res/values-ko/strings.xml +++ b/libs/WindowManager/Shell/res/values-ko/strings.xml @@ -100,8 +100,7 @@ <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"해결되지 않았나요?\n되돌리려면 탭하세요."</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"카메라에 문제가 없나요? 닫으려면 탭하세요."</string> <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"앱 메뉴는 여기에서 찾을 수 있습니다."</string> - <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) --> - <skip /> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"데스크톱 윈도윙을 실행하여 여러 앱을 함께 열 수 있습니다."</string> <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"언제든지 앱 메뉴에서 전체 화면으로 돌아갈 수 있습니다."</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"더 많은 정보를 보고 더 많은 작업을 처리하세요"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"화면 분할을 사용하려면 다른 앱을 드래그해 가져옵니다."</string> @@ -122,8 +121,7 @@ <string name="handle_text" msgid="4419667835599523257">"앱 핸들"</string> <string name="app_icon_text" msgid="2823268023931811747">"앱 아이콘"</string> <string name="fullscreen_text" msgid="1162316685217676079">"전체 화면"</string> - <!-- no translation found for desktop_text (9058641752519570266) --> - <skip /> + <string name="desktop_text" msgid="9058641752519570266">"데스크톱 윈도잉"</string> <string name="split_screen_text" msgid="1396336058129570886">"화면 분할"</string> <string name="more_button_text" msgid="3655388105592893530">"더보기"</string> <string name="float_button_text" msgid="9221657008391364581">"플로팅"</string> @@ -136,8 +134,7 @@ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"가로세로 비율 변경"</string> <string name="close_text" msgid="4986518933445178928">"닫기"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"메뉴 닫기"</string> - <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) --> - <skip /> + <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g>(데스크톱 윈도윙)"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"화면 최대화"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"크기 조절"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"앱을 여기로 이동할 수 없음"</string> diff --git a/libs/WindowManager/Shell/res/values-ky/strings.xml b/libs/WindowManager/Shell/res/values-ky/strings.xml index 36194cdaaa47..629070cbe810 100644 --- a/libs/WindowManager/Shell/res/values-ky/strings.xml +++ b/libs/WindowManager/Shell/res/values-ky/strings.xml @@ -100,8 +100,7 @@ <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Оңдолгон жокпу?\nАртка кайтаруу үчүн таптаңыз"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Камерада маселе жокпу? Этибарга албоо үчүн таптаңыз."</string> <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Колдонмонун менюсун ушул жерден таба аласыз"</string> - <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) --> - <skip /> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Бир убакта бир нече колдонмону ачуу үчүн иш тактанын терезелери режимине өтүңүз"</string> <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Каалаган убакта колдонмонун менюсунан толук экранга кайта аласыз"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Көрүп, көбүрөөк нерселерди жасаңыз"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Экранды бөлүү үчүн башка колдонмону сүйрөңүз"</string> @@ -122,8 +121,7 @@ <string name="handle_text" msgid="4419667835599523257">"Колдонмонун маркери"</string> <string name="app_icon_text" msgid="2823268023931811747">"Колдонмонун сүрөтчөсү"</string> <string name="fullscreen_text" msgid="1162316685217676079">"Толук экран"</string> - <!-- no translation found for desktop_text (9058641752519570266) --> - <skip /> + <string name="desktop_text" msgid="9058641752519570266">"Иш тактанын терезелери"</string> <string name="split_screen_text" msgid="1396336058129570886">"Экранды бөлүү"</string> <string name="more_button_text" msgid="3655388105592893530">"Дагы"</string> <string name="float_button_text" msgid="9221657008391364581">"Калкыма"</string> @@ -136,8 +134,7 @@ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Тараптардын катнашын өзгөртүү"</string> <string name="close_text" msgid="4986518933445178928">"Жабуу"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Менюну жабуу"</string> - <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) --> - <skip /> + <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Иш тактанын терезелери)"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Экранды чоңойтуу"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Өлчөмүн өзгөртүү"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Колдонмону бул жерге жылдырууга болбойт"</string> diff --git a/libs/WindowManager/Shell/res/values-lo/strings.xml b/libs/WindowManager/Shell/res/values-lo/strings.xml index 671f92447204..f2d0e6bd7af4 100644 --- a/libs/WindowManager/Shell/res/values-lo/strings.xml +++ b/libs/WindowManager/Shell/res/values-lo/strings.xml @@ -100,8 +100,7 @@ <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ບໍ່ໄດ້ແກ້ໄຂມັນບໍ?\nແຕະເພື່ອແປງກັບຄືນ"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ບໍ່ມີບັນຫາກ້ອງຖ່າຍຮູບບໍ? ແຕະເພື່ອປິດໄວ້."</string> <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"ສາມາດເບິ່ງເມນູແອັບໄດ້ບ່ອນນີ້"</string> - <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) --> - <skip /> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"ເຂົ້າສູ່ໜ້າຈໍເດັສທັອບເພື່ອເປີດຫຼາຍແອັບພ້ອມກັນ"</string> <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"ກັບຄືນໄປຫາໂໝດເຕັມຈໍໄດ້ທຸກເວລາຈາກເມນູແອັບ"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"ເບິ່ງ ແລະ ເຮັດຫຼາຍຂຶ້ນ"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"ລາກໄປໄວ້ໃນແອັບອື່ນເພື່ອແບ່ງໜ້າຈໍ"</string> @@ -122,8 +121,7 @@ <string name="handle_text" msgid="4419667835599523257">"ຊື່ຜູ້ໃຊ້ແອັບ"</string> <string name="app_icon_text" msgid="2823268023931811747">"ໄອຄອນແອັບ"</string> <string name="fullscreen_text" msgid="1162316685217676079">"ເຕັມຈໍ"</string> - <!-- no translation found for desktop_text (9058641752519570266) --> - <skip /> + <string name="desktop_text" msgid="9058641752519570266">"ໜ້າຈໍເດັສທັອບ"</string> <string name="split_screen_text" msgid="1396336058129570886">"ແບ່ງໜ້າຈໍ"</string> <string name="more_button_text" msgid="3655388105592893530">"ເພີ່ມເຕີມ"</string> <string name="float_button_text" msgid="9221657008391364581">"ລອຍ"</string> @@ -136,8 +134,7 @@ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"ປ່ຽນອັດຕາສ່ວນຮູບ"</string> <string name="close_text" msgid="4986518933445178928">"ປິດ"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"ປິດເມນູ"</string> - <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) --> - <skip /> + <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ໜ້າຈໍເດັສທັອບ)"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"ປັບຈໍໃຫຍ່ສຸດ"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"ປັບຂະໜາດ"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"ບໍ່ສາມາດຍ້າຍແອັບມາບ່ອນນີ້ໄດ້"</string> diff --git a/libs/WindowManager/Shell/res/values-lt/strings.xml b/libs/WindowManager/Shell/res/values-lt/strings.xml index 794c5ab02c19..ed4b14cd94dd 100644 --- a/libs/WindowManager/Shell/res/values-lt/strings.xml +++ b/libs/WindowManager/Shell/res/values-lt/strings.xml @@ -100,8 +100,7 @@ <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nepavyko pataisyti?\nPalieskite, kad grąžintumėte"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nėra jokių problemų dėl kameros? Palieskite, kad atsisakytumėte."</string> <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Programos meniu rasite čia"</string> - <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) --> - <skip /> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Įjunkite versijos staliniams kompiuteriams rodinį, kad galėtumėte vienu metu atidaryti kelias programas"</string> <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Bet kada iš programos meniu grįžkite į viso ekrano režimą"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Daugiau turinio ir funkcijų"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Vilkite kitoje programoje, kad galėtumėte naudoti išskaidyto ekrano režimą"</string> @@ -122,8 +121,7 @@ <string name="handle_text" msgid="4419667835599523257">"Programos kreipinys"</string> <string name="app_icon_text" msgid="2823268023931811747">"Programos piktograma"</string> <string name="fullscreen_text" msgid="1162316685217676079">"Visas ekranas"</string> - <!-- no translation found for desktop_text (9058641752519570266) --> - <skip /> + <string name="desktop_text" msgid="9058641752519570266">"Versijos staliniams kompiuteriams rodinys"</string> <string name="split_screen_text" msgid="1396336058129570886">"Išskaidyto ekrano režimas"</string> <string name="more_button_text" msgid="3655388105592893530">"Daugiau"</string> <string name="float_button_text" msgid="9221657008391364581">"Slankusis langas"</string> @@ -136,8 +134,7 @@ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Keisti kraštinių santykį"</string> <string name="close_text" msgid="4986518933445178928">"Uždaryti"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Uždaryti meniu"</string> - <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) --> - <skip /> + <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"„<xliff:g id="APP_NAME">%1$s</xliff:g>“ (versijos staliniams kompiuteriams rodinys)"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Išskleisti ekraną"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Pakeisti dydį"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Programos negalima perkelti čia"</string> diff --git a/libs/WindowManager/Shell/res/values-lv/strings.xml b/libs/WindowManager/Shell/res/values-lv/strings.xml index 5b44112c76d5..c24b43a686c3 100644 --- a/libs/WindowManager/Shell/res/values-lv/strings.xml +++ b/libs/WindowManager/Shell/res/values-lv/strings.xml @@ -100,8 +100,7 @@ <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Vai problēma netika novērsta?\nPieskarieties, lai atjaunotu."</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Vai nav problēmu ar kameru? Pieskarieties, lai nerādītu."</string> <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Šeit ir pieejama lietotņu izvēlne"</string> - <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) --> - <skip /> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Lai atvērtu vairākas lietotnes vienlaikus, pārejiet uz darbvirsmas logošanu"</string> <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"No lietotnes izvēlnes varat jebkurā brīdī atgriezties pilnekrāna režīmā."</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Uzziniet un paveiciet vairāk"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Lai izmantotu sadalītu ekrānu, ievelciet vēl vienu lietotni"</string> @@ -122,8 +121,7 @@ <string name="handle_text" msgid="4419667835599523257">"Lietotnes turis"</string> <string name="app_icon_text" msgid="2823268023931811747">"Lietotnes ikona"</string> <string name="fullscreen_text" msgid="1162316685217676079">"Pilnekrāna režīms"</string> - <!-- no translation found for desktop_text (9058641752519570266) --> - <skip /> + <string name="desktop_text" msgid="9058641752519570266">"Darbvirsmas logošana"</string> <string name="split_screen_text" msgid="1396336058129570886">"Sadalīt ekrānu"</string> <string name="more_button_text" msgid="3655388105592893530">"Vairāk"</string> <string name="float_button_text" msgid="9221657008391364581">"Peldošs"</string> @@ -136,8 +134,7 @@ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Mainīt malu attiecību"</string> <string name="close_text" msgid="4986518933445178928">"Aizvērt"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Aizvērt izvēlni"</string> - <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) --> - <skip /> + <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (darbvirsmas logošana)"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimizēt ekrānu"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Mainīt lielumu"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Lietotni nevar pārvietot šeit."</string> diff --git a/libs/WindowManager/Shell/res/values-mk/strings.xml b/libs/WindowManager/Shell/res/values-mk/strings.xml index 96bf9b67144e..06ed3232514b 100644 --- a/libs/WindowManager/Shell/res/values-mk/strings.xml +++ b/libs/WindowManager/Shell/res/values-mk/strings.xml @@ -100,8 +100,7 @@ <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Не се поправи?\nДопрете за враќање"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Нема проблеми со камерата? Допрете за отфрлање."</string> <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Менито со апликации може да го најдете овде"</string> - <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) --> - <skip /> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Влезете во режимот со прозорци на работната површина за да отворите повеќе апликации заедно"</string> <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Вратете се на цел екран од менито со апликации кога сакате"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Погледнете и направете повеќе"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Повлечете друга апликација за поделен екран"</string> @@ -122,8 +121,7 @@ <string name="handle_text" msgid="4419667835599523257">"Прекар на апликацијата"</string> <string name="app_icon_text" msgid="2823268023931811747">"Икона на апликацијата"</string> <string name="fullscreen_text" msgid="1162316685217676079">"Цел екран"</string> - <!-- no translation found for desktop_text (9058641752519570266) --> - <skip /> + <string name="desktop_text" msgid="9058641752519570266">"Режим со прозорци на работната површина"</string> <string name="split_screen_text" msgid="1396336058129570886">"Поделен екран"</string> <string name="more_button_text" msgid="3655388105592893530">"Повеќе"</string> <string name="float_button_text" msgid="9221657008391364581">"Лебдечко"</string> @@ -136,8 +134,7 @@ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Промени го соодносот"</string> <string name="close_text" msgid="4986518933445178928">"Затворете"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Затворете го менито"</string> - <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) --> - <skip /> + <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (режим со прозорци на работната површина)"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Максимизирај го екранот"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Смени големина"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Апликацијата не може да се премести овде"</string> diff --git a/libs/WindowManager/Shell/res/values-ml/strings.xml b/libs/WindowManager/Shell/res/values-ml/strings.xml index 8085601076ef..dd4ec1bbf1cd 100644 --- a/libs/WindowManager/Shell/res/values-ml/strings.xml +++ b/libs/WindowManager/Shell/res/values-ml/strings.xml @@ -100,8 +100,7 @@ <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"അത് പരിഹരിച്ചില്ലേ?\nപുനഃസ്ഥാപിക്കാൻ ടാപ്പ് ചെയ്യുക"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ക്യാമറാ പ്രശ്നങ്ങളൊന്നുമില്ലേ? നിരസിക്കാൻ ടാപ്പ് ചെയ്യുക."</string> <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"ആപ്പ് മെനു ഇവിടെ കണ്ടെത്താനാകും"</string> - <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) --> - <skip /> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"ഒന്നിലധികം ആപ്പുകൾ ഒരുമിച്ച് തുറക്കാൻ ഡെസ്ക്ടോപ്പ് വിൻഡോയിംഗിൽ പ്രവേശിക്കുക"</string> <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"ആപ്പ് മെനുവിൽ നിന്ന് ഏതുസമയത്തും പൂർണ്ണ സ്ക്രീനിലേക്ക് മടങ്ങുക"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"കൂടുതൽ കാണുക, ചെയ്യുക"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"സ്ക്രീൻ വിഭജന മോഡിന്, മറ്റൊരു ആപ്പ് വലിച്ചിടുക"</string> @@ -122,8 +121,7 @@ <string name="handle_text" msgid="4419667835599523257">"ആപ്പ് ഹാൻഡിൽ"</string> <string name="app_icon_text" msgid="2823268023931811747">"ആപ്പ് ഐക്കൺ"</string> <string name="fullscreen_text" msgid="1162316685217676079">"പൂർണ്ണസ്ക്രീൻ"</string> - <!-- no translation found for desktop_text (9058641752519570266) --> - <skip /> + <string name="desktop_text" msgid="9058641752519570266">"ഡെസ്ക്ടോപ്പ് വിൻഡോയിംഗ്"</string> <string name="split_screen_text" msgid="1396336058129570886">"സ്ക്രീൻ വിഭജനം"</string> <string name="more_button_text" msgid="3655388105592893530">"കൂടുതൽ"</string> <string name="float_button_text" msgid="9221657008391364581">"ഫ്ലോട്ട്"</string> @@ -136,8 +134,7 @@ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"വീക്ഷണ അനുപാതം മാറ്റുക"</string> <string name="close_text" msgid="4986518933445178928">"അടയ്ക്കുക"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"മെനു അടയ്ക്കുക"</string> - <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) --> - <skip /> + <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ഡെസ്ക്ടോപ്പ് വിൻഡോയിംഗ്)"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"സ്ക്രീൻ വലുതാക്കുക"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"വലുപ്പം മാറ്റുക"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"ആപ്പ് ഇവിടേക്ക് നീക്കാനാകില്ല"</string> diff --git a/libs/WindowManager/Shell/res/values-mn/strings.xml b/libs/WindowManager/Shell/res/values-mn/strings.xml index 5efe62d7d837..8683827ae004 100644 --- a/libs/WindowManager/Shell/res/values-mn/strings.xml +++ b/libs/WindowManager/Shell/res/values-mn/strings.xml @@ -100,8 +100,7 @@ <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Үүнийг засаагүй юу?\nБуцаахын тулд товшино уу"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Камерын асуудал байхгүй юу? Хаахын тулд товшино уу."</string> <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Аппын цэсийг эндээс олох боломжтой"</string> - <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) --> - <skip /> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Олон аппыг хамтад нь нээхийн тулд дэлгэцийн цонх үүсгэх гэж орно уу"</string> <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Аппын цэсээс бүтэн дэлгэц рүү хүссэн үедээ буцна уу"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Харж илүү ихийг хий"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Дэлгэц хуваах горимд ашиглахын тулд өөр аппыг чирнэ үү"</string> @@ -122,8 +121,7 @@ <string name="handle_text" msgid="4419667835599523257">"Аппын бариул"</string> <string name="app_icon_text" msgid="2823268023931811747">"Aппын дүрс тэмдэг"</string> <string name="fullscreen_text" msgid="1162316685217676079">"Бүтэн дэлгэц"</string> - <!-- no translation found for desktop_text (9058641752519570266) --> - <skip /> + <string name="desktop_text" msgid="9058641752519570266">"Дэлгэцийн цонх үүсгэх онцлог"</string> <string name="split_screen_text" msgid="1396336058129570886">"Дэлгэцийг хуваах"</string> <string name="more_button_text" msgid="3655388105592893530">"Бусад"</string> <string name="float_button_text" msgid="9221657008391364581">"Хөвөгч"</string> @@ -136,8 +134,7 @@ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Аспектын харьцааг өөрчлөх"</string> <string name="close_text" msgid="4986518933445178928">"Хаах"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Цэсийг хаах"</string> - <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) --> - <skip /> + <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Дэлгэцийн цонх үүсгэх онцлог)"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Дэлгэцийг томруулах"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Хэмжээг өөрчлөх"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Аппыг ийш зөөх боломжгүй"</string> diff --git a/libs/WindowManager/Shell/res/values-mr/strings.xml b/libs/WindowManager/Shell/res/values-mr/strings.xml index e10c8d82440d..b7b3fb673d96 100644 --- a/libs/WindowManager/Shell/res/values-mr/strings.xml +++ b/libs/WindowManager/Shell/res/values-mr/strings.xml @@ -100,8 +100,7 @@ <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"निराकरण झाले नाही?\nरिव्हर्ट करण्यासाठी कृपया टॅप करा"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"कॅमेराशी संबंधित कोणत्याही समस्या नाहीत का? डिसमिस करण्यासाठी टॅप करा."</string> <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"ॲप मेनू इथे आढळू शकतो"</string> - <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) --> - <skip /> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"एकाहून अधिक ॲप्स एकत्र उघडण्यासाठी डेस्कटॉप विंडोइंग एंटर करा"</string> <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"ॲप मेनूमधून कधीही फुल स्क्रीनवर परत या"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"पहा आणि आणखी बरेच काही करा"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"स्प्लिट स्क्रीन वापरण्यासाठी दुसरे ॲप ड्रॅग करा"</string> @@ -122,8 +121,7 @@ <string name="handle_text" msgid="4419667835599523257">"अॅपचे हँडल"</string> <string name="app_icon_text" msgid="2823268023931811747">"अॅप आयकन"</string> <string name="fullscreen_text" msgid="1162316685217676079">"फुलस्क्रीन"</string> - <!-- no translation found for desktop_text (9058641752519570266) --> - <skip /> + <string name="desktop_text" msgid="9058641752519570266">"डेस्कटॉप विंडोइंग"</string> <string name="split_screen_text" msgid="1396336058129570886">"स्प्लिट स्क्रीन"</string> <string name="more_button_text" msgid="3655388105592893530">"आणखी"</string> <string name="float_button_text" msgid="9221657008391364581">"फ्लोट"</string> @@ -136,8 +134,7 @@ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"आस्पेक्ट रेशो बदला"</string> <string name="close_text" msgid="4986518933445178928">"बंद करा"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"मेनू बंद करा"</string> - <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) --> - <skip /> + <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (डेस्कटॉप विंडोइंग)"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"स्क्रीन मोठी करा"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"आकार बदला"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"अॅप इथे हलवू शकत नाही"</string> diff --git a/libs/WindowManager/Shell/res/values-ms/strings.xml b/libs/WindowManager/Shell/res/values-ms/strings.xml index e18e7ec68640..0896b5ee88cb 100644 --- a/libs/WindowManager/Shell/res/values-ms/strings.xml +++ b/libs/WindowManager/Shell/res/values-ms/strings.xml @@ -100,8 +100,7 @@ <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Isu tidak dibetulkan?\nKetik untuk kembali"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Tiada isu kamera? Ketik untuk mengetepikan."</string> <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Menu apl boleh ditemukan di sini"</string> - <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) --> - <skip /> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Masuki tetingkap desktop untuk membuka berbilang apl serentak"</string> <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Kembali kepada skrin penuh pada bila-bila masa daripada menu apl"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Lihat dan lakukan lebih"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Seret masuk apl lain untuk menggunakan skrin pisah"</string> @@ -122,8 +121,7 @@ <string name="handle_text" msgid="4419667835599523257">"Pengendalian apl"</string> <string name="app_icon_text" msgid="2823268023931811747">"Ikon Apl"</string> <string name="fullscreen_text" msgid="1162316685217676079">"Skrin penuh"</string> - <!-- no translation found for desktop_text (9058641752519570266) --> - <skip /> + <string name="desktop_text" msgid="9058641752519570266">"Tetingkap desktop"</string> <string name="split_screen_text" msgid="1396336058129570886">"Skrin Pisah"</string> <string name="more_button_text" msgid="3655388105592893530">"Lagi"</string> <string name="float_button_text" msgid="9221657008391364581">"Terapung"</string> @@ -136,8 +134,7 @@ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Tukar nisbah bidang"</string> <string name="close_text" msgid="4986518933445178928">"Tutup"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Tutup Menu"</string> - <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) --> - <skip /> + <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Tetingkap desktop)"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimumkan Skrin"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Ubah saiz"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Apl tidak boleh dialihkan ke sini"</string> diff --git a/libs/WindowManager/Shell/res/values-my/strings.xml b/libs/WindowManager/Shell/res/values-my/strings.xml index 334a79799d53..0f336e828f47 100644 --- a/libs/WindowManager/Shell/res/values-my/strings.xml +++ b/libs/WindowManager/Shell/res/values-my/strings.xml @@ -100,8 +100,7 @@ <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ကောင်းမသွားဘူးလား။\nပြန်ပြောင်းရန် တို့ပါ"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ကင်မရာပြဿနာ မရှိဘူးလား။ ပယ်ရန် တို့ပါ။"</string> <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"အက်ပ်မီနူးကို ဤနေရာတွင် တွေ့နိုင်သည်"</string> - <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) --> - <skip /> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"အက်ပ်များစွာကို အတူတကွဖွင့်ရန်အတွက် ဒက်စ်တော့ဝင်းဒိုးမုဒ်သို့ ဝင်ရောက်နိုင်သည်"</string> <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"အက်ပ်မီနူးမှ ဖန်သားပြင်အပြည့်သို့ အချိန်မရွေး ပြန်သွားနိုင်သည်"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"ကြည့်ပြီး ပိုမိုလုပ်ဆောင်ပါ"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"မျက်နှာပြင် ခွဲ၍ပြသခြင်းအတွက် အက်ပ်နောက်တစ်ခုကို ဖိဆွဲပါ"</string> @@ -122,8 +121,7 @@ <string name="handle_text" msgid="4419667835599523257">"အက်ပ်သုံးသူအမည်"</string> <string name="app_icon_text" msgid="2823268023931811747">"အက်ပ်သင်္ကေတ"</string> <string name="fullscreen_text" msgid="1162316685217676079">"ဖန်သားပြင်အပြည့်"</string> - <!-- no translation found for desktop_text (9058641752519570266) --> - <skip /> + <string name="desktop_text" msgid="9058641752519570266">"ဒက်စ်တော့ဝင်းဒိုးမုဒ်"</string> <string name="split_screen_text" msgid="1396336058129570886">"မျက်နှာပြင် ခွဲ၍ပြသရန်"</string> <string name="more_button_text" msgid="3655388105592893530">"ပိုပြပါ"</string> <string name="float_button_text" msgid="9221657008391364581">"မျှောရန်"</string> @@ -136,8 +134,7 @@ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"အချိုးအစား ပြောင်းရန်"</string> <string name="close_text" msgid="4986518933445178928">"ပိတ်ရန်"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"မီနူး ပိတ်ရန်"</string> - <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) --> - <skip /> + <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ဒက်စ်တော့ဝင်းဒိုးမုဒ်)"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"စခရင်ကို ချဲ့မည်"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"အရွယ်ပြင်ရန်"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"အက်ပ်ကို ဤနေရာသို့ ရွှေ့၍မရပါ"</string> diff --git a/libs/WindowManager/Shell/res/values-nb/strings.xml b/libs/WindowManager/Shell/res/values-nb/strings.xml index 7e21b475b2fa..3207614c013f 100644 --- a/libs/WindowManager/Shell/res/values-nb/strings.xml +++ b/libs/WindowManager/Shell/res/values-nb/strings.xml @@ -100,8 +100,7 @@ <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Ble ikke problemet løst?\nTrykk for å gå tilbake"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Har du ingen kameraproblemer? Trykk for å lukke."</string> <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Her finner du appmenyen"</string> - <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) --> - <skip /> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Start datamaskin-vindusvisning for å åpne flere apper samtidig"</string> <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Du kan gå tilbake til fullskjermmodusen når som helst fra appmenyen"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Se og gjør mer"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Dra inn en annen app for å bruke delt skjerm"</string> @@ -122,8 +121,7 @@ <string name="handle_text" msgid="4419667835599523257">"Apphåndtak"</string> <string name="app_icon_text" msgid="2823268023931811747">"Appikon"</string> <string name="fullscreen_text" msgid="1162316685217676079">"Fullskjerm"</string> - <!-- no translation found for desktop_text (9058641752519570266) --> - <skip /> + <string name="desktop_text" msgid="9058641752519570266">"Datamaskin-vindusvisning"</string> <string name="split_screen_text" msgid="1396336058129570886">"Delt skjerm"</string> <string name="more_button_text" msgid="3655388105592893530">"Mer"</string> <string name="float_button_text" msgid="9221657008391364581">"Svevende"</string> @@ -136,8 +134,7 @@ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Endre høyde/bredde-forholdet"</string> <string name="close_text" msgid="4986518933445178928">"Lukk"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Lukk menyen"</string> - <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) --> - <skip /> + <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (datamaskin-vindusvisning)"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimer skjermen"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Endre størrelse"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Appen kan ikke flyttes hit"</string> diff --git a/libs/WindowManager/Shell/res/values-ne/strings.xml b/libs/WindowManager/Shell/res/values-ne/strings.xml index 976f8378606a..2c7be991dd67 100644 --- a/libs/WindowManager/Shell/res/values-ne/strings.xml +++ b/libs/WindowManager/Shell/res/values-ne/strings.xml @@ -100,8 +100,7 @@ <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"समस्या हल भएन?\nपहिलेको जस्तै बनाउन ट्याप गर्नुहोस्"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"क्यामेरासम्बन्धी कुनै पनि समस्या छैन? खारेज गर्न ट्याप गर्नुहोस्।"</string> <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"एपको मेनु यहाँ भेट्टाउन सकिन्छ"</string> - <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) --> - <skip /> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"एकभन्दा बढी एपहरू सँगै देखाउन डेस्कटप विन्डोइङ हाल्नुहोस्"</string> <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"जुनसुकै बेला एपको मेनुबाट फुल स्क्रिनमा फर्कनुहोस्"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"थप कुरा हेर्नुहोस् र गर्नुहोस्"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"स्प्लिट स्क्रिन मोड प्रयोग गर्न अर्को एप ड्रयाग एन्ड ड्रप गर्नुहोस्"</string> @@ -122,8 +121,7 @@ <string name="handle_text" msgid="4419667835599523257">"एपको ह्यान्डल"</string> <string name="app_icon_text" msgid="2823268023931811747">"एपको आइकन"</string> <string name="fullscreen_text" msgid="1162316685217676079">"फुल स्क्रिन"</string> - <!-- no translation found for desktop_text (9058641752519570266) --> - <skip /> + <string name="desktop_text" msgid="9058641752519570266">"डेस्कटप विन्डोइङ"</string> <string name="split_screen_text" msgid="1396336058129570886">"स्प्लिट स्क्रिन"</string> <string name="more_button_text" msgid="3655388105592893530">"थप"</string> <string name="float_button_text" msgid="9221657008391364581">"फ्लोट"</string> @@ -136,8 +134,7 @@ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"एस्पेक्ट रेसियो परिवर्तन गर्नुहोस्"</string> <string name="close_text" msgid="4986518933445178928">"बन्द गर्नुहोस्"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"मेनु बन्द गर्नुहोस्"</string> - <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) --> - <skip /> + <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (डेस्कटप विन्डोइङ)"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"स्क्रिन ठुलो बनाउनुहोस्"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"आकार बदल्नुहोस्"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"एप सारेर यहाँ ल्याउन सकिएन"</string> diff --git a/libs/WindowManager/Shell/res/values-nl/strings.xml b/libs/WindowManager/Shell/res/values-nl/strings.xml index 7178e418a5aa..099f875e0eb9 100644 --- a/libs/WindowManager/Shell/res/values-nl/strings.xml +++ b/libs/WindowManager/Shell/res/values-nl/strings.xml @@ -100,8 +100,7 @@ <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Is dit geen oplossing?\nTik om terug te zetten."</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Geen cameraproblemen? Tik om te sluiten."</string> <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Het app-menu vind je hier"</string> - <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) --> - <skip /> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Ga naar de desktopvensterfunctie om meerdere apps tegelijk te openen"</string> <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Ga wanneer je wilt terug naar volledig scherm vanuit het app-menu"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Zie en doe meer"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Sleep een andere app hier naartoe om het scherm te splitsen"</string> @@ -122,8 +121,7 @@ <string name="handle_text" msgid="4419667835599523257">"App-handgreep"</string> <string name="app_icon_text" msgid="2823268023931811747">"App-icoon"</string> <string name="fullscreen_text" msgid="1162316685217676079">"Volledig scherm"</string> - <!-- no translation found for desktop_text (9058641752519570266) --> - <skip /> + <string name="desktop_text" msgid="9058641752519570266">"Desktopvensterfunctie"</string> <string name="split_screen_text" msgid="1396336058129570886">"Gesplitst scherm"</string> <string name="more_button_text" msgid="3655388105592893530">"Meer"</string> <string name="float_button_text" msgid="9221657008391364581">"Zwevend"</string> @@ -136,8 +134,7 @@ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Beeldverhouding wijzigen"</string> <string name="close_text" msgid="4986518933445178928">"Sluiten"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Menu sluiten"</string> - <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) --> - <skip /> + <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (desktopvensterfunctie)"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Scherm maximaliseren"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Formaat aanpassen"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Kan de app niet hierheen verplaatsen"</string> diff --git a/libs/WindowManager/Shell/res/values-pa/strings.xml b/libs/WindowManager/Shell/res/values-pa/strings.xml index 7e8929003017..50e3cd6a3022 100644 --- a/libs/WindowManager/Shell/res/values-pa/strings.xml +++ b/libs/WindowManager/Shell/res/values-pa/strings.xml @@ -100,8 +100,7 @@ <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"ਕੀ ਇਹ ਠੀਕ ਨਹੀਂ ਹੋਈ?\nਵਾਪਸ ਉਹੀ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"ਕੀ ਕੈਮਰੇ ਸੰਬੰਧੀ ਕੋਈ ਸਮੱਸਿਆ ਨਹੀਂ ਹੈ? ਖਾਰਜ ਕਰਨ ਲਈ ਟੈਪ ਕਰੋ।"</string> <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"ਐਪ ਮੀਨੂ ਇੱਥੇ ਮਿਲ ਸਕਦਾ ਹੈ"</string> - <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) --> - <skip /> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"ਕਈ ਐਪਾਂ ਨੂੰ ਇਕੱਠੇ ਖੋਲ੍ਹਣ ਲਈ ਡੈਸਕਟਾਪ ਵਿੰਡੋ ਵਿੱਚ ਦਾਖਲ ਹੋਵੋ"</string> <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"ਐਪ ਮੀਨੂ ਤੋਂ ਕਿਸੇ ਵੀ ਸਮੇਂ ਪੂਰੀ ਸਕ੍ਰੀਨ \'ਤੇ ਵਾਪਸ ਜਾਓ"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"ਦੇਖੋ ਅਤੇ ਹੋਰ ਬਹੁਤ ਕੁਝ ਕਰੋ"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"ਸਪਲਿਟ ਸਕ੍ਰੀਨ ਦੇ ਲਈ ਕਿਸੇ ਹੋਰ ਐਪ ਵਿੱਚ ਘਸੀਟੋ"</string> @@ -122,8 +121,7 @@ <string name="handle_text" msgid="4419667835599523257">"ਐਪ ਹੈਂਡਲ"</string> <string name="app_icon_text" msgid="2823268023931811747">"ਐਪ ਪ੍ਰਤੀਕ"</string> <string name="fullscreen_text" msgid="1162316685217676079">"ਪੂਰੀ-ਸਕ੍ਰੀਨ"</string> - <!-- no translation found for desktop_text (9058641752519570266) --> - <skip /> + <string name="desktop_text" msgid="9058641752519570266">"ਡੈਸਕਟਾਪ ਵਿੰਡੋ"</string> <string name="split_screen_text" msgid="1396336058129570886">"ਸਪਲਿਟ ਸਕ੍ਰੀਨ"</string> <string name="more_button_text" msgid="3655388105592893530">"ਹੋਰ"</string> <string name="float_button_text" msgid="9221657008391364581">"ਫ਼ਲੋਟ"</string> @@ -136,8 +134,7 @@ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"ਆਕਾਰ ਅਨੁਪਾਤ ਬਦਲੋ"</string> <string name="close_text" msgid="4986518933445178928">"ਬੰਦ ਕਰੋ"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"ਮੀਨੂ ਬੰਦ ਕਰੋ"</string> - <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) --> - <skip /> + <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ਡੈਸਕਟਾਪ ਵਿੰਡੋ)"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"ਸਕ੍ਰੀਨ ਦਾ ਆਕਾਰ ਵਧਾਓ"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"ਆਕਾਰ ਬਦਲੋ"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"ਐਪ ਨੂੰ ਇੱਥੇ ਨਹੀਂ ਲਿਜਾਇਆ ਜਾ ਸਕਦਾ"</string> diff --git a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml index 765a2071a037..d5535183551e 100644 --- a/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml +++ b/libs/WindowManager/Shell/res/values-pt-rPT/strings.xml @@ -100,8 +100,7 @@ <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Não foi corrigido?\nToque para reverter"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nenhum problema com a câmara? Toque para ignorar."</string> <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"O menu da app pode ser encontrado aqui"</string> - <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) --> - <skip /> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Entre no modo de janelas de computador para abrir várias apps em conjunto"</string> <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Regresse ao ecrã inteiro em qualquer altura a partir do menu da app"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Veja e faça mais"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Arraste outra app para usar o ecrã dividido"</string> @@ -122,8 +121,7 @@ <string name="handle_text" msgid="4419667835599523257">"Indicador da app"</string> <string name="app_icon_text" msgid="2823268023931811747">"Ícone da app"</string> <string name="fullscreen_text" msgid="1162316685217676079">"Ecrã inteiro"</string> - <!-- no translation found for desktop_text (9058641752519570266) --> - <skip /> + <string name="desktop_text" msgid="9058641752519570266">"Janelas de computador"</string> <string name="split_screen_text" msgid="1396336058129570886">"Ecrã dividido"</string> <string name="more_button_text" msgid="3655388105592893530">"Mais"</string> <string name="float_button_text" msgid="9221657008391364581">"Flutuar"</string> @@ -136,8 +134,7 @@ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Alterar formato"</string> <string name="close_text" msgid="4986518933445178928">"Fechar"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Fechar menu"</string> - <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) --> - <skip /> + <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (janelas de computador)"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximizar ecrã"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Redimensionar"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Não é possível mover a app para aqui"</string> diff --git a/libs/WindowManager/Shell/res/values-ro/strings.xml b/libs/WindowManager/Shell/res/values-ro/strings.xml index 68d045fdbe29..286443c69d72 100644 --- a/libs/WindowManager/Shell/res/values-ro/strings.xml +++ b/libs/WindowManager/Shell/res/values-ro/strings.xml @@ -100,8 +100,7 @@ <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nu ai remediat problema?\nAtinge pentru a reveni"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nu ai probleme cu camera foto? Atinge pentru a închide."</string> <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Meniul aplicației poate fi găsit aici"</string> - <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) --> - <skip /> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Accesează afișarea în ferestre pe desktop pentru a deschide mai multe aplicații simultan"</string> <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Revino oricând la ecranul complet din meniul aplicației"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Vezi și fă mai multe"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Trage în altă aplicație pentru a folosi ecranul împărțit"</string> @@ -122,8 +121,7 @@ <string name="handle_text" msgid="4419667835599523257">"Handle de aplicație"</string> <string name="app_icon_text" msgid="2823268023931811747">"Pictograma aplicației"</string> <string name="fullscreen_text" msgid="1162316685217676079">"Ecran complet"</string> - <!-- no translation found for desktop_text (9058641752519570266) --> - <skip /> + <string name="desktop_text" msgid="9058641752519570266">"Ferestre pe desktop"</string> <string name="split_screen_text" msgid="1396336058129570886">"Ecran împărțit"</string> <string name="more_button_text" msgid="3655388105592893530">"Mai multe"</string> <string name="float_button_text" msgid="9221657008391364581">"Flotantă"</string> @@ -136,8 +134,7 @@ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Schimbă raportul de dimensiuni"</string> <string name="close_text" msgid="4986518933445178928">"Închide"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Închide meniul"</string> - <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) --> - <skip /> + <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ferestre pe desktop)"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximizează fereastra"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Redimensionează"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Aplicația nu poate fi mutată aici"</string> diff --git a/libs/WindowManager/Shell/res/values-ru/strings.xml b/libs/WindowManager/Shell/res/values-ru/strings.xml index 8e683b8afb79..472a239ca344 100644 --- a/libs/WindowManager/Shell/res/values-ru/strings.xml +++ b/libs/WindowManager/Shell/res/values-ru/strings.xml @@ -100,8 +100,7 @@ <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Не помогло?\nНажмите, чтобы отменить изменения."</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Нет проблем с камерой? Нажмите, чтобы закрыть."</string> <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Здесь вы найдете меню приложения"</string> - <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) --> - <skip /> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Чтобы открыть сразу несколько приложений, перейдите в режим компьютерных окон"</string> <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Вернуться в полноэкранный режим можно из меню приложения"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Выполняйте несколько задач одновременно"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Перетащите сюда другое приложение, чтобы использовать разделение экрана."</string> @@ -122,8 +121,7 @@ <string name="handle_text" msgid="4419667835599523257">"Обозначение приложения"</string> <string name="app_icon_text" msgid="2823268023931811747">"Значок приложения"</string> <string name="fullscreen_text" msgid="1162316685217676079">"Полноэкранный режим"</string> - <!-- no translation found for desktop_text (9058641752519570266) --> - <skip /> + <string name="desktop_text" msgid="9058641752519570266">"Режим компьютерных окон"</string> <string name="split_screen_text" msgid="1396336058129570886">"Разделить экран"</string> <string name="more_button_text" msgid="3655388105592893530">"Ещё"</string> <string name="float_button_text" msgid="9221657008391364581">"Плавающее окно"</string> @@ -136,8 +134,7 @@ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Изменить соотношение сторон"</string> <string name="close_text" msgid="4986518933445178928">"Закрыть"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Закрыть меню"</string> - <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) --> - <skip /> + <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (режим компьютерных окон)"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Развернуть на весь экран"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Изменить размер"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Приложение нельзя сюда переместить"</string> diff --git a/libs/WindowManager/Shell/res/values-si/strings.xml b/libs/WindowManager/Shell/res/values-si/strings.xml index 6d548395bfac..6c91955a425e 100644 --- a/libs/WindowManager/Shell/res/values-si/strings.xml +++ b/libs/WindowManager/Shell/res/values-si/strings.xml @@ -100,8 +100,7 @@ <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"එය විසඳුවේ නැතිද?\nප්රතිවර්තනය කිරීමට තට්ටු කරන්න"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"කැමරා ගැටලු නොමැතිද? ඉවත දැමීමට තට්ටු කරන්න"</string> <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"යෙදුම් මෙනුව මෙතැනින් සොයා ගත හැක"</string> - <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) --> - <skip /> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"බහු යෙදුම් එකවර විවෘත කිරීමට ඩෙස්ක්ටොප් කවුළුවට ඇතුළු වන්න"</string> <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"යෙදුම් මෙනුවෙන් ඕනෑම වේලාවක පූර්ණ තිරය වෙත ආපසු යන්න"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"බලන්න සහ තවත් දේ කරන්න"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"බෙදුම් තිරය සඳහා වෙනත් යෙදුමකට අදින්න"</string> @@ -122,8 +121,7 @@ <string name="handle_text" msgid="4419667835599523257">"යෙදුම් හසුරුව"</string> <string name="app_icon_text" msgid="2823268023931811747">"යෙදුම් නිරූපකය"</string> <string name="fullscreen_text" msgid="1162316685217676079">"පූර්ණ තිරය"</string> - <!-- no translation found for desktop_text (9058641752519570266) --> - <skip /> + <string name="desktop_text" msgid="9058641752519570266">"ඩෙස්ක්ටොප් කවුළුකරණය"</string> <string name="split_screen_text" msgid="1396336058129570886">"බෙදුම් තිරය"</string> <string name="more_button_text" msgid="3655388105592893530">"තව"</string> <string name="float_button_text" msgid="9221657008391364581">"පාවෙන"</string> @@ -136,8 +134,7 @@ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"දර්ශන අනුපාතය වෙනස් කරන්න"</string> <string name="close_text" msgid="4986518933445178928">"වසන්න"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"මෙනුව වසන්න"</string> - <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) --> - <skip /> + <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ඩෙස්ක්ටොප් කවුළුකරණය)"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"තිරය උපරිම කරන්න"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"ප්රතිප්රමාණය කරන්න"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"යෙදුම මෙතැනට ගෙන යා නොහැක"</string> diff --git a/libs/WindowManager/Shell/res/values-sk/strings.xml b/libs/WindowManager/Shell/res/values-sk/strings.xml index c04b03873dc7..08404d016789 100644 --- a/libs/WindowManager/Shell/res/values-sk/strings.xml +++ b/libs/WindowManager/Shell/res/values-sk/strings.xml @@ -100,8 +100,7 @@ <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nevyriešilo sa to?\nKlepnutím sa vráťte."</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nemáte problémy s kamerou? Klepnutím zatvoríte."</string> <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Ponuku aplikácie nájdete tu"</string> - <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) --> - <skip /> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Aktivujte windowing na pracovnej ploche a otvorte viac aplikácií naraz"</string> <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Z ponuky aplikácie sa môžete kedykoľvek vrátiť na celú obrazovku"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Zobrazte si a zvládnite toho viac"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Rozdelenú obrazovku môžete použiť presunutím do inej aplikácie"</string> @@ -122,8 +121,7 @@ <string name="handle_text" msgid="4419667835599523257">"Rukoväť aplikácie"</string> <string name="app_icon_text" msgid="2823268023931811747">"Ikona aplikácie"</string> <string name="fullscreen_text" msgid="1162316685217676079">"Celá obrazovka"</string> - <!-- no translation found for desktop_text (9058641752519570266) --> - <skip /> + <string name="desktop_text" msgid="9058641752519570266">"Windowing na pracovnej ploche"</string> <string name="split_screen_text" msgid="1396336058129570886">"Rozdelená obrazovka"</string> <string name="more_button_text" msgid="3655388105592893530">"Viac"</string> <string name="float_button_text" msgid="9221657008391364581">"Plávajúce"</string> @@ -136,8 +134,7 @@ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Zmeniť pomer strán"</string> <string name="close_text" msgid="4986518933445178928">"Zavrieť"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Zavrieť ponuku"</string> - <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) --> - <skip /> + <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (windowing na pracovnej ploche)"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximalizovať obrazovku"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Zmeniť veľkosť"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Aplikácia sa sem nedá presunúť"</string> diff --git a/libs/WindowManager/Shell/res/values-sl/strings.xml b/libs/WindowManager/Shell/res/values-sl/strings.xml index 22d7bfe978cc..deed0e0fe27a 100644 --- a/libs/WindowManager/Shell/res/values-sl/strings.xml +++ b/libs/WindowManager/Shell/res/values-sl/strings.xml @@ -100,8 +100,7 @@ <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"To ni odpravilo težave?\nDotaknite se za povrnitev"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nimate težav s fotoaparatom? Dotaknite se za opustitev."</string> <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Meni aplikacije najdete tukaj"</string> - <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) --> - <skip /> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Preklopite v namizni način prikaza več oken hkrati, če želite odpreti več aplikacij hkrati"</string> <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"V meniju aplikacije se lahko kadar koli vrnete v celozaslonski način"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Oglejte si in naredite več"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Za razdeljeni zaslon povlecite sem še eno aplikacijo."</string> @@ -122,8 +121,7 @@ <string name="handle_text" msgid="4419667835599523257">"Identifikator aplikacije"</string> <string name="app_icon_text" msgid="2823268023931811747">"Ikona aplikacije"</string> <string name="fullscreen_text" msgid="1162316685217676079">"Celozaslonsko"</string> - <!-- no translation found for desktop_text (9058641752519570266) --> - <skip /> + <string name="desktop_text" msgid="9058641752519570266">"Namizni način prikaza več oken hkrati"</string> <string name="split_screen_text" msgid="1396336058129570886">"Razdeljen zaslon"</string> <string name="more_button_text" msgid="3655388105592893530">"Več"</string> <string name="float_button_text" msgid="9221657008391364581">"Lebdeče"</string> @@ -136,8 +134,7 @@ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Sprememba razmerja stranic"</string> <string name="close_text" msgid="4986518933445178928">"Zapri"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Zapri meni"</string> - <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) --> - <skip /> + <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (namizni način prikaza več oken hkrati)"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimiraj zaslon"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Spremeni velikost"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Aplikacije ni mogoče premakniti sem"</string> diff --git a/libs/WindowManager/Shell/res/values-sq/strings.xml b/libs/WindowManager/Shell/res/values-sq/strings.xml index 9330ec3edde5..c6b8c6da56c2 100644 --- a/libs/WindowManager/Shell/res/values-sq/strings.xml +++ b/libs/WindowManager/Shell/res/values-sq/strings.xml @@ -100,8 +100,7 @@ <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Nuk u rregullua?\nTrokit për ta rikthyer"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Nuk ka probleme me kamerën? Trokit për ta shpërfillur."</string> <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Menyja e aplikacioneve mund të gjendet këtu"</string> - <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) --> - <skip /> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Kalo te ndërfaqja me dritare në desktop për të hapur disa aplikacione së bashku"</string> <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Kthehu tek ekrani i plotë në çdo kohë nga menyja e aplikacioneve"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Shiko dhe bëj më shumë"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Zvarrite në një aplikacion tjetër për ekranin e ndarë"</string> @@ -122,8 +121,7 @@ <string name="handle_text" msgid="4419667835599523257">"Emërtimi i aplikacionit"</string> <string name="app_icon_text" msgid="2823268023931811747">"Ikona e aplikacionit"</string> <string name="fullscreen_text" msgid="1162316685217676079">"Ekrani i plotë"</string> - <!-- no translation found for desktop_text (9058641752519570266) --> - <skip /> + <string name="desktop_text" msgid="9058641752519570266">"Ndërfaqja me dritare në desktop"</string> <string name="split_screen_text" msgid="1396336058129570886">"Ekrani i ndarë"</string> <string name="more_button_text" msgid="3655388105592893530">"Më shumë"</string> <string name="float_button_text" msgid="9221657008391364581">"Pluskuese"</string> @@ -136,8 +134,7 @@ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Ndrysho raportin e pamjes"</string> <string name="close_text" msgid="4986518933445178928">"Mbyll"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Mbyll menynë"</string> - <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) --> - <skip /> + <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ndërfaqja me dritare në desktop)"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maksimizo ekranin"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Ndrysho përmasat"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Aplikacioni nuk mund të zhvendoset këtu"</string> diff --git a/libs/WindowManager/Shell/res/values-sr/strings.xml b/libs/WindowManager/Shell/res/values-sr/strings.xml index 5bdad159b146..3fe25f9d3d9e 100644 --- a/libs/WindowManager/Shell/res/values-sr/strings.xml +++ b/libs/WindowManager/Shell/res/values-sr/strings.xml @@ -100,8 +100,7 @@ <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Проблем није решен?\nДодирните да бисте вратили"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Немате проблема са камером? Додирните да бисте одбацили."</string> <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Мени апликације можете да пронађете овде"</string> - <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) --> - <skip /> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Уђите у прозорски приказ за рачунаре да бисте истовремено отворили више апликација"</string> <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Вратите се на цео екран било када из менија апликације"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Видите и урадите више"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Превуците другу апликацију да бисте користили подељени екран"</string> @@ -122,8 +121,7 @@ <string name="handle_text" msgid="4419667835599523257">"Идентификатор апликације"</string> <string name="app_icon_text" msgid="2823268023931811747">"Икона апликације"</string> <string name="fullscreen_text" msgid="1162316685217676079">"Преко целог екрана"</string> - <!-- no translation found for desktop_text (9058641752519570266) --> - <skip /> + <string name="desktop_text" msgid="9058641752519570266">"Прозорски приказ за рачунаре"</string> <string name="split_screen_text" msgid="1396336058129570886">"Подељени екран"</string> <string name="more_button_text" msgid="3655388105592893530">"Још"</string> <string name="float_button_text" msgid="9221657008391364581">"Плутајуће"</string> @@ -136,8 +134,7 @@ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Промени размеру"</string> <string name="close_text" msgid="4986518933445178928">"Затворите"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Затворите мени"</string> - <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) --> - <skip /> + <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (прозорски приказ за рачунаре)"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Повећај екран"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Прилагоди"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Апликација не може да се премести овде"</string> diff --git a/libs/WindowManager/Shell/res/values-sv/strings.xml b/libs/WindowManager/Shell/res/values-sv/strings.xml index 40a0a5aa6288..404bdaf6294d 100644 --- a/libs/WindowManager/Shell/res/values-sv/strings.xml +++ b/libs/WindowManager/Shell/res/values-sv/strings.xml @@ -100,8 +100,7 @@ <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Löstes inte problemet?\nTryck för att återställa"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Inga problem med kameran? Tryck för att ignorera."</string> <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Appmenyn finns här"</string> - <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) --> - <skip /> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Använd fönsterstapling för att öppna flera appar samtidigt"</string> <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Återgå till helskärm när som helst från appmenyn"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Se och gör mer"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Dra till en annan app för att dela upp skärmen"</string> @@ -122,8 +121,7 @@ <string name="handle_text" msgid="4419667835599523257">"Apphandtag"</string> <string name="app_icon_text" msgid="2823268023931811747">"Appikon"</string> <string name="fullscreen_text" msgid="1162316685217676079">"Helskärm"</string> - <!-- no translation found for desktop_text (9058641752519570266) --> - <skip /> + <string name="desktop_text" msgid="9058641752519570266">"Fönsterstapling"</string> <string name="split_screen_text" msgid="1396336058129570886">"Delad skärm"</string> <string name="more_button_text" msgid="3655388105592893530">"Mer"</string> <string name="float_button_text" msgid="9221657008391364581">"Svävande"</string> @@ -136,8 +134,7 @@ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Ändra bildformat"</string> <string name="close_text" msgid="4986518933445178928">"Stäng"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Stäng menyn"</string> - <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) --> - <skip /> + <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (fönsterstapling)"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Maximera skärmen"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Ändra storlek"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Det går inte att flytta appen hit"</string> diff --git a/libs/WindowManager/Shell/res/values-sw/strings.xml b/libs/WindowManager/Shell/res/values-sw/strings.xml index 20429e17d0d6..3bd7988874b8 100644 --- a/libs/WindowManager/Shell/res/values-sw/strings.xml +++ b/libs/WindowManager/Shell/res/values-sw/strings.xml @@ -100,8 +100,7 @@ <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Umeshindwa kurekebisha?\nGusa ili urejeshe nakala ya awali"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Je, hakuna hitilafu za kamera? Gusa ili uondoe."</string> <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Unaweza kupata menyu ya programu hapa"</string> - <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) --> - <skip /> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Tumia hali ya kupanga madirisha ya kompyuta ya mezani ili ufungue programu nyingi pamoja"</string> <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Rudi kwenye skrini nzima wakati wowote ukitumia menyu ya programu"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Angalia na ufanye zaidi"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Buruta katika programu nyingine ili utumie skrini iliyogawanywa"</string> @@ -122,8 +121,7 @@ <string name="handle_text" msgid="4419667835599523257">"Utambulisho wa programu"</string> <string name="app_icon_text" msgid="2823268023931811747">"Aikoni ya Programu"</string> <string name="fullscreen_text" msgid="1162316685217676079">"Skrini nzima"</string> - <!-- no translation found for desktop_text (9058641752519570266) --> - <skip /> + <string name="desktop_text" msgid="9058641752519570266">"Kupanga madirisha ya kompyuta ya mezani"</string> <string name="split_screen_text" msgid="1396336058129570886">"Gawa Skrini"</string> <string name="more_button_text" msgid="3655388105592893530">"Zaidi"</string> <string name="float_button_text" msgid="9221657008391364581">"Inayoelea"</string> @@ -136,8 +134,7 @@ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Badilisha uwiano"</string> <string name="close_text" msgid="4986518933445178928">"Funga"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Funga Menyu"</string> - <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) --> - <skip /> + <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Kupanga madirisha ya kompyuta ya mezani)"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Panua Dirisha kwenye Skrini"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Badilisha ukubwa"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Imeshindwa kuhamishia programu hapa"</string> diff --git a/libs/WindowManager/Shell/res/values-ta/strings.xml b/libs/WindowManager/Shell/res/values-ta/strings.xml index 74ecfdcf73a9..12780dfd1747 100644 --- a/libs/WindowManager/Shell/res/values-ta/strings.xml +++ b/libs/WindowManager/Shell/res/values-ta/strings.xml @@ -100,8 +100,7 @@ <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"சிக்கல்கள் சரிசெய்யப்படவில்லையா?\nமாற்றியமைக்க தட்டவும்"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"கேமரா தொடர்பான சிக்கல்கள் எதுவும் இல்லையா? நிராகரிக்க தட்டவும்."</string> <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"ஆப்ஸ் மெனுவை இங்கே பார்க்கலாம்"</string> - <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) --> - <skip /> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"பல ஆப்ஸை ஒன்றாகத் திறக்க டெஸ்க்டாப் சாளரமாக்குதலுக்குச் செல்லலாம்"</string> <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"ஆப்ஸ் மெனுவிலிருந்து எப்போது வேண்டுமானாலும் முழுத்திரைக்குத் திரும்பலாம்"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"பலவற்றைப் பார்த்தல் மற்றும் செய்தல்"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"திரைப் பிரிப்புக்கு மற்றொரு ஆப்ஸை இழுக்கலாம்"</string> @@ -122,8 +121,7 @@ <string name="handle_text" msgid="4419667835599523257">"ஆப்ஸ் ஹேண்டில்"</string> <string name="app_icon_text" msgid="2823268023931811747">"ஆப்ஸ் ஐகான்"</string> <string name="fullscreen_text" msgid="1162316685217676079">"முழுத்திரை"</string> - <!-- no translation found for desktop_text (9058641752519570266) --> - <skip /> + <string name="desktop_text" msgid="9058641752519570266">"டெஸ்க்டாப் சாளரமாக்குதல்"</string> <string name="split_screen_text" msgid="1396336058129570886">"திரையைப் பிரிக்கும்"</string> <string name="more_button_text" msgid="3655388105592893530">"கூடுதல் விருப்பத்தேர்வுகள்"</string> <string name="float_button_text" msgid="9221657008391364581">"மிதக்கும் சாளரம்"</string> @@ -136,8 +134,7 @@ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"தோற்ற விகிதத்தை மாற்று"</string> <string name="close_text" msgid="4986518933445178928">"மூடும்"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"மெனுவை மூடும்"</string> - <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) --> - <skip /> + <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (டெஸ்க்டாப் சாளரமாக்குதல்)"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"திரையைப் பெரிதாக்கு"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"அளவை மாற்று"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"ஆப்ஸை இங்கே நகர்த்த முடியாது"</string> diff --git a/libs/WindowManager/Shell/res/values-te/strings.xml b/libs/WindowManager/Shell/res/values-te/strings.xml index f46c9b628d1a..2044fe70c7c3 100644 --- a/libs/WindowManager/Shell/res/values-te/strings.xml +++ b/libs/WindowManager/Shell/res/values-te/strings.xml @@ -100,8 +100,7 @@ <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"దాని సమస్యను పరిష్కరించలేదా?\nపూర్వస్థితికి మార్చడానికి ట్యాప్ చేయండి"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"కెమెరా సమస్యలు లేవా? తీసివేయడానికి ట్యాప్ చేయండి."</string> <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"యాప్ మెనూను ఇక్కడ పొందవచ్చు"</string> - <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) --> - <skip /> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"పలు యాప్లను ఒకేసారి తెరవడానికి డెస్క్టాప్ వీక్షణకు ఎంటర్ అవ్వండి"</string> <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"యాప్ మెనూ నుండి ఏ సమయంలోనైనా ఫుల్ స్క్రీన్కు తిరిగి రండి"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"చూసి, మరిన్ని చేయండి"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"స్ప్లిట్ స్క్రీన్ కోసం మరొక యాప్లోకి లాగండి"</string> @@ -122,8 +121,7 @@ <string name="handle_text" msgid="4419667835599523257">"యాప్ హ్యాండిల్"</string> <string name="app_icon_text" msgid="2823268023931811747">"యాప్ చిహ్నం"</string> <string name="fullscreen_text" msgid="1162316685217676079">"ఫుల్-స్క్రీన్"</string> - <!-- no translation found for desktop_text (9058641752519570266) --> - <skip /> + <string name="desktop_text" msgid="9058641752519570266">"డెస్క్టాప్ వీక్షణ"</string> <string name="split_screen_text" msgid="1396336058129570886">"స్ప్లిట్ స్క్రీన్"</string> <string name="more_button_text" msgid="3655388105592893530">"మరిన్ని"</string> <string name="float_button_text" msgid="9221657008391364581">"ఫ్లోట్"</string> @@ -136,8 +134,7 @@ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"ఆకార నిష్పత్తిని మార్చండి"</string> <string name="close_text" msgid="4986518933445178928">"మూసివేయండి"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"మెనూను మూసివేయండి"</string> - <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) --> - <skip /> + <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (డెస్క్టాప్ వీక్షణ)"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"స్క్రీన్ సైజ్ను పెంచండి"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"సైజ్ మార్చండి"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"యాప్ను ఇక్కడకి తరలించడం సాధ్యం కాదు"</string> diff --git a/libs/WindowManager/Shell/res/values-tl/strings.xml b/libs/WindowManager/Shell/res/values-tl/strings.xml index ae6df04c255c..586d655d7901 100644 --- a/libs/WindowManager/Shell/res/values-tl/strings.xml +++ b/libs/WindowManager/Shell/res/values-tl/strings.xml @@ -100,8 +100,7 @@ <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Hindi ito naayos?\nI-tap para i-revert"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Walang isyu sa camera? I-tap para i-dismiss."</string> <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Makikita rito ang menu ng app"</string> - <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) --> - <skip /> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Pumunta sa desktop windowing para magbukas ng maraming app nang sabay-sabay"</string> <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Bumalik sa full screen anumang oras mula sa menu ng app"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Tumingin at gumawa ng higit pa"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Mag-drag ng isa pang app para sa split screen"</string> @@ -122,8 +121,7 @@ <string name="handle_text" msgid="4419667835599523257">"Handle ng app"</string> <string name="app_icon_text" msgid="2823268023931811747">"Icon ng App"</string> <string name="fullscreen_text" msgid="1162316685217676079">"Fullscreen"</string> - <!-- no translation found for desktop_text (9058641752519570266) --> - <skip /> + <string name="desktop_text" msgid="9058641752519570266">"Desktop windowing"</string> <string name="split_screen_text" msgid="1396336058129570886">"Split Screen"</string> <string name="more_button_text" msgid="3655388105592893530">"Higit pa"</string> <string name="float_button_text" msgid="9221657008391364581">"Float"</string> @@ -136,8 +134,7 @@ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Baguhin ang aspect ratio"</string> <string name="close_text" msgid="4986518933445178928">"Isara"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Isara ang Menu"</string> - <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) --> - <skip /> + <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Desktop windowing)"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"I-maximize ang Screen"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"I-resize"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Hindi mailipat dito ang app"</string> diff --git a/libs/WindowManager/Shell/res/values-tr/strings.xml b/libs/WindowManager/Shell/res/values-tr/strings.xml index 116740ef93f7..9605acfb9a8b 100644 --- a/libs/WindowManager/Shell/res/values-tr/strings.xml +++ b/libs/WindowManager/Shell/res/values-tr/strings.xml @@ -100,8 +100,7 @@ <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Bu işlem sorunu düzeltmedi mi?\nİşlemi geri almak için dokunun"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Kameranızda sorun yok mu? Kapatmak için dokunun."</string> <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Uygulama menüsünü burada bulabilirsiniz"</string> - <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) --> - <skip /> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Birden fazla uygulamayı birlikte açmak için masaüstü pencerelemeye geçin"</string> <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Uygulama menüsünden dilediğiniz zaman tam ekrana dönebilirsiniz"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Daha fazlasını görün ve yapın"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Bölünmüş ekran için başka bir uygulamayı sürükleyin"</string> @@ -122,8 +121,7 @@ <string name="handle_text" msgid="4419667835599523257">"Uygulama tanıtıcısı"</string> <string name="app_icon_text" msgid="2823268023931811747">"Uygulama Simgesi"</string> <string name="fullscreen_text" msgid="1162316685217676079">"Tam Ekran"</string> - <!-- no translation found for desktop_text (9058641752519570266) --> - <skip /> + <string name="desktop_text" msgid="9058641752519570266">"Masaüstü pencereleme"</string> <string name="split_screen_text" msgid="1396336058129570886">"Bölünmüş Ekran"</string> <string name="more_button_text" msgid="3655388105592893530">"Daha Fazla"</string> <string name="float_button_text" msgid="9221657008391364581">"Havada Süzülen"</string> @@ -136,8 +134,7 @@ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"En boy oranını değiştir"</string> <string name="close_text" msgid="4986518933445178928">"Kapat"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Menüyü kapat"</string> - <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) --> - <skip /> + <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (masaüstü pencereleme)"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Ekranı Büyüt"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Yeniden boyutlandır"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Uygulama buraya taşınamıyor"</string> diff --git a/libs/WindowManager/Shell/res/values-ur/strings.xml b/libs/WindowManager/Shell/res/values-ur/strings.xml index 635bc4086c0d..9614ce9112f7 100644 --- a/libs/WindowManager/Shell/res/values-ur/strings.xml +++ b/libs/WindowManager/Shell/res/values-ur/strings.xml @@ -100,8 +100,7 @@ <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"یہ حل نہیں ہوا؟\nلوٹانے کیلئے تھپتھپائیں"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"کوئی کیمرے کا مسئلہ نہیں ہے؟ برخاست کرنے کیلئے تھپتھپائیں۔"</string> <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"ایپ کا مینو یہاں پایا جا سکتا ہے"</string> - <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) --> - <skip /> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"متعدد ایپس کو ایک ساتھ کھولنے کے لیے ڈیسک ٹاپ ونڈوئنگ میں داخل ہوں"</string> <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"ایپ مینو سے کسی بھی وقت فُل اسکرین پر واپس جائیں"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"دیکھیں اور بہت کچھ کریں"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"اسپلٹ اسکرین کے ليے دوسری ایپ میں گھسیٹیں"</string> @@ -122,8 +121,7 @@ <string name="handle_text" msgid="4419667835599523257">"ایپ ہینڈل"</string> <string name="app_icon_text" msgid="2823268023931811747">"ایپ کا آئیکن"</string> <string name="fullscreen_text" msgid="1162316685217676079">"مکمل اسکرین"</string> - <!-- no translation found for desktop_text (9058641752519570266) --> - <skip /> + <string name="desktop_text" msgid="9058641752519570266">"ڈیسک ٹاپ ونڈوئنگ"</string> <string name="split_screen_text" msgid="1396336058129570886">"اسپلٹ اسکرین"</string> <string name="more_button_text" msgid="3655388105592893530">"مزید"</string> <string name="float_button_text" msgid="9221657008391364581">"فلوٹ"</string> @@ -136,8 +134,7 @@ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"تناسبی شرح کو تبدیل کریں"</string> <string name="close_text" msgid="4986518933445178928">"بند کریں"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"مینیو بند کریں"</string> - <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) --> - <skip /> + <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (ڈیسک ٹاپ ونڈوئنگ)"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"اسکرین کو بڑا کریں"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"سائز تبدیل کریں"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"ایپ کو یہاں منتقل نہیں کیا جا سکتا"</string> diff --git a/libs/WindowManager/Shell/res/values-uz/strings.xml b/libs/WindowManager/Shell/res/values-uz/strings.xml index 6ce2bd859bea..6025467ea44b 100644 --- a/libs/WindowManager/Shell/res/values-uz/strings.xml +++ b/libs/WindowManager/Shell/res/values-uz/strings.xml @@ -100,8 +100,7 @@ <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Tuzatilmadimi?\nQaytarish uchun bosing"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Kamera muammosizmi? Yopish uchun bosing."</string> <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Ilova menyusi shu yerda chiqadi"</string> - <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) --> - <skip /> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Bir nechta ilovani birga ochish uchun oynalarni desktop rejimida chiqarish"</string> <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Ilova menyusi orqali istalganda butun ekranga qaytish mumkin"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Yana boshqa amallar"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Ekranni ikkiga ajratish uchun boshqa ilovani bu yerga torting"</string> @@ -122,8 +121,7 @@ <string name="handle_text" msgid="4419667835599523257">"Ilova identifikatori"</string> <string name="app_icon_text" msgid="2823268023931811747">"Ilova belgisi"</string> <string name="fullscreen_text" msgid="1162316685217676079">"Butun ekran"</string> - <!-- no translation found for desktop_text (9058641752519570266) --> - <skip /> + <string name="desktop_text" msgid="9058641752519570266">"Desktop rejimidagi oynalar"</string> <string name="split_screen_text" msgid="1396336058129570886">"Ekranni ikkiga ajratish"</string> <string name="more_button_text" msgid="3655388105592893530">"Yana"</string> <string name="float_button_text" msgid="9221657008391364581">"Pufakli"</string> @@ -136,8 +134,7 @@ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Tomonlar nisbatini oʻzgartirish"</string> <string name="close_text" msgid="4986518933445178928">"Yopish"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Menyuni yopish"</string> - <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) --> - <skip /> + <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Desktop rejimidagi oynalar)"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Ekranni yoyish"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Oʻlchamini oʻzgartirish"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Ilova bu yerga surilmaydi"</string> diff --git a/libs/WindowManager/Shell/res/values-vi/strings.xml b/libs/WindowManager/Shell/res/values-vi/strings.xml index 67f80c142ea3..4c394b2aec46 100644 --- a/libs/WindowManager/Shell/res/values-vi/strings.xml +++ b/libs/WindowManager/Shell/res/values-vi/strings.xml @@ -100,8 +100,7 @@ <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Bạn chưa khắc phục vấn đề?\nHãy nhấn để hủy bỏ"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Không có vấn đề với máy ảnh? Hãy nhấn để đóng."</string> <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Bạn có thể tìm thấy trình đơn ứng dụng tại đây"</string> - <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) --> - <skip /> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Chuyển sang chế độ cửa sổ trên máy tính để mở nhiều ứng dụng cùng lúc"</string> <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Trở về chế độ toàn màn hình bất cứ lúc nào từ trình đơn ứng dụng"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Xem và làm được nhiều việc hơn"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Kéo một ứng dụng khác vào để chia đôi màn hình"</string> @@ -122,8 +121,7 @@ <string name="handle_text" msgid="4419667835599523257">"Ô điều khiển ứng dụng"</string> <string name="app_icon_text" msgid="2823268023931811747">"Biểu tượng ứng dụng"</string> <string name="fullscreen_text" msgid="1162316685217676079">"Toàn màn hình"</string> - <!-- no translation found for desktop_text (9058641752519570266) --> - <skip /> + <string name="desktop_text" msgid="9058641752519570266">"Cửa sổ trên máy tính"</string> <string name="split_screen_text" msgid="1396336058129570886">"Chia đôi màn hình"</string> <string name="more_button_text" msgid="3655388105592893530">"Tuỳ chọn khác"</string> <string name="float_button_text" msgid="9221657008391364581">"Nổi"</string> @@ -136,8 +134,7 @@ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Thay đổi tỷ lệ khung hình"</string> <string name="close_text" msgid="4986518933445178928">"Đóng"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Đóng trình đơn"</string> - <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) --> - <skip /> + <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Chế độ cửa sổ trên máy tính)"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Mở rộng màn hình"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Đổi kích thước"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"Không di chuyển được ứng dụng đến đây"</string> diff --git a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml index 74e6b5c1e491..fda5c744eccf 100644 --- a/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml +++ b/libs/WindowManager/Shell/res/values-zh-rHK/strings.xml @@ -100,8 +100,7 @@ <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"未能修正問題?\n輕按即可還原"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"相機冇問題?㩒一下就可以即可閂咗佢。"</string> <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"你可在這裡找到應用程式選單"</string> - <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) --> - <skip /> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"進入電腦分割視窗模式可同時開啟多個應用程式"</string> <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"你可隨時從應用程式選單返回全螢幕"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"瀏覽更多內容及執行更多操作"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"拖入另一個應用程式即可分割螢幕"</string> @@ -122,8 +121,7 @@ <string name="handle_text" msgid="4419667835599523257">"應用程式控點"</string> <string name="app_icon_text" msgid="2823268023931811747">"應用程式圖示"</string> <string name="fullscreen_text" msgid="1162316685217676079">"全螢幕"</string> - <!-- no translation found for desktop_text (9058641752519570266) --> - <skip /> + <string name="desktop_text" msgid="9058641752519570266">"電腦分割視窗"</string> <string name="split_screen_text" msgid="1396336058129570886">"分割螢幕"</string> <string name="more_button_text" msgid="3655388105592893530">"更多"</string> <string name="float_button_text" msgid="9221657008391364581">"浮動"</string> @@ -136,8 +134,7 @@ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"變更長寬比"</string> <string name="close_text" msgid="4986518933445178928">"關閉"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"關閉選單"</string> - <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) --> - <skip /> + <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (電腦分割視窗)"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"畫面最大化"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"調整大小"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"應用程式無法移至這裡"</string> diff --git a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml index 575b217229bd..e83c647b59bb 100644 --- a/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml +++ b/libs/WindowManager/Shell/res/values-zh-rTW/strings.xml @@ -100,8 +100,7 @@ <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"未修正問題嗎?\n輕觸即可還原"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"相機沒問題嗎?輕觸即可關閉。"</string> <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"你可以在這裡查看應用程式選單"</string> - <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) --> - <skip /> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"進入電腦分割視窗模式可同時開啟多個應用程式"</string> <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"你隨時可以從應用程式選單返回全螢幕模式"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"瀏覽更多內容及執行更多操作"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"拖進另一個應用程式即可使用分割畫面模式"</string> @@ -122,8 +121,7 @@ <string name="handle_text" msgid="4419667835599523257">"應用程式控制代碼"</string> <string name="app_icon_text" msgid="2823268023931811747">"應用程式圖示"</string> <string name="fullscreen_text" msgid="1162316685217676079">"全螢幕"</string> - <!-- no translation found for desktop_text (9058641752519570266) --> - <skip /> + <string name="desktop_text" msgid="9058641752519570266">"電腦分割視窗"</string> <string name="split_screen_text" msgid="1396336058129570886">"分割畫面"</string> <string name="more_button_text" msgid="3655388105592893530">"更多"</string> <string name="float_button_text" msgid="9221657008391364581">"浮動"</string> @@ -136,8 +134,7 @@ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"變更顯示比例"</string> <string name="close_text" msgid="4986518933445178928">"關閉"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"關閉選單"</string> - <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) --> - <skip /> + <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (電腦分割視窗)"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"畫面最大化"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"調整大小"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"應用程式無法移至此處"</string> diff --git a/libs/WindowManager/Shell/res/values-zu/strings.xml b/libs/WindowManager/Shell/res/values-zu/strings.xml index 30403cd21862..4d658f291e02 100644 --- a/libs/WindowManager/Shell/res/values-zu/strings.xml +++ b/libs/WindowManager/Shell/res/values-zu/strings.xml @@ -100,8 +100,7 @@ <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Akuyilungisanga?\nThepha ukuze ubuyele"</string> <string name="camera_compat_dismiss_button_description" msgid="2795364433503817511">"Azikho izinkinga zekhamera? Thepha ukuze ucashise."</string> <string name="windowing_app_handle_education_tooltip" msgid="2929643449849791854">"Imenyu ye-app ingatholakala lapha"</string> - <!-- no translation found for windowing_desktop_mode_image_button_education_tooltip (7171915734817051666) --> - <skip /> + <string name="windowing_desktop_mode_image_button_education_tooltip" msgid="7171915734817051666">"Faka ukwenziwa kwamawindi amaningi kwedeskithophu ukuze uvule ama-app amaningi ndawonye"</string> <string name="windowing_desktop_mode_exit_education_tooltip" msgid="5225660258192054132">"Buyela esikrinini esigcwele noma nini ukusuka kumenyu ye-app"</string> <string name="letterbox_education_dialog_title" msgid="7739895354143295358">"Bona futhi wenze okuningi"</string> <string name="letterbox_education_split_screen_text" msgid="449233070804658627">"Hudula kwenye i-app mayelana nokuhlukanisa isikrini"</string> @@ -122,8 +121,7 @@ <string name="handle_text" msgid="4419667835599523257">"Inkomba ye-App"</string> <string name="app_icon_text" msgid="2823268023931811747">"Isithonjana Se-app"</string> <string name="fullscreen_text" msgid="1162316685217676079">"Isikrini esigcwele"</string> - <!-- no translation found for desktop_text (9058641752519570266) --> - <skip /> + <string name="desktop_text" msgid="9058641752519570266">"Ukwenziwa kwamawindi amaningi kwedeskithophu"</string> <string name="split_screen_text" msgid="1396336058129570886">"Hlukanisa isikrini"</string> <string name="more_button_text" msgid="3655388105592893530">"Okwengeziwe"</string> <string name="float_button_text" msgid="9221657008391364581">"Iflowuthi"</string> @@ -136,8 +134,7 @@ <string name="change_aspect_ratio_text" msgid="9104456064548212806">"Shintsha ukubukeka kwesilinganiselo"</string> <string name="close_text" msgid="4986518933445178928">"Vala"</string> <string name="collapse_menu_text" msgid="7515008122450342029">"Vala Imenyu"</string> - <!-- no translation found for desktop_mode_app_header_chip_text (7617377295944971651) --> - <skip /> + <string name="desktop_mode_app_header_chip_text" msgid="7617377295944971651">"<xliff:g id="APP_NAME">%1$s</xliff:g> (Ukwenziwa kwamawindi amaningi kwedeskithophu)"</string> <string name="desktop_mode_maximize_menu_maximize_text" msgid="3275717276171114411">"Khulisa Isikrini Sifike Ekugcineni"</string> <string name="desktop_mode_maximize_menu_snap_text" msgid="5673738963174074006">"Shintsha usayizi"</string> <string name="desktop_mode_non_resizable_snap_text" msgid="3771776422751387878">"I-app ayikwazi ukuhanjiswa lapha"</string> diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml index e1bf6638a9b2..e68680219349 100644 --- a/libs/WindowManager/Shell/res/values/dimen.xml +++ b/libs/WindowManager/Shell/res/values/dimen.xml @@ -294,6 +294,8 @@ <dimen name="bubble_bar_expanded_view_drop_target_padding_top">60dp</dimen> <dimen name="bubble_bar_expanded_view_drop_target_padding_bottom">24dp</dimen> <dimen name="bubble_bar_expanded_view_drop_target_padding_horizontal">48dp</dimen> + <dimen name="bubble_bar_drop_target_width">84dp</dimen> + <dimen name="bubble_bar_drop_target_height">48dp</dimen> <!-- Width of the box around bottom center of the screen where drag only leads to dismiss --> <dimen name="bubble_bar_dismiss_zone_width">192dp</dimen> <!-- Height of the box around bottom center of the screen where drag only leads to dismiss --> diff --git a/libs/WindowManager/Shell/res/values/styles.xml b/libs/WindowManager/Shell/res/values/styles.xml index 5f1db83d7acb..08cda7b94a78 100644 --- a/libs/WindowManager/Shell/res/values/styles.xml +++ b/libs/WindowManager/Shell/res/values/styles.xml @@ -45,7 +45,13 @@ <item name="android:layout_height">52dp</item> <item name="android:textColor">@androidprv:color/materialColorOnSurface</item> <item name="android:drawableTint">@androidprv:color/materialColorOnSurface</item> - <item name="android:importantForAccessibility">no</item> + <item name="android:importantForAccessibility">yes</item> + <item name="android:gravity">start|center_vertical</item> + <item name="android:paddingHorizontal">16dp</item> + <item name="android:clickable">true</item> + <item name="android:focusable">true</item> + <item name="android:orientation">horizontal</item> + <item name="android:background">?android:attr/selectableItemBackground</item> </style> <style name="DesktopModeHandleMenuActionButtonImage"> diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/GroupedTaskInfo.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/GroupedTaskInfo.java index 25b9f8ccc6ae..f68afea92850 100644 --- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/GroupedTaskInfo.java +++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/GroupedTaskInfo.java @@ -18,6 +18,7 @@ package com.android.wm.shell.shared; import static android.app.WindowConfiguration.windowingModeToString; import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS; +import static android.view.Display.INVALID_DISPLAY; import android.annotation.IntDef; import android.app.ActivityManager.RecentTaskInfo; @@ -65,6 +66,11 @@ public class GroupedTaskInfo implements Parcelable { private final int mDeskId; /** + * The ID of the display that desk with [mDeskId] is in. + */ + private final int mDeskDisplayId; + + /** * The type of this particular task info, can be one of TYPE_FULLSCREEN, TYPE_SPLIT or * TYPE_DESK. */ @@ -109,17 +115,19 @@ public class GroupedTaskInfo implements Parcelable { * Create new for a stack of fullscreen tasks */ public static GroupedTaskInfo forFullscreenTasks(@NonNull TaskInfo task) { - return new GroupedTaskInfo(/* deskId = */ -1, List.of(task), null, TYPE_FULLSCREEN, - /* minimizedFreeformTaskIds = */ null); + return new GroupedTaskInfo(/* deskId = */ -1, /* displayId = */ INVALID_DISPLAY, + List.of(task), null, + TYPE_FULLSCREEN, /* minimizedFreeformTaskIds = */ null); } /** * Create new for a pair of tasks in split screen */ public static GroupedTaskInfo forSplitTasks(@NonNull TaskInfo task1, - @NonNull TaskInfo task2, @NonNull SplitBounds splitBounds) { - return new GroupedTaskInfo(/* deskId = */ -1, List.of(task1, task2), splitBounds, - TYPE_SPLIT, /* minimizedFreeformTaskIds = */ null); + @NonNull TaskInfo task2, @NonNull SplitBounds splitBounds) { + return new GroupedTaskInfo(/* deskId = */ -1, /* displayId = */ INVALID_DISPLAY, + List.of(task1, task2), + splitBounds, TYPE_SPLIT, /* minimizedFreeformTaskIds = */ null); } /** @@ -127,9 +135,11 @@ public class GroupedTaskInfo implements Parcelable { */ public static GroupedTaskInfo forDeskTasks( int deskId, + int deskDisplayId, @NonNull List<TaskInfo> tasks, @NonNull Set<Integer> minimizedFreeformTaskIds) { - return new GroupedTaskInfo(deskId, tasks, /* splitBounds = */ null, TYPE_DESK, + return new GroupedTaskInfo(deskId, deskDisplayId, tasks, /* splitBounds = */ null, + TYPE_DESK, minimizedFreeformTaskIds.stream().mapToInt(i -> i).toArray()); } @@ -149,11 +159,13 @@ public class GroupedTaskInfo implements Parcelable { private GroupedTaskInfo( int deskId, + int deskDisplayId, @NonNull List<TaskInfo> tasks, @Nullable SplitBounds splitBounds, @GroupType int type, @Nullable int[] minimizedFreeformTaskIds) { mDeskId = deskId; + mDeskDisplayId = deskDisplayId; mTasks = tasks; mGroupedTasks = null; mSplitBounds = splitBounds; @@ -164,6 +176,7 @@ public class GroupedTaskInfo implements Parcelable { private GroupedTaskInfo(@NonNull List<GroupedTaskInfo> groupedTasks) { mDeskId = -1; + mDeskDisplayId = INVALID_DISPLAY; mTasks = null; mGroupedTasks = groupedTasks; mSplitBounds = null; @@ -185,6 +198,7 @@ public class GroupedTaskInfo implements Parcelable { protected GroupedTaskInfo(@NonNull Parcel parcel) { mDeskId = parcel.readInt(); + mDeskDisplayId = parcel.readInt(); mTasks = new ArrayList(); final int numTasks = parcel.readInt(); for (int i = 0; i < numTasks; i++) { @@ -295,6 +309,16 @@ public class GroupedTaskInfo implements Parcelable { } /** + * Returns the ID of the display that hosts the desk represented by [mDeskId]. + */ + public int getDeskDisplayId() { + if (mType != TYPE_DESK) { + throw new IllegalStateException("No display ID for non desktop task"); + } + return mDeskDisplayId; + } + + /** * Get type of this recents entry. One of {@link GroupType}. * Note: This is deprecated, callers should use `isBaseType()` and not make assumptions about * specific group types @@ -323,6 +347,7 @@ public class GroupedTaskInfo implements Parcelable { } GroupedTaskInfo other = (GroupedTaskInfo) obj; return mDeskId == other.mDeskId + && mDeskDisplayId == other.mDeskDisplayId && mType == other.mType && Objects.equals(mTasks, other.mTasks) && Objects.equals(mGroupedTasks, other.mGroupedTasks) @@ -332,7 +357,7 @@ public class GroupedTaskInfo implements Parcelable { @Override public int hashCode() { - return Objects.hash(mDeskId, mType, mTasks, mGroupedTasks, mSplitBounds, + return Objects.hash(mDeskId, mDeskDisplayId, mType, mTasks, mGroupedTasks, mSplitBounds, Arrays.hashCode(mMinimizedTaskIds)); } @@ -345,6 +370,7 @@ public class GroupedTaskInfo implements Parcelable { .collect(Collectors.joining(",\n\t", "[\n\t", "\n]"))); } else { taskString.append("Desk ID= ").append(mDeskId).append(", "); + taskString.append("Desk Display ID=").append(mDeskDisplayId).append(", "); taskString.append("Tasks=" + mTasks.stream() .map(taskInfo -> getTaskInfoDumpString(taskInfo)) .collect(Collectors.joining(", ", "[", "]"))); @@ -377,6 +403,7 @@ public class GroupedTaskInfo implements Parcelable { @Override public void writeToParcel(Parcel parcel, int flags) { parcel.writeInt(mDeskId); + parcel.writeInt(mDeskDisplayId); // We don't use the parcel list methods because we want to only write the TaskInfo state // and not the subclasses (Recents/RunningTaskInfo) whose fields are all deprecated final int tasksSize = mTasks != null ? mTasks.size() : 0; diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleDropTargetBoundsProvider.kt b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleDropTargetBoundsProvider.kt index 9bee11a92430..84e0fbe96de2 100644 --- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleDropTargetBoundsProvider.kt +++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/bubbles/BubbleDropTargetBoundsProvider.kt @@ -26,4 +26,9 @@ interface BubbleDropTargetBoundsProvider { * Get bubble bar expanded view visual drop target bounds on screen */ fun getBubbleBarExpandedViewDropTargetBounds(onLeft: Boolean): Rect + + /** + * Get the bar visual drop target bounds on screen + */ + fun getBarDropTargetBounds(onLeft: Boolean): Rect }
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java index 7f8cfaeb9c03..5d59af940da0 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java @@ -332,7 +332,11 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont @Override public void onThresholdCrossed() { - BackAnimationController.this.onThresholdCrossed(); + if (predictiveBackDelayWmTransition()) { + mShellExecutor.execute(BackAnimationController.this::onThresholdCrossed); + } else { + BackAnimationController.this.onThresholdCrossed(); + } } @Override @@ -448,7 +452,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont final boolean shouldDispatchToAnimator = shouldDispatchToAnimator(); if (!shouldDispatchToAnimator && mActiveCallback != null) { mCurrentTracker.updateStartLocation(); - tryDispatchOnBackStarted(mActiveCallback, mCurrentTracker.createStartEvent()); + tryDispatchOnBackStarted(mActiveCallback, mCurrentTracker.createStartEvent(null)); if (mBackNavigationInfo != null && !isAppProgressGenerationAllowed()) { tryPilferPointers(); } @@ -604,7 +608,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont mActiveCallback = mBackNavigationInfo.getOnBackInvokedCallback(); // App is handling back animation. Cancel system animation latency tracking. cancelLatencyTracking(); - tryDispatchOnBackStarted(mActiveCallback, touchTracker.createStartEvent()); + tryDispatchOnBackStarted(mActiveCallback, touchTracker.createStartEvent(null)); if (!isAppProgressGenerationAllowed()) { tryPilferPointers(); } @@ -1041,7 +1045,7 @@ public class BackAnimationController implements RemoteCallable<BackAnimationCont () -> mShellExecutor.execute(this::onBackAnimationFinished)); if (mApps.length >= 1) { - BackMotionEvent startEvent = mCurrentTracker.createStartEvent(); + BackMotionEvent startEvent = mCurrentTracker.createStartEvent(mApps[0]); dispatchOnBackStarted(mActiveCallback, startEvent); if (startEvent.getSwipeEdge() == EDGE_NONE) { // TODO(b/373544911): onBackStarted is dispatched here so that diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java index 03d6b0a8075d..0b45b086e13c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java @@ -106,6 +106,8 @@ public class BubblePositioner implements BubbleDropTargetBoundsProvider { private int mBarExpViewDropTargetPaddingTop; private int mBarExpViewDropTargetPaddingBottom; private int mBarExpViewDropTargetPaddingHorizontal; + private int mBarDropTargetWidth; + private int mBarDropTargetHeight; private PointF mRestingStackPosition; @@ -181,6 +183,8 @@ public class BubblePositioner implements BubbleDropTargetBoundsProvider { R.dimen.bubble_bar_expanded_view_drop_target_padding_bottom); mBarExpViewDropTargetPaddingHorizontal = res.getDimensionPixelSize( R.dimen.bubble_bar_expanded_view_drop_target_padding_horizontal); + mBarDropTargetWidth = res.getDimensionPixelSize(R.dimen.bubble_bar_drop_target_width); + mBarDropTargetHeight = res.getDimensionPixelSize(R.dimen.bubble_bar_drop_target_height); if (mShowingInBubbleBar) { mExpandedViewLargeScreenWidth = mExpandedViewBubbleBarWidth; @@ -1003,4 +1007,20 @@ public class BubblePositioner implements BubbleDropTargetBoundsProvider { ); return bounds; } + + @NonNull + @Override + public Rect getBarDropTargetBounds(boolean onLeft) { + Rect bounds = getBubbleBarExpandedViewDropTargetBounds(onLeft); + bounds.top = getBubbleBarTopOnScreen(); + bounds.bottom = bounds.top + mBarDropTargetHeight; + if (onLeft) { + // Keep the left edge from expanded view + bounds.right = bounds.left + mBarDropTargetWidth; + } else { + // Keep the right edge from expanded view + bounds.left = bounds.right - mBarDropTargetWidth; + } + return bounds; + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTransitions.java index 728975e8ef9f..6f7d7a486453 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTransitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTransitions.java @@ -649,7 +649,7 @@ public class BubbleTransitions { @Override public void continueCollapse() { mBubble.cleanupTaskView(); - if (mTaskLeash == null || !mTaskLeash.isValid()) return; + if (mTaskLeash == null || !mTaskLeash.isValid() || !mRootLeash.isValid()) return; SurfaceControl.Transaction t = new SurfaceControl.Transaction(); t.reparent(mTaskLeash, mRootLeash); t.apply(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsAlgorithm.java index 04e8d8dee520..5d603d6c087d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsAlgorithm.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipBoundsAlgorithm.java @@ -221,8 +221,11 @@ public class PipBoundsAlgorithm { + " than destination(%s)", sourceRectHint, destinationBounds); return false; } - if (!PictureInPictureParams.isSameAspectRatio(sourceRectHint, - new Rational(destinationBounds.width(), destinationBounds.height()))) { + // We use the aspect ratio of source rect hint to check against destination bounds + // here to avoid upscaling error. + final Rational srcAspectRatio = new Rational( + sourceRectHint.width(), sourceRectHint.height()); + if (!PictureInPictureParams.isSameAspectRatio(destinationBounds, srcAspectRatio)) { ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE, "isSourceRectHintValidForEnterPip=false, hint(%s) does not match" + " destination(%s) aspect ratio", sourceRectHint, destinationBounds); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipDesktopState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipDesktopState.java deleted file mode 100644 index 1128fb2259b2..000000000000 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipDesktopState.java +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright (C) 2025 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.wm.shell.common.pip; - -import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; -import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; - -import android.window.DesktopExperienceFlags; -import android.window.DesktopModeFlags; -import android.window.DisplayAreaInfo; - -import com.android.wm.shell.Flags; -import com.android.wm.shell.RootTaskDisplayAreaOrganizer; -import com.android.wm.shell.desktopmode.DesktopUserRepositories; -import com.android.wm.shell.desktopmode.DragToDesktopTransitionHandler; - -import java.util.Optional; - -/** Helper class for PiP on Desktop Mode. */ -public class PipDesktopState { - private final PipDisplayLayoutState mPipDisplayLayoutState; - private final Optional<DesktopUserRepositories> mDesktopUserRepositoriesOptional; - private final Optional<DragToDesktopTransitionHandler> mDragToDesktopTransitionHandlerOptional; - private final RootTaskDisplayAreaOrganizer mRootTaskDisplayAreaOrganizer; - - public PipDesktopState(PipDisplayLayoutState pipDisplayLayoutState, - Optional<DesktopUserRepositories> desktopUserRepositoriesOptional, - Optional<DragToDesktopTransitionHandler> dragToDesktopTransitionHandlerOptional, - RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer) { - mPipDisplayLayoutState = pipDisplayLayoutState; - mDesktopUserRepositoriesOptional = desktopUserRepositoriesOptional; - mDragToDesktopTransitionHandlerOptional = dragToDesktopTransitionHandlerOptional; - mRootTaskDisplayAreaOrganizer = rootTaskDisplayAreaOrganizer; - } - - /** - * Returns whether PiP in Desktop Windowing is enabled by checking the following: - * - PiP in Desktop Windowing flag is enabled - * - DesktopUserRepositories is injected - * - DragToDesktopTransitionHandler is injected - */ - public boolean isDesktopWindowingPipEnabled() { - return DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PIP.isTrue() - && mDesktopUserRepositoriesOptional.isPresent() - && mDragToDesktopTransitionHandlerOptional.isPresent(); - } - - /** - * Returns whether PiP in Connected Displays is enabled by checking the following: - * - PiP in Connected Displays flag is enabled - * - PiP2 flag is enabled - */ - public boolean isConnectedDisplaysPipEnabled() { - return DesktopExperienceFlags.ENABLE_CONNECTED_DISPLAYS_PIP.isTrue() && Flags.enablePip2(); - } - - /** Returns whether the display with the PiP task is in freeform windowing mode. */ - private boolean isDisplayInFreeform() { - final DisplayAreaInfo tdaInfo = mRootTaskDisplayAreaOrganizer.getDisplayAreaInfo( - mPipDisplayLayoutState.getDisplayId()); - if (tdaInfo != null) { - return tdaInfo.configuration.windowConfiguration.getWindowingMode() - == WINDOWING_MODE_FREEFORM; - } - return false; - } - - /** Returns whether PiP is active in a display that is in active Desktop Mode session. */ - public boolean isPipInDesktopMode() { - // Early return if PiP in Desktop Windowing is not supported. - if (!isDesktopWindowingPipEnabled()) { - return false; - } - final int displayId = mPipDisplayLayoutState.getDisplayId(); - return mDesktopUserRepositoriesOptional.get().getCurrent().isAnyDeskActive(displayId); - } - - /** - * The windowing mode to restore to when resizing out of PIP direction. - * Defaults to undefined and can be overridden to restore to an alternate windowing mode. - */ - public int getOutPipWindowingMode() { - // If we are exiting PiP while the device is in Desktop mode (the task should expand to - // freeform windowing mode): - // 1) If the display windowing mode is freeform, set windowing mode to UNDEFINED so it will - // resolve the windowing mode to the display's windowing mode. - // 2) If the display windowing mode is not FREEFORM, set windowing mode to FREEFORM. - if (isPipInDesktopMode()) { - if (isDisplayInFreeform()) { - return WINDOWING_MODE_UNDEFINED; - } else { - return WINDOWING_MODE_FREEFORM; - } - } - - // By default, or if the task is going to fullscreen, reset the windowing mode to undefined. - return WINDOWING_MODE_UNDEFINED; - } - - /** Returns whether there is a drag-to-desktop transition in progress. */ - public boolean isDragToDesktopInProgress() { - // Early return if PiP in Desktop Windowing is not supported. - if (!isDesktopWindowingPipEnabled()) { - return false; - } - return mDragToDesktopTransitionHandlerOptional.get().getInProgress(); - } -} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipDesktopState.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipDesktopState.kt new file mode 100644 index 000000000000..55bde8906b63 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/pip/PipDesktopState.kt @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.wm.shell.common.pip + +import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM +import android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED +import android.window.DesktopExperienceFlags +import android.window.DesktopModeFlags +import com.android.wm.shell.Flags +import com.android.wm.shell.RootTaskDisplayAreaOrganizer +import com.android.wm.shell.desktopmode.DesktopUserRepositories +import com.android.wm.shell.desktopmode.DragToDesktopTransitionHandler +import java.util.Optional + +/** Helper class for PiP on Desktop Mode. */ +class PipDesktopState( + private val pipDisplayLayoutState: PipDisplayLayoutState, + private val desktopUserRepositoriesOptional: Optional<DesktopUserRepositories>, + private val dragToDesktopTransitionHandlerOptional: Optional<DragToDesktopTransitionHandler>, + private val rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer +) { + /** + * Returns whether PiP in Desktop Windowing is enabled by checking the following: + * - PiP in Desktop Windowing flag is enabled + * - DesktopUserRepositories is present + * - DragToDesktopTransitionHandler is present + */ + fun isDesktopWindowingPipEnabled(): Boolean = + DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_PIP.isTrue && + desktopUserRepositoriesOptional.isPresent && + dragToDesktopTransitionHandlerOptional.isPresent + + /** + * Returns whether PiP in Connected Displays is enabled by checking the following: + * - PiP in Connected Displays flag is enabled + * - PiP2 flag is enabled + */ + fun isConnectedDisplaysPipEnabled(): Boolean = + DesktopExperienceFlags.ENABLE_CONNECTED_DISPLAYS_PIP.isTrue && Flags.enablePip2() + + /** Returns whether the display with the PiP task is in freeform windowing mode. */ + private fun isDisplayInFreeform(): Boolean { + val tdaInfo = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo( + pipDisplayLayoutState.displayId + ) + + return tdaInfo?.configuration?.windowConfiguration?.windowingMode == WINDOWING_MODE_FREEFORM + } + + /** Returns whether PiP is active in a display that is in active Desktop Mode session. */ + fun isPipInDesktopMode(): Boolean { + if (!isDesktopWindowingPipEnabled()) { + return false + } + + val displayId = pipDisplayLayoutState.displayId + return desktopUserRepositoriesOptional.get().current.isAnyDeskActive(displayId) + } + + /** Returns the windowing mode to restore to when resizing out of PIP direction. */ + // TODO(b/403345629): Update this for Multi-Desktop. + fun getOutPipWindowingMode(): Int { + // If we are exiting PiP while the device is in Desktop mode, the task should expand to + // freeform windowing mode. + // 1) If the display windowing mode is freeform, set windowing mode to UNDEFINED so it will + // resolve the windowing mode to the display's windowing mode. + // 2) If the display windowing mode is not FREEFORM, set windowing mode to FREEFORM. + if (isPipInDesktopMode()) { + return if (isDisplayInFreeform()) { + WINDOWING_MODE_UNDEFINED + } else { + WINDOWING_MODE_FREEFORM + } + } + + // By default, or if the task is going to fullscreen, reset the windowing mode to undefined. + return WINDOWING_MODE_UNDEFINED + } + + /** Returns whether there is a drag-to-desktop transition in progress. */ + fun isDragToDesktopInProgress(): Boolean = + isDesktopWindowingPipEnabled() && dragToDesktopTransitionHandlerOptional.get().inProgress +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/FlexParallaxSpec.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/FlexParallaxSpec.java index 9fa162164e0e..d9a66e1d64b2 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/FlexParallaxSpec.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/FlexParallaxSpec.java @@ -57,6 +57,11 @@ public class FlexParallaxSpec implements ParallaxSpec { * @return 0f = no dim applied. 1f = full black. */ public float getDimValue(int position, DividerSnapAlgorithm snapAlgorithm) { + // On tablets, apps don't go offscreen, so only dim for dismissal. + if (!snapAlgorithm.areOffscreenRatiosSupported()) { + return ParallaxSpec.super.getDimValue(position, snapAlgorithm); + } + int startDismissPos = snapAlgorithm.getDismissStartTarget().getPosition(); int firstTargetPos = snapAlgorithm.getFirstSplitTarget().getPosition(); int middleTargetPos = snapAlgorithm.getMiddleTarget().getPosition(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java index 4413c8715c0d..d5f4a3885dbb 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java @@ -112,6 +112,12 @@ public class CompatUIController implements OnDisplaysChangedListener, new SparseArray<>(0); /** + * {@link SparseArray} that maps task ids to {@link CompatUIInfo}. + */ + private final SparseArray<CompatUIInfo> mTaskIdToCompatUIInfoMap = + new SparseArray<>(0); + + /** * {@link Set} of task ids for which we need to display a restart confirmation dialog */ private Set<Integer> mSetOfTaskIdsShowingRestartDialog = new HashSet<>(); @@ -261,7 +267,11 @@ public class CompatUIController implements OnDisplaysChangedListener, private void handleDisplayCompatShowRestartDialog( CompatUIRequests.DisplayCompatShowRestartDialog request) { - onRestartButtonClicked(new Pair<>(request.getTaskInfo(), request.getTaskListener())); + final CompatUIInfo compatUIInfo = mTaskIdToCompatUIInfoMap.get(request.getTaskId()); + if (compatUIInfo == null) { + return; + } + onRestartButtonClicked(new Pair<>(compatUIInfo.getTaskInfo(), compatUIInfo.getListener())); } /** @@ -273,6 +283,11 @@ public class CompatUIController implements OnDisplaysChangedListener, public void onCompatInfoChanged(@NonNull CompatUIInfo compatUIInfo) { final TaskInfo taskInfo = compatUIInfo.getTaskInfo(); final ShellTaskOrganizer.TaskListener taskListener = compatUIInfo.getListener(); + if (taskListener == null) { + mTaskIdToCompatUIInfoMap.delete(taskInfo.taskId); + } else { + mTaskIdToCompatUIInfoMap.put(taskInfo.taskId, compatUIInfo); + } final boolean isInDisplayCompatMode = taskInfo.appCompatTaskInfo.isRestartMenuEnabledForDisplayMove(); if (taskInfo != null && !taskInfo.appCompatTaskInfo.isTopActivityInSizeCompat() diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/impl/CompatUIRequests.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/impl/CompatUIRequests.kt index da4fc99491dc..b7af596ee0ae 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/impl/CompatUIRequests.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/impl/CompatUIRequests.kt @@ -16,8 +16,6 @@ package com.android.wm.shell.compatui.impl -import android.app.TaskInfo -import com.android.wm.shell.ShellTaskOrganizer import com.android.wm.shell.compatui.api.CompatUIRequest internal const val DISPLAY_COMPAT_SHOW_RESTART_DIALOG = 0 @@ -27,7 +25,6 @@ internal const val DISPLAY_COMPAT_SHOW_RESTART_DIALOG = 0 */ sealed class CompatUIRequests(override val requestId: Int) : CompatUIRequest { /** Sent when the restart handle menu is clicked, and a restart dialog is requested. */ - data class DisplayCompatShowRestartDialog(val taskInfo: TaskInfo, - val taskListener: ShellTaskOrganizer.TaskListener) : + data class DisplayCompatShowRestartDialog(val taskId: Int) : CompatUIRequests(DISPLAY_COMPAT_SHOW_RESTART_DIALOG) } 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 2cf671b0f446..613e78753b66 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 @@ -1213,7 +1213,8 @@ public abstract class WMShellModule { ShellTaskOrganizer shellTaskOrganizer, TaskStackListenerImpl taskStackListener, ToggleResizeDesktopTaskTransitionHandler toggleResizeDesktopTaskTransitionHandler, - @DynamicOverride DesktopUserRepositories desktopUserRepositories) { + @DynamicOverride DesktopUserRepositories desktopUserRepositories, + DisplayController displayController) { if (DesktopModeStatus.canEnterDesktopMode(context)) { return Optional.of( new DesktopActivityOrientationChangeHandler( @@ -1222,7 +1223,8 @@ public abstract class WMShellModule { shellTaskOrganizer, taskStackListener, toggleResizeDesktopTaskTransitionHandler, - desktopUserRepositories)); + desktopUserRepositories, + displayController)); } return Optional.empty(); } @@ -1341,7 +1343,9 @@ public abstract class WMShellModule { Context context, ShellInit shellInit, @ShellMainThread CoroutineScope mainScope, + ShellController shellController, DisplayController displayController, + RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer, Optional<DesktopUserRepositories> desktopUserRepositories, Optional<DesktopTasksController> desktopTasksController, Optional<DesktopDisplayModeController> desktopDisplayModeController, @@ -1355,7 +1359,9 @@ public abstract class WMShellModule { context, shellInit, mainScope, + shellController, displayController, + rootTaskDisplayAreaOrganizer, desktopRepositoryInitializer, desktopUserRepositories.get(), desktopTasksController.get(), diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopActivityOrientationChangeHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopActivityOrientationChangeHandler.kt index b8f4bb8d8323..39ce5d9023a6 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopActivityOrientationChangeHandler.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopActivityOrientationChangeHandler.kt @@ -23,10 +23,10 @@ import android.content.pm.ActivityInfo.ScreenOrientation import android.content.res.Configuration.ORIENTATION_LANDSCAPE import android.content.res.Configuration.ORIENTATION_PORTRAIT import android.graphics.Rect -import android.util.Size import android.window.WindowContainerTransaction import com.android.window.flags.Flags import com.android.wm.shell.ShellTaskOrganizer +import com.android.wm.shell.common.DisplayController import com.android.wm.shell.common.TaskStackListenerCallback import com.android.wm.shell.common.TaskStackListenerImpl import com.android.wm.shell.shared.desktopmode.DesktopModeStatus @@ -40,6 +40,7 @@ class DesktopActivityOrientationChangeHandler( private val taskStackListener: TaskStackListenerImpl, private val resizeHandler: ToggleResizeDesktopTaskTransitionHandler, private val desktopUserRepositories: DesktopUserRepositories, + private val displayController: DisplayController, ) { init { @@ -101,12 +102,24 @@ class DesktopActivityOrientationChangeHandler( orientation == ORIENTATION_LANDSCAPE && ActivityInfo.isFixedOrientationPortrait(requestedOrientation) ) { + val displayLayout = displayController.getDisplayLayout(task.displayId) ?: return + val captionInsets = + task.configuration.windowConfiguration.appBounds?.let { + it.top - task.configuration.windowConfiguration.bounds.top + } ?: 0 + val newOrientationBounds = + calculateInitialBounds( + displayLayout = displayLayout, + taskInfo = task, + captionInsets = captionInsets, + requestedScreenOrientation = requestedOrientation, + ) - val finalSize = Size(taskHeight, taskWidth) // Use the center x as the resizing anchor point. - val left = taskBounds.centerX() - finalSize.width / 2 - val right = left + finalSize.width - val finalBounds = Rect(left, taskBounds.top, right, taskBounds.top + finalSize.height) + val left = taskBounds.centerX() - newOrientationBounds.width() / 2 + val right = left + newOrientationBounds.width() + val finalBounds = + Rect(left, taskBounds.top, right, taskBounds.top + newOrientationBounds.height()) val wct = WindowContainerTransaction().setBounds(task.token, finalBounds) resizeHandler.startTransition(wct) diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandler.kt index 683b74392fa6..3b98f8123b46 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandler.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandler.kt @@ -17,16 +17,20 @@ package com.android.wm.shell.desktopmode import android.content.Context +import android.view.Display import android.view.Display.DEFAULT_DISPLAY import android.window.DesktopExperienceFlags import com.android.internal.protolog.ProtoLog +import com.android.wm.shell.RootTaskDisplayAreaOrganizer import com.android.wm.shell.common.DisplayController import com.android.wm.shell.common.DisplayController.OnDisplaysChangedListener import com.android.wm.shell.desktopmode.multidesks.OnDeskRemovedListener import com.android.wm.shell.desktopmode.persistence.DesktopRepositoryInitializer import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE import com.android.wm.shell.shared.desktopmode.DesktopModeStatus +import com.android.wm.shell.sysui.ShellController import com.android.wm.shell.sysui.ShellInit +import com.android.wm.shell.sysui.UserChangeListener import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.cancel import kotlinx.coroutines.launch @@ -36,7 +40,9 @@ class DesktopDisplayEventHandler( private val context: Context, shellInit: ShellInit, private val mainScope: CoroutineScope, + private val shellController: ShellController, private val displayController: DisplayController, + private val rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer, private val desktopRepositoryInitializer: DesktopRepositoryInitializer, private val desktopUserRepositories: DesktopUserRepositories, private val desktopTasksController: DesktopTasksController, @@ -53,8 +59,17 @@ class DesktopDisplayEventHandler( private fun onInit() { displayController.addDisplayWindowListener(this) - if (DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue()) { + if (DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) { desktopTasksController.onDeskRemovedListener = this + + shellController.addUserChangeListener( + object : UserChangeListener { + override fun onUserChanged(newUserId: Int, userContext: Context) { + val displayIds = rootTaskDisplayAreaOrganizer.displayIds + createDefaultDesksIfNeeded(displayIds.toSet()) + } + } + ) } } @@ -63,23 +78,7 @@ class DesktopDisplayEventHandler( desktopDisplayModeController.refreshDisplayWindowingMode() } - if (!supportsDesks(displayId)) { - logV("Display #$displayId does not support desks") - return - } - - mainScope.launch { - desktopRepositoryInitializer.isInitialized.collect { initialized -> - if (!initialized) return@collect - if (desktopRepository.getNumberOfDesks(displayId) == 0) { - logV("Creating new desk in new display#$displayId") - // TODO: b/393978539 - consider activating the desk on creation when - // applicable, such as for connected displays. - desktopTasksController.createDesk(displayId) - } - cancel() - } - } + createDefaultDesksIfNeeded(displayIds = setOf(displayId)) } override fun onDisplayRemoved(displayId: Int) { @@ -93,8 +92,34 @@ class DesktopDisplayEventHandler( override fun onDeskRemoved(lastDisplayId: Int, deskId: Int) { val remainingDesks = desktopRepository.getNumberOfDesks(lastDisplayId) if (remainingDesks == 0) { - logV("All desks removed from display#$lastDisplayId, creating empty desk") - desktopTasksController.createDesk(lastDisplayId) + logV("All desks removed from display#$lastDisplayId") + createDefaultDesksIfNeeded(setOf(lastDisplayId)) + } + } + + private fun createDefaultDesksIfNeeded(displayIds: Set<Int>) { + if (!DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) return + logV("createDefaultDesksIfNeeded displays=%s", displayIds) + mainScope.launch { + desktopRepositoryInitializer.isInitialized.collect { initialized -> + if (!initialized) return@collect + displayIds + .filter { displayId -> displayId != Display.INVALID_DISPLAY } + .filter { displayId -> supportsDesks(displayId) } + .filter { displayId -> desktopRepository.getNumberOfDesks(displayId) == 0 } + .also { displaysNeedingDesk -> + logV( + "createDefaultDesksIfNeeded creating default desks in displays=%s", + displaysNeedingDesk, + ) + } + .forEach { displayId -> + // TODO: b/393978539 - consider activating the desk on creation when + // applicable, such as for connected displays. + desktopTasksController.createDesk(displayId) + } + cancel() + } } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandler.kt index 1ea545f3ab67..19507c17bc95 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandler.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandler.kt @@ -23,10 +23,7 @@ import android.hardware.input.InputManager import android.hardware.input.InputManager.KeyGestureEventHandler import android.hardware.input.KeyGestureEvent import android.os.IBinder -import android.window.DesktopModeFlags -import com.android.hardware.input.Flags.manageKeyGestures import com.android.internal.protolog.ProtoLog -import com.android.window.flags.Flags.enableMoveToNextDisplayShortcut import com.android.wm.shell.ShellTaskOrganizer import com.android.wm.shell.common.DisplayController import com.android.wm.shell.common.ShellExecutor @@ -51,16 +48,20 @@ class DesktopModeKeyGestureHandler( ) : KeyGestureEventHandler { init { - inputManager.registerKeyGestureEventHandler(this) + if (desktopTasksController.isPresent && desktopModeWindowDecorViewModel.isPresent) { + val supportedGestures = + listOf( + KeyGestureEvent.KEY_GESTURE_TYPE_MOVE_TO_NEXT_DISPLAY, + KeyGestureEvent.KEY_GESTURE_TYPE_SNAP_LEFT_FREEFORM_WINDOW, + KeyGestureEvent.KEY_GESTURE_TYPE_SNAP_RIGHT_FREEFORM_WINDOW, + KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MAXIMIZE_FREEFORM_WINDOW, + KeyGestureEvent.KEY_GESTURE_TYPE_MINIMIZE_FREEFORM_WINDOW, + ) + inputManager.registerKeyGestureEventHandler(supportedGestures, this) + } } - override fun handleKeyGestureEvent(event: KeyGestureEvent, focusedToken: IBinder?): Boolean { - if ( - !desktopTasksController.isPresent || - !desktopModeWindowDecorViewModel.isPresent - ) { - return false - } + override fun handleKeyGestureEvent(event: KeyGestureEvent, focusedToken: IBinder?) { when (event.keyGestureType) { KeyGestureEvent.KEY_GESTURE_TYPE_MOVE_TO_NEXT_DISPLAY -> { logV("Key gesture MOVE_TO_NEXT_DISPLAY is handled") @@ -69,7 +70,6 @@ class DesktopModeKeyGestureHandler( desktopTasksController.get().moveToNextDisplay(it.taskId) } } - return true } KeyGestureEvent.KEY_GESTURE_TYPE_SNAP_LEFT_FREEFORM_WINDOW -> { logV("Key gesture SNAP_LEFT_FREEFORM_WINDOW is handled") @@ -85,7 +85,6 @@ class DesktopModeKeyGestureHandler( ) } } - return true } KeyGestureEvent.KEY_GESTURE_TYPE_SNAP_RIGHT_FREEFORM_WINDOW -> { logV("Key gesture SNAP_RIGHT_FREEFORM_WINDOW is handled") @@ -101,7 +100,6 @@ class DesktopModeKeyGestureHandler( ) } } - return true } KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MAXIMIZE_FREEFORM_WINDOW -> { logV("Key gesture TOGGLE_MAXIMIZE_FREEFORM_WINDOW is handled") @@ -120,7 +118,6 @@ class DesktopModeKeyGestureHandler( ) } } - return true } KeyGestureEvent.KEY_GESTURE_TYPE_MINIMIZE_FREEFORM_WINDOW -> { logV("Key gesture MINIMIZE_FREEFORM_WINDOW is handled") @@ -129,9 +126,7 @@ class DesktopModeKeyGestureHandler( desktopTasksController.get().minimizeTask(it, MinimizeReason.KEY_GESTURE) } } - return true } - else -> return false } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt index a8b0bafee724..3c44fe8061aa 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt @@ -69,6 +69,7 @@ fun calculateInitialBounds( taskInfo: RunningTaskInfo, scale: Float = DESKTOP_MODE_INITIAL_BOUNDS_SCALE, captionInsets: Int = 0, + requestedScreenOrientation: Int? = null, ): Rect { val screenBounds = Rect(0, 0, displayLayout.width(), displayLayout.height()) val appAspectRatio = calculateAspectRatio(taskInfo) @@ -85,12 +86,13 @@ fun calculateInitialBounds( } val topActivityInfo = taskInfo.topActivityInfo ?: return positionInScreen(idealSize, stableBounds) + val screenOrientation = requestedScreenOrientation ?: topActivityInfo.screenOrientation val initialSize: Size = when (taskInfo.configuration.orientation) { ORIENTATION_LANDSCAPE -> { if (taskInfo.canChangeAspectRatio) { - if (isFixedOrientationPortrait(topActivityInfo.screenOrientation)) { + if (isFixedOrientationPortrait(screenOrientation)) { // For portrait resizeable activities, respect apps fullscreen width but // apply ideal size height. Size( @@ -104,14 +106,20 @@ fun calculateInitialBounds( } else { // If activity is unresizeable, regardless of orientation, calculate maximum // size (within the ideal size) maintaining original aspect ratio. - maximizeSizeGivenAspectRatio(taskInfo, idealSize, appAspectRatio, captionInsets) + maximizeSizeGivenAspectRatio( + taskInfo, + idealSize, + appAspectRatio, + captionInsets, + screenOrientation, + ) } } ORIENTATION_PORTRAIT -> { val customPortraitWidthForLandscapeApp = screenBounds.width() - (DESKTOP_MODE_LANDSCAPE_APP_PADDING * 2) if (taskInfo.canChangeAspectRatio) { - if (isFixedOrientationLandscape(topActivityInfo.screenOrientation)) { + if (isFixedOrientationLandscape(screenOrientation)) { // For landscape resizeable activities, respect apps fullscreen height and // apply custom app width. Size( @@ -123,7 +131,7 @@ fun calculateInitialBounds( idealSize } } else { - if (isFixedOrientationLandscape(topActivityInfo.screenOrientation)) { + if (isFixedOrientationLandscape(screenOrientation)) { // For landscape unresizeable activities, apply custom app width to ideal // size and calculate maximum size with this area while maintaining original // aspect ratio. @@ -132,6 +140,7 @@ fun calculateInitialBounds( Size(customPortraitWidthForLandscapeApp, idealSize.height), appAspectRatio, captionInsets, + screenOrientation, ) } else { // For portrait unresizeable activities, calculate maximum size (within the @@ -141,6 +150,7 @@ fun calculateInitialBounds( idealSize, appAspectRatio, captionInsets, + screenOrientation, ) } } @@ -190,13 +200,16 @@ fun maximizeSizeGivenAspectRatio( targetArea: Size, aspectRatio: Float, captionInsets: Int = 0, + requestedScreenOrientation: Int? = null, ): Size { val targetHeight = targetArea.height - captionInsets val targetWidth = targetArea.width val finalHeight: Int val finalWidth: Int // Get orientation either through top activity or task's orientation - if (taskInfo.hasPortraitTopActivity()) { + val screenOrientation = + requestedScreenOrientation ?: taskInfo.topActivityInfo?.screenOrientation + if (taskInfo.hasPortraitTopActivity(screenOrientation)) { val tempWidth = ceil(targetHeight / aspectRatio).toInt() if (tempWidth <= targetWidth) { finalHeight = targetHeight @@ -354,9 +367,8 @@ fun centerInArea(desiredSize: Size, areaBounds: Rect, leftStart: Int, topStart: return Rect(newLeft, newTop, newRight, newBottom) } -private fun TaskInfo.hasPortraitTopActivity(): Boolean { - val topActivityScreenOrientation = - topActivityInfo?.screenOrientation ?: SCREEN_ORIENTATION_UNSPECIFIED +private fun TaskInfo.hasPortraitTopActivity(screenOrientation: Int?): Boolean { + val topActivityScreenOrientation = screenOrientation ?: SCREEN_ORIENTATION_UNSPECIFIED val appBounds = configuration.windowConfiguration.appBounds return when { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt index 0920f274f51d..093e8ef8bc0e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt @@ -287,8 +287,8 @@ class DesktopTasksController( DeskRecreationFactory { deskUserId, destinationDisplayId, deskId -> if (deskUserId != userId) { // TODO: b/400984250 - add multi-user support for multi-desk restoration. - logW("Tried to recreated desk of another user.") - deskId + logW("Tried to re-create desk of another user.") + null } else { desksOrganizer.createDesk(destinationDisplayId) } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandler.java index 95cc1e68ac11..f382632ff790 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandler.java @@ -46,6 +46,7 @@ import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.jank.Cuj; import com.android.internal.jank.InteractionJankMonitor; +import com.android.internal.util.LatencyTracker; import com.android.wm.shell.shared.annotations.ShellMainThread; import com.android.wm.shell.shared.desktopmode.DesktopModeTransitionSource; import com.android.wm.shell.transition.Transitions; @@ -68,6 +69,7 @@ public class ExitDesktopTaskTransitionHandler implements Transitions.TransitionH private final Context mContext; private final Transitions mTransitions; private final InteractionJankMonitor mInteractionJankMonitor; + private final LatencyTracker mLatencyTracker; @ShellMainThread private final Handler mHandler; private final List<IBinder> mPendingTransitionTokens = new ArrayList<>(); @@ -95,6 +97,7 @@ public class ExitDesktopTaskTransitionHandler implements Transitions.TransitionH mTransactionSupplier = supplier; mContext = context; mInteractionJankMonitor = interactionJankMonitor; + mLatencyTracker = LatencyTracker.getInstance(mContext); mHandler = handler; } @@ -109,6 +112,7 @@ public class ExitDesktopTaskTransitionHandler implements Transitions.TransitionH public IBinder startTransition(@NonNull DesktopModeTransitionSource transitionSource, @NonNull WindowContainerTransaction wct, Point position, Function0<Unit> onAnimationEndCallback) { + mLatencyTracker.onActionStart(LatencyTracker.ACTION_DESKTOP_MODE_EXIT_MODE); mPosition = position; mOnAnimationFinishedCallback = onAnimationEndCallback; final IBinder token = mTransitions.startTransition(getExitTransitionType(transitionSource), @@ -141,6 +145,11 @@ public class ExitDesktopTaskTransitionHandler implements Transitions.TransitionH mPendingTransitionTokens.remove(transition); + + if (transitionHandled) { + mLatencyTracker.onActionEnd(LatencyTracker.ACTION_DESKTOP_MODE_EXIT_MODE); + } + return transitionHandled; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/VisualIndicatorViewContainer.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/VisualIndicatorViewContainer.kt index 23562388b3e5..5e4122ba14ec 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/VisualIndicatorViewContainer.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/VisualIndicatorViewContainer.kt @@ -18,6 +18,7 @@ package com.android.wm.shell.desktopmode import android.animation.Animator import android.animation.AnimatorListenerAdapter +import android.animation.AnimatorSet import android.animation.RectEvaluator import android.animation.ValueAnimator import android.app.ActivityManager @@ -32,6 +33,8 @@ import android.view.View import android.view.WindowManager import android.view.WindowlessWindowManager import android.view.animation.DecelerateInterpolator +import android.widget.FrameLayout +import androidx.core.animation.doOnEnd import com.android.internal.annotations.VisibleForTesting import com.android.window.flags.Flags import com.android.wm.shell.R @@ -42,6 +45,7 @@ import com.android.wm.shell.common.SyncTransactionQueue import com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType import com.android.wm.shell.shared.annotations.ShellDesktopThread import com.android.wm.shell.shared.annotations.ShellMainThread +import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper import com.android.wm.shell.shared.bubbles.BubbleDropTargetBoundsProvider import com.android.wm.shell.windowdecor.WindowDecoration.SurfaceControlViewHostFactory import com.android.wm.shell.windowdecor.tiling.SnapEventHandler @@ -64,6 +68,8 @@ constructor( private val snapEventHandler: SnapEventHandler, ) { @VisibleForTesting var indicatorView: View? = null + // Optional extra indicator showing the outline of the bubble bar + private var barIndicatorView: View? = null private var indicatorViewHost: SurfaceControlViewHost? = null // Below variables and the SyncTransactionQueue are the only variables that should // be accessed from shell main thread. Everything else should be used exclusively @@ -93,7 +99,12 @@ constructor( screenWidth = metrics.widthPixels screenHeight = metrics.heightPixels } - indicatorView = View(context) + indicatorView = + if (BubbleAnythingFlagHelper.enableBubbleToFullscreen()) { + FrameLayout(context) + } else { + View(context) + } val leash = indicatorBuilder .setName("Desktop Mode Visual Indicator") @@ -183,23 +194,50 @@ constructor( ) } else { val animStartType = IndicatorType.valueOf(currentType.name) - val animator = - indicatorView?.let { - VisualIndicatorAnimator.animateIndicatorType( - it, - layout, - animStartType, - newType, - bubbleBoundsProvider, - taskInfo.displayId, - snapEventHandler, - ) - } ?: return@execute + val indicator = indicatorView ?: return@execute + var animator: Animator = + VisualIndicatorAnimator.animateIndicatorType( + indicator, + layout, + animStartType, + newType, + bubbleBoundsProvider, + taskInfo.displayId, + snapEventHandler, + ) + if (BubbleAnythingFlagHelper.enableBubbleToFullscreen()) { + if (currentType.isBubbleType() || newType.isBubbleType()) { + animator = addBarIndicatorAnimation(animator, currentType, newType) + } + } animator.start() } } } + private fun addBarIndicatorAnimation( + visualIndicatorAnimator: Animator, + currentType: IndicatorType, + newType: IndicatorType, + ): Animator { + if (newType.isBubbleType()) { + getOrCreateBubbleBarIndicator(newType)?.let { bar -> + return AnimatorSet().apply { + playTogether(visualIndicatorAnimator, fadeBarIndicatorIn(bar)) + } + } + } + if (currentType.isBubbleType()) { + barIndicatorView?.let { bar -> + barIndicatorView = null + return AnimatorSet().apply { + playTogether(visualIndicatorAnimator, fadeBarIndicatorOut(bar)) + } + } + } + return visualIndicatorAnimator + } + /** * Fade indicator in as provided type. * @@ -223,17 +261,20 @@ constructor( snapEventHandler: SnapEventHandler, ) { desktopExecutor.assertCurrentThread() - indicatorView?.let { - it.setBackgroundResource(R.drawable.desktop_windowing_transition_background) - val animator = + indicatorView?.let { indicator -> + indicator.setBackgroundResource(R.drawable.desktop_windowing_transition_background) + var animator: Animator = VisualIndicatorAnimator.fadeBoundsIn( - it, + indicator, type, layout, bubbleBoundsProvider, displayId, snapEventHandler, ) + if (BubbleAnythingFlagHelper.enableBubbleToFullscreen()) { + animator = addBarIndicatorAnimation(animator, IndicatorType.NO_INDICATOR, type) + } animator.start() } } @@ -259,7 +300,7 @@ constructor( desktopExecutor.execute { indicatorView?.let { val animStartType = IndicatorType.valueOf(currentType.name) - val animator = + var animator: Animator = VisualIndicatorAnimator.fadeBoundsOut( it, animStartType, @@ -268,6 +309,10 @@ constructor( displayId, snapEventHandler, ) + if (BubbleAnythingFlagHelper.enableBubbleToFullscreen()) { + animator = + addBarIndicatorAnimation(animator, currentType, IndicatorType.NO_INDICATOR) + } animator.addListener( object : AnimatorListenerAdapter() { override fun onAnimationEnd(animation: Animator) { @@ -302,6 +347,38 @@ constructor( isReleased = true } + private fun getOrCreateBubbleBarIndicator(type: IndicatorType): View? { + val container = indicatorView as? FrameLayout ?: return null + val onLeft = type == IndicatorType.TO_BUBBLE_LEFT_INDICATOR + val bounds = bubbleBoundsProvider?.getBarDropTargetBounds(onLeft) ?: return null + val lp = FrameLayout.LayoutParams(bounds.width(), bounds.height()) + lp.leftMargin = bounds.left + lp.topMargin = bounds.top + if (barIndicatorView == null) { + val indicator = View(container.context) + indicator.setBackgroundResource(R.drawable.desktop_windowing_transition_background) + container.addView(indicator, lp) + barIndicatorView = indicator + } else { + barIndicatorView?.layoutParams = lp + } + return barIndicatorView + } + + private fun fadeBarIndicatorIn(barIndicator: View): Animator { + // Use layout bounds as the end bounds in case the view has not been laid out yet + val lp = barIndicator.layoutParams + val endBounds = Rect(0, 0, lp.width, lp.height) + return VisualIndicatorAnimator.fadeBoundsIn(barIndicator, endBounds) + } + + private fun fadeBarIndicatorOut(barIndicator: View): Animator { + val startBounds = Rect(0, 0, barIndicator.width, barIndicator.height) + val barAnimator = VisualIndicatorAnimator.fadeBoundsOut(barIndicator, startBounds) + barAnimator.doOnEnd { (indicatorView as? FrameLayout)?.removeView(barIndicator) } + return barAnimator + } + /** * Animator for Desktop Mode transitions which supports bounds and alpha animation. Functions * should only be called from the desktop executor. @@ -383,9 +460,13 @@ constructor( displayId, snapEventHandler, ) + return fadeBoundsIn(view, endBounds) + } + + @ShellDesktopThread + fun fadeBoundsIn(view: View, endBounds: Rect): VisualIndicatorAnimator { val startBounds = getMinBounds(endBounds) view.background.bounds = startBounds - val animator = VisualIndicatorAnimator(view, startBounds, endBounds) animator.interpolator = DecelerateInterpolator() setupIndicatorAnimation(animator, AlphaAnimType.ALPHA_FADE_IN_ANIM) @@ -409,6 +490,11 @@ constructor( displayId, snapEventHandler, ) + return fadeBoundsOut(view, startBounds) + } + + @ShellDesktopThread + fun fadeBoundsOut(view: View, startBounds: Rect): VisualIndicatorAnimator { val endBounds = getMinBounds(startBounds) view.background.bounds = startBounds val animator = VisualIndicatorAnimator(view, startBounds, endBounds) @@ -571,4 +657,9 @@ constructor( } } } + + private fun IndicatorType.isBubbleType(): Boolean { + return this == IndicatorType.TO_BUBBLE_LEFT_INDICATOR || + this == IndicatorType.TO_BUBBLE_RIGHT_INDICATOR + } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializer.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializer.kt index 8191181cac11..a2dd5dbc8709 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializer.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializer.kt @@ -21,7 +21,7 @@ import kotlinx.coroutines.flow.StateFlow /** Interface for initializing the [DesktopUserRepositories]. */ interface DesktopRepositoryInitializer { - /** A factory used to recreate a desk from persistence. */ + /** A factory used to re-create a desk from persistence. */ var deskRecreationFactory: DeskRecreationFactory /** A flow that emits true when the repository has been initialized. */ @@ -30,9 +30,11 @@ interface DesktopRepositoryInitializer { /** Initialize the user repositories from a persistent data store. */ fun initialize(userRepositories: DesktopUserRepositories) - /** A factory for recreating desks. */ + /** A factory for re-creating desks. */ fun interface DeskRecreationFactory { - /** Recreates a restored desk and returns the new desk id. */ - suspend fun recreateDesk(userId: Int, destinationDisplayId: Int, deskId: Int): Int + /** + * Re-creates a restored desk and returns the new desk id, or null if re-creation failed. + */ + suspend fun recreateDesk(userId: Int, destinationDisplayId: Int, deskId: Int): Int? } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializerImpl.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializerImpl.kt index 5ed0b1d1616f..3ee48072ee86 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializerImpl.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializerImpl.kt @@ -69,7 +69,7 @@ class DesktopRepositoryInitializerImpl( desksToRestore.map { it.desktopId }, userId, ) - desksToRestore.forEach { persistentDesktop -> + for (persistentDesktop in desksToRestore) { val maxTasks = getTaskLimit(persistentDesktop) val displayId = persistentDesktop.displayId val deskId = persistentDesktop.desktopId @@ -81,17 +81,29 @@ class DesktopRepositoryInitializerImpl( destinationDisplayId = newDisplayId, deskId = deskId, ) - logV( - "Recreated desk=%d in display=%d using new deskId=%d and displayId=%d", - deskId, - displayId, - newDeskId, - newDisplayId, - ) - if (newDeskId != deskId || newDisplayId != displayId) { + if (newDeskId != null) { + logV( + "Re-created desk=%d in display=%d using new" + + " deskId=%d and displayId=%d", + deskId, + displayId, + newDeskId, + newDisplayId, + ) + } + if (newDeskId == null || newDeskId != deskId || newDisplayId != displayId) { logV("Removing obsolete desk from persistence under deskId=%d", deskId) persistentRepository.removeDesktop(userId, deskId) } + if (newDeskId == null) { + logW( + "Could not re-create desk=%d from display=%d in displayId=%d", + deskId, + displayId, + newDisplayId, + ) + continue + } // TODO: b/393961770 - [DesktopRepository] doesn't save desks to the // persistent repository until a task is added to them. Update it so that @@ -177,6 +189,10 @@ class DesktopRepositoryInitializerImpl( ProtoLog.v(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments) } + private fun logW(msg: String, vararg arguments: Any?) { + ProtoLog.w(WM_SHELL_DESKTOP_MODE, "%s: $msg", TAG, *arguments) + } + /** A default implementation of [DeskRecreationFactory] that reuses the desk id. */ private class DefaultDeskRecreationFactory : DeskRecreationFactory { override suspend fun recreateDesk( diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java index 4e341ac9b7eb..0e974ef9083b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip2/phone/PipController.java @@ -31,6 +31,8 @@ import android.content.pm.ActivityInfo; import android.content.res.Configuration; import android.graphics.Rect; import android.os.Bundle; +import android.os.Debug; +import android.util.Log; import android.view.SurfaceControl; import android.window.DesktopExperienceFlags; import android.window.DisplayAreaInfo; @@ -369,7 +371,13 @@ public class PipController implements ConfigurationChangeListener, mPipBoundsAlgorithm.applySnapFraction(toBounds, snapFraction); mPipBoundsState.setBounds(toBounds); } - t.setBounds(mPipTransitionState.getPipTaskToken(), mPipBoundsState.getBounds()); + if (mPipTransitionState.getPipTaskToken() == null) { + Log.wtf(TAG, "PipController.onDisplayChange no PiP task token" + + " state=" + mPipTransitionState.getState() + + " callers=\n" + Debug.getCallers(4, " ")); + } else { + t.setBounds(mPipTransitionState.getPipTaskToken(), mPipBoundsState.getBounds()); + } // Update the size spec in PipBoundsState afterwards. mPipBoundsState.updateMinMaxSize(mPipBoundsState.getAspectRatio()); } 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 bb5b5cec1b4a..382fa9640ff9 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 @@ -536,17 +536,20 @@ public class RecentTasksController implements TaskStackListenerCallback, } /** - * Represents a desk whose ID is `mDeskId` and contains the tasks in `mDeskTasks`. Some of these - * tasks are minimized and their IDs are contained in the `mMinimizedDeskTasks` set. + * Represents a desk whose ID is `mDeskId` inside the display with `mDisplayId` and contains + * the tasks in `mDeskTasks`. Some of these tasks are minimized and their IDs are contained + * in the `mMinimizedDeskTasks` set. */ private static class Desk { final int mDeskId; + final int mDisplayId; boolean mHasVisibleTasks = false; final ArrayList<TaskInfo> mDeskTasks = new ArrayList<>(); final Set<Integer> mMinimizedDeskTasks = new HashSet<>(); - Desk(int deskId) { + Desk(int deskId, int displayId) { mDeskId = deskId; + mDisplayId = displayId; } void addTask(TaskInfo taskInfo, boolean isMinimized, boolean isVisible) { @@ -562,7 +565,8 @@ public class RecentTasksController implements TaskStackListenerCallback, } GroupedTaskInfo createDeskTaskInfo() { - return GroupedTaskInfo.forDeskTasks(mDeskId, mDeskTasks, mMinimizedDeskTasks); + return GroupedTaskInfo.forDeskTasks(mDeskId, mDisplayId, mDeskTasks, + mMinimizedDeskTasks); } } @@ -601,7 +605,8 @@ public class RecentTasksController implements TaskStackListenerCallback, private Desk getOrCreateDesk(int deskId) { var desk = mTmpDesks.get(deskId); if (desk == null) { - desk = new Desk(deskId); + desk = new Desk(deskId, + mDesktopUserRepositories.get().getCurrent().getDisplayForDesk(deskId)); mTmpDesks.put(deskId, desk); } return desk; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java index 3e03e001c49b..8e10f15a36cc 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java @@ -1135,6 +1135,7 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler, if (openingLeafCount > 0) { appearedTargets = new RemoteAnimationTarget[openingLeafCount]; } + boolean onlyOpeningPausedTasks = true; int nextTargetIdx = 0; for (int i = 0; i < openingTasks.size(); ++i) { final TransitionInfo.Change change = openingTasks.get(i); @@ -1188,6 +1189,7 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler, " opening new leaf taskId=%d wasClosing=%b", target.taskId, wasClosing); mOpeningTasks.add(new TaskState(change, target.leash)); + onlyOpeningPausedTasks = false; } else { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, " opening new taskId=%d", change.getTaskInfo().taskId); @@ -1196,10 +1198,17 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler, // is only animating the leafs. startT.show(change.getLeash()); mOpeningTasks.add(new TaskState(change, null)); + onlyOpeningPausedTasks = false; } } didMergeThings = true; - mState = STATE_NEW_TASK; + if (!onlyOpeningPausedTasks) { + // If we are only opening paused leaf tasks, then we aren't actually quick + // switching or launching a new task from overview, and if Launcher requests to + // finish(toHome=false) as a response to the pausing tasks being opened again, + // we should allow that to be considered returningToApp + mState = STATE_NEW_TASK; + } } if (mPausingTasks.isEmpty()) { // The pausing tasks may be removed by the incoming closing tasks. @@ -1368,8 +1377,9 @@ public class RecentsTransitionHandler implements Transitions.TransitionHandler, ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENTS_TRANSITION, "[%d] RecentsController.finishInner: toHome=%b userLeave=%b " - + "willFinishToHome=%b state=%d reason=%s", - mInstanceId, toHome, sendUserLeaveHint, mWillFinishToHome, mState, reason); + + "willFinishToHome=%b state=%d hasPausingTasks=%b reason=%s", + mInstanceId, toHome, sendUserLeaveHint, mWillFinishToHome, mState, + mPausingTasks != null, reason); final SurfaceControl.Transaction t = mFinishTransaction; final WindowContainerTransaction wct = new WindowContainerTransaction(); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java index 23dfb41d52c1..cca982142a3a 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java @@ -153,7 +153,6 @@ public class HomeTransitionObserver implements TransitionObserver, return; } mPendingStartDragTransition = null; - if (aborted) return; if (mPendingHomeVisibilityUpdate != null) { notifyHomeVisibilityChanged(mPendingHomeVisibilityUpdate); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java index 003ef1d453fc..4f49ebcd2e83 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java @@ -602,6 +602,11 @@ public class Transitions implements RemoteCallable<Transitions>, // Just in case there is a race with another animation (eg. recents finish()). // Changes are visible->visible so it's a problem if it isn't visible. t.show(leash); + // If there is a transient launch followed by a launch of one of the pausing tasks, + // we may end up with TRANSIT_TO_BACK followed by a CHANGE (w/ flag MOVE_TO_TOP), + // but since we are hiding the leash in the finish transaction above, we should also + // update the finish transaction here to reflect the change in visibility + finishT.show(leash); } } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CarWindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CarWindowDecoration.java index 3182745d813e..f6acca95916f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CarWindowDecoration.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CarWindowDecoration.java @@ -27,6 +27,7 @@ import android.view.InsetsState; import android.view.SurfaceControl; import android.view.View; import android.view.WindowInsets; +import android.window.DesktopModeFlags; import android.window.WindowContainerTransaction; import androidx.annotation.NonNull; @@ -88,6 +89,9 @@ public class CarWindowDecoration extends WindowDecoration<WindowDecorLinearLayou updateRelayoutParams(mRelayoutParams, taskInfo, isCaptionVisible); relayout(mRelayoutParams, startT, finishT, wct, mRootView, mResult); + if (DesktopModeFlags.ENABLE_DESKTOP_APP_HANDLE_ANIMATION.isTrue()) { + setCaptionVisibility(isCaptionVisible); + } // After this line, mTaskInfo is up-to-date and should be used instead of taskInfo mBgExecutor.execute(() -> mTaskOrganizer.applyTransaction(wct)); @@ -102,6 +106,15 @@ public class CarWindowDecoration extends WindowDecoration<WindowDecorLinearLayou } } + private void setCaptionVisibility(boolean visible) { + if (mRootView == null) { + return; + } + final int v = visible ? View.VISIBLE : View.GONE; + final View captionView = mRootView.findViewById(getCaptionViewId()); + captionView.setVisibility(v); + } + @Override @NonNull Rect calculateValidDragArea() { 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 e8019e47e374..ae103895d56b 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 @@ -863,7 +863,8 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin if (!isAppHandle(mWindowDecorViewHolder)) return; asAppHandle(mWindowDecorViewHolder).bindData(new AppHandleViewHolder.HandleData( mTaskInfo, determineHandlePosition(), mResult.mCaptionWidth, - mResult.mCaptionHeight, isCaptionVisible() + mResult.mCaptionHeight, /* showInputLayer= */ isCaptionVisible(), + /* isCaptionVisible= */ isCaptionVisible() )); } @@ -876,7 +877,8 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin inFullImmersive, hasGlobalFocus, /* maximizeHoverEnabled= */ canOpenMaximizeMenu( - /* animatingTaskResizeOrReposition= */ false) + /* animatingTaskResizeOrReposition= */ false), + isCaptionVisible() )); } @@ -1866,7 +1868,8 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin DesktopModeUtils.isTaskMaximized(mTaskInfo, mDisplayController), inFullImmersive, isFocused(), - /* maximizeHoverEnabled= */ canOpenMaximizeMenu(animatingTaskResizeOrReposition))); + /* maximizeHoverEnabled= */ canOpenMaximizeMenu(animatingTaskResizeOrReposition), + isCaptionVisible())); } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt index 9cc64ac9c276..5f13ba907831 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenu.kt @@ -37,7 +37,6 @@ import android.view.WindowInsets.Type.systemBars import android.view.WindowManager import android.widget.ImageButton import android.widget.ImageView -import android.widget.LinearLayout import android.widget.Space import android.window.DesktopModeFlags import android.window.SurfaceSyncGroup @@ -501,6 +500,12 @@ class HandleMenu( t = iconButtondrawableBaseInset, b = iconButtondrawableBaseInset, l = 0, r = iconButtondrawableShiftInset ) + private val iconButtonDrawableInsetStart + get() = + if (context.isRtl) iconButtonDrawableInsetsRight else iconButtonDrawableInsetsLeft + private val iconButtonDrawableInsetEnd + get() = + if (context.isRtl) iconButtonDrawableInsetsLeft else iconButtonDrawableInsetsRight // App Info Pill. private val appInfoPill = rootView.requireViewById<View>(R.id.app_info_pill) @@ -549,9 +554,8 @@ class HandleMenu( private val openInAppOrBrowserPill = rootView.requireViewById<View>( R.id.open_in_app_or_browser_pill ) - private val openInAppOrBrowserBtn = openInAppOrBrowserPill.requireViewById<View>( - R.id.open_in_app_or_browser_button - ) + private val openInAppOrBrowserBtn = openInAppOrBrowserPill + .requireViewById<HandleMenuActionButton>(R.id.open_in_app_or_browser_button) private val openByDefaultBtn = openInAppOrBrowserPill.requireViewById<ImageButton>( R.id.open_by_default_button ) @@ -758,20 +762,16 @@ class HandleMenu( floatingBtn.isEnabled = !taskInfo.isPinned floatingBtn.imageTintList = style.windowingButtonColor desktopBtn.isGone = !shouldShowDesktopModeButton + desktopBtnSpace.isGone = !shouldShowDesktopModeButton desktopBtn.isSelected = taskInfo.isFreeform desktopBtn.isEnabled = !taskInfo.isFreeform desktopBtn.imageTintList = style.windowingButtonColor - val startInsets = if (context.isRtl) iconButtonDrawableInsetsRight - else iconButtonDrawableInsetsLeft - val endInsets = if (context.isRtl) iconButtonDrawableInsetsLeft - else iconButtonDrawableInsetsRight - fullscreenBtn.apply { background = createBackgroundDrawable( color = style.textColor, cornerRadius = iconButtonRippleRadius, - drawableInsets = startInsets + drawableInsets = iconButtonDrawableInsetStart ) } @@ -795,7 +795,7 @@ class HandleMenu( background = createBackgroundDrawable( color = style.textColor, cornerRadius = iconButtonRippleRadius, - drawableInsets = endInsets + drawableInsets = iconButtonDrawableInsetEnd ) } } @@ -808,20 +808,15 @@ class HandleMenu( newWindowBtn to shouldShowNewWindowButton, manageWindowBtn to shouldShowManageWindowsButton, changeAspectRatioBtn to shouldShowChangeAspectRatioButton, - ).forEach { - val button = it.first - val shouldShow = it.second - - val buttonRoot = button.requireViewById<LinearLayout>(R.id.action_button) - val label = buttonRoot.requireViewById<MarqueedTextView>(R.id.label) - val image = buttonRoot.requireViewById<ImageView>(R.id.image) - - button.isGone = !shouldShow - label.apply { - setTextColor(style.textColor) - startMarquee() + ).forEach { (button, shouldShow) -> + button.apply { + isGone = !shouldShow + textView.apply { + setTextColor(style.textColor) + startMarquee() + } + iconView.imageTintList = ColorStateList.valueOf(style.textColor) } - image.imageTintList = ColorStateList.valueOf(style.textColor) } } @@ -837,20 +832,24 @@ class HandleMenu( getString(R.string.open_in_browser_text) } - val buttonRoot = openInAppOrBrowserBtn.requireViewById<LinearLayout>(R.id.action_button) - val label = openInAppOrBrowserBtn.requireViewById<MarqueedTextView>(R.id.label) - val image = openInAppOrBrowserBtn.requireViewById<ImageView>(R.id.image) - openInAppOrBrowserBtn.contentDescription = btnText - buttonRoot.contentDescription = btnText - label.apply { - text = btnText - setTextColor(style.textColor) - startMarquee() + openInAppOrBrowserBtn.apply { + contentDescription = btnText + textView.apply { + text = btnText + setTextColor(style.textColor) + startMarquee() + } + iconView.imageTintList = ColorStateList.valueOf(style.textColor) } - image.imageTintList = ColorStateList.valueOf(style.textColor) - openByDefaultBtn.isGone = isBrowserApp - openByDefaultBtn.imageTintList = ColorStateList.valueOf(style.textColor) + openByDefaultBtn.apply { + isGone = isBrowserApp + imageTintList = ColorStateList.valueOf(style.textColor) + background = createBackgroundDrawable( + color = style.textColor, + cornerRadius = iconButtonRippleRadius, + drawableInsets = iconButtonDrawableInsetEnd) + } } private fun getString(@StringRes resId: Int): String = context.resources.getString(resId) diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenuActionButton.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenuActionButton.kt index a723a7a4ac20..7aba54eef899 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenuActionButton.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/HandleMenuActionButton.kt @@ -39,20 +39,18 @@ class HandleMenuActionButton @JvmOverloads constructor( defStyleAttr: Int = 0 ) : LinearLayout(context, attrs, defStyleAttr) { - private val rootElement: LinearLayout - private val iconView: ImageView - private val textView: MarqueedTextView + val iconView: ImageView + val textView: MarqueedTextView init { - val view = LayoutInflater.from(context).inflate( + LayoutInflater.from(context).inflate( R.layout.desktop_mode_window_decor_handle_menu_action_button, this, true) - rootElement = findViewById(R.id.action_button) iconView = findViewById(R.id.image) textView = findViewById(R.id.label) context.withStyledAttributes(attrs, R.styleable.HandleMenuActionButton) { + contentDescription = getString(R.styleable.HandleMenuActionButton_android_text) textView.text = getString(R.styleable.HandleMenuActionButton_android_text) - rootElement.contentDescription = getString(R.styleable.HandleMenuActionButton_android_text) textView.setTextColor(getColor(R.styleable.HandleMenuActionButton_android_textColor, 0)) iconView.setImageResource(getResourceId( R.styleable.HandleMenuActionButton_android_src, 0)) @@ -62,15 +60,6 @@ class HandleMenuActionButton @JvmOverloads constructor( } /** - * Sets a listener to be invoked when this view is clicked. - * - * @param l the [OnClickListener] that receives click events. - */ - override fun setOnClickListener(l: OnClickListener?) { - rootElement.setOnClickListener(l) - } - - /** * Sets the text color for the text inside the button. * * @param color the color to set for the text, as a color integer. diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositioner.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositioner.kt index eb324f74ca82..238242792782 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositioner.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositioner.kt @@ -278,13 +278,16 @@ class MultiDisplayVeiledResizeTaskPositioner( currentDisplayLayout, ) ) - - multiDisplayDragMoveIndicatorController.onDragEnd( - desktopWindowDecoration.mTaskInfo.taskId, - transactionSupplier, - ) } + // Call the MultiDisplayDragMoveIndicatorController to clear any active indicator + // surfaces. This is necessary even if the drag ended on the same display, as surfaces + // may have been created for other displays during the drag. + multiDisplayDragMoveIndicatorController.onDragEnd( + desktopWindowDecoration.mTaskInfo.taskId, + transactionSupplier, + ) + interactionJankMonitor.end(Cuj.CUJ_DESKTOP_MODE_DRAG_WINDOW) } 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 6fd963f4203d..6a9b366dfb97 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 @@ -48,6 +48,7 @@ import android.view.View; import android.view.WindowManager; import android.view.WindowlessWindowManager; import android.window.DesktopExperienceFlags; +import android.window.DesktopModeFlags; import android.window.SurfaceSyncGroup; import android.window.TaskConstants; import android.window.WindowContainerToken; @@ -652,7 +653,9 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer> */ private void updateCaptionVisibility(View rootView, @NonNull RelayoutParams params) { mIsCaptionVisible = params.mIsCaptionVisible; - setCaptionVisibility(rootView, mIsCaptionVisible); + if (!DesktopModeFlags.ENABLE_DESKTOP_APP_HANDLE_ANIMATION.isTrue()) { + setCaptionVisibility(rootView, mIsCaptionVisible); + } } void setTaskDragResizer(TaskDragResizer taskDragResizer) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleAnimator.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleAnimator.kt new file mode 100644 index 000000000000..f0a85306d177 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleAnimator.kt @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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.windowdecor + +import android.animation.ObjectAnimator +import android.view.View +import android.view.View.Visibility +import android.view.animation.PathInterpolator +import android.widget.ImageButton +import androidx.core.animation.doOnEnd +import com.android.wm.shell.shared.animation.Interpolators + +/** + * Animates the Desktop View's app handle. + */ +class AppHandleAnimator( + private val appHandleView: View, + private val captionHandle: ImageButton, +) { + companion object { + // Constants for animating the whole caption + private const val APP_HANDLE_ALPHA_FADE_IN_ANIMATION_DURATION_MS: Long = 275L + private const val APP_HANDLE_ALPHA_FADE_OUT_ANIMATION_DURATION_MS: Long = 340 + private val APP_HANDLE_ANIMATION_INTERPOLATOR = PathInterpolator( + 0.4f, + 0f, + 0.2f, + 1f + ) + + // Constants for animating the caption's handle + private const val HANDLE_ANIMATION_DURATION: Long = 100 + private val HANDLE_ANIMATION_INTERPOLATOR = Interpolators.FAST_OUT_SLOW_IN + } + + private var animator: ObjectAnimator? = null + + /** Animates the given caption view to the given visibility after a visibility change. */ + fun animateVisibilityChange(@Visibility visible: Int) { + when (visible) { + View.VISIBLE -> animateShowAppHandle() + else -> animateHideAppHandle() + } + } + + /** Animate appearance/disappearance of caption's handle. */ + fun animateCaptionHandleAlpha(startValue: Float, endValue: Float) { + cancel() + animator = ObjectAnimator.ofFloat(captionHandle, View.ALPHA, startValue, endValue).apply { + duration = HANDLE_ANIMATION_DURATION + interpolator = HANDLE_ANIMATION_INTERPOLATOR + start() + } + } + + private fun animateShowAppHandle() { + cancel() + appHandleView.alpha = 0f + appHandleView.visibility = View.VISIBLE + animator = ObjectAnimator.ofFloat(appHandleView, View.ALPHA, 1f).apply { + duration = APP_HANDLE_ALPHA_FADE_IN_ANIMATION_DURATION_MS + interpolator = APP_HANDLE_ANIMATION_INTERPOLATOR + start() + } + } + + private fun animateHideAppHandle() { + cancel() + animator = ObjectAnimator.ofFloat(appHandleView, View.ALPHA, 0f).apply { + duration = APP_HANDLE_ALPHA_FADE_OUT_ANIMATION_DURATION_MS + interpolator = APP_HANDLE_ANIMATION_INTERPOLATOR + doOnEnd { + appHandleView.visibility = View.GONE + } + start() + } + } + + /** + * Cancels any active animations. + */ + fun cancel() { + animator?.removeAllListeners() + animator?.cancel() + animator = null + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt index 0985587a330e..9d16be59ba34 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolder.kt @@ -15,7 +15,6 @@ */ package com.android.wm.shell.windowdecor.viewholder -import android.animation.ObjectAnimator import android.app.ActivityManager.RunningTaskInfo import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM import android.content.res.ColorStateList @@ -40,8 +39,8 @@ import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.Accessibilit import com.android.internal.policy.SystemBarUtils import com.android.window.flags.Flags import com.android.wm.shell.R -import com.android.wm.shell.shared.animation.Interpolators import com.android.wm.shell.shared.bubbles.BubbleAnythingFlagHelper +import com.android.wm.shell.windowdecor.AppHandleAnimator import com.android.wm.shell.windowdecor.WindowManagerWrapper import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalSystemViewContainer @@ -57,22 +56,20 @@ class AppHandleViewHolder( private val handler: Handler ) : WindowDecorationViewHolder<AppHandleViewHolder.HandleData>(rootView) { - companion object { - private const val CAPTION_HANDLE_ANIMATION_DURATION: Long = 100 - } - data class HandleData( val taskInfo: RunningTaskInfo, val position: Point, val width: Int, val height: Int, - val showInputLayer: Boolean + val showInputLayer: Boolean, + val isCaptionVisible: Boolean, ) : Data() private lateinit var taskInfo: RunningTaskInfo private val captionView: View = rootView.requireViewById(R.id.desktop_mode_caption) private val captionHandle: ImageButton = rootView.requireViewById(R.id.caption_handle) private val inputManager = context.getSystemService(InputManager::class.java) + private val animator: AppHandleAnimator = AppHandleAnimator(rootView, captionHandle) private var statusBarInputLayerExists = false // An invisible View that takes up the same coordinates as captionHandle but is layered @@ -101,7 +98,14 @@ class AppHandleViewHolder( } override fun bindData(data: HandleData) { - bindData(data.taskInfo, data.position, data.width, data.height, data.showInputLayer) + bindData( + data.taskInfo, + data.position, + data.width, + data.height, + data.showInputLayer, + data.isCaptionVisible + ) } private fun bindData( @@ -109,8 +113,10 @@ class AppHandleViewHolder( position: Point, width: Int, height: Int, - showInputLayer: Boolean + showInputLayer: Boolean, + isCaptionVisible: Boolean ) { + setVisibility(isCaptionVisible) captionHandle.imageTintList = ColorStateList.valueOf(getCaptionHandleBarColor(taskInfo)) this.taskInfo = taskInfo // If handle is not in status bar region(i.e., bottom stage in vertical split), @@ -131,11 +137,11 @@ class AppHandleViewHolder( } override fun onHandleMenuOpened() { - animateCaptionHandleAlpha(startValue = 1f, endValue = 0f) + animator.animateCaptionHandleAlpha(startValue = 1f, endValue = 0f) } override fun onHandleMenuClosed() { - animateCaptionHandleAlpha(startValue = 0f, endValue = 1f) + animator.animateCaptionHandleAlpha(startValue = 0f, endValue = 1f) } private fun createStatusBarInputLayer(handlePosition: Point, @@ -239,6 +245,17 @@ class AppHandleViewHolder( } } + private fun setVisibility(visible: Boolean) { + val v = if (visible) View.VISIBLE else View.GONE + if ( + captionView.visibility == v || + !DesktopModeFlags.ENABLE_DESKTOP_APP_HANDLE_ANIMATION.isTrue() + ) { + return + } + animator.animateVisibilityChange(v) + } + private fun getCaptionHandleBarColor(taskInfo: RunningTaskInfo): Int { return if (shouldUseLightCaptionColors(taskInfo)) { context.getColor(R.color.desktop_mode_caption_handle_bar_light) @@ -264,18 +281,10 @@ class AppHandleViewHolder( } ?: false } - /** Animate appearance/disappearance of caption handle as the handle menu is animated. */ - private fun animateCaptionHandleAlpha(startValue: Float, endValue: Float) { - val animator = - ObjectAnimator.ofFloat(captionHandle, View.ALPHA, startValue, endValue).apply { - duration = CAPTION_HANDLE_ANIMATION_DURATION - interpolator = Interpolators.FAST_OUT_SLOW_IN - } - animator.start() + override fun close() { + animator.cancel() } - override fun close() {} - /** Factory class for creating [AppHandleViewHolder] objects. */ class Factory { /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt index 30712b55bdfa..0e2698d0b6fa 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/viewholder/AppHeaderViewHolder.kt @@ -83,6 +83,7 @@ class AppHeaderViewHolder( val inFullImmersiveState: Boolean, val hasGlobalFocus: Boolean, val enableMaximizeLongClick: Boolean, + val isCaptionVisible: Boolean, ) : Data() private val decorThemeUtil = DecorThemeUtil(context) @@ -264,7 +265,8 @@ class AppHeaderViewHolder( data.isTaskMaximized, data.inFullImmersiveState, data.hasGlobalFocus, - data.enableMaximizeLongClick + data.enableMaximizeLongClick, + data.isCaptionVisible, ) } @@ -306,6 +308,7 @@ class AppHeaderViewHolder( inFullImmersiveState: Boolean, hasGlobalFocus: Boolean, enableMaximizeLongClick: Boolean, + isCaptionVisible: Boolean, ) { if (DesktopModeFlags.ENABLE_THEMED_APP_HEADERS.isTrue()) { bindDataWithThemedHeaders( @@ -314,13 +317,21 @@ class AppHeaderViewHolder( inFullImmersiveState, hasGlobalFocus, enableMaximizeLongClick, + isCaptionVisible, ) } else { - bindDataLegacy(taskInfo, hasGlobalFocus) + bindDataLegacy(taskInfo, hasGlobalFocus, isCaptionVisible) } } - private fun bindDataLegacy(taskInfo: RunningTaskInfo, hasGlobalFocus: Boolean) { + private fun bindDataLegacy( + taskInfo: RunningTaskInfo, + hasGlobalFocus: Boolean, + isCaptionVisible: Boolean, + ) { + if (DesktopModeFlags.ENABLE_DESKTOP_APP_HANDLE_ANIMATION.isTrue()) { + setCaptionVisibility(isCaptionVisible) + } captionView.setBackgroundColor(getCaptionBackgroundColor(taskInfo, hasGlobalFocus)) val color = getAppNameAndButtonColor(taskInfo, hasGlobalFocus) val alpha = Color.alpha(color) @@ -359,10 +370,15 @@ class AppHeaderViewHolder( inFullImmersiveState: Boolean, hasGlobalFocus: Boolean, enableMaximizeLongClick: Boolean, + isCaptionVisible: Boolean, ) { val header = fillHeaderInfo(taskInfo, hasGlobalFocus) val headerStyle = getHeaderStyle(header) + if (DesktopModeFlags.ENABLE_DESKTOP_APP_HANDLE_ANIMATION.isTrue()) { + setCaptionVisibility(isCaptionVisible) + } + // Caption Background when (headerStyle.background) { is HeaderStyle.Background.Opaque -> { @@ -464,6 +480,11 @@ class AppHeaderViewHolder( } } + private fun setCaptionVisibility(visible: Boolean) { + val v = if (visible) View.VISIBLE else View.GONE + captionView.visibility = v + } + override fun onHandleMenuOpened() {} override fun onHandleMenuClosed() {} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java index 43bcc3b61124..2ef6c558b0b5 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackProgressAnimatorTest.java @@ -58,7 +58,8 @@ public class BackProgressAnimatorTest extends ShellTestCase { /* frameTime = */ 0, /* progress = */ progress, /* triggerBack = */ false, - /* swipeEdge = */ BackEvent.EDGE_LEFT); + /* swipeEdge = */ BackEvent.EDGE_LEFT, + /* departingAnimationTarget = */ null); } @Before diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomCrossActivityBackAnimationTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomCrossActivityBackAnimationTest.kt index 9d4cc49a7a65..2cc52c5ab9ad 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomCrossActivityBackAnimationTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/CustomCrossActivityBackAnimationTest.kt @@ -224,7 +224,8 @@ class CustomCrossActivityBackAnimationTest : ShellTestCase() { /* frameTime = */ 0, /* progress = */ progress, /* triggerBack = */ false, - /* swipeEdge = */ BackEvent.EDGE_LEFT + /* swipeEdge = */ BackEvent.EDGE_LEFT, + /* departingAnimationTarget = */ null ) private fun createAnimationTarget(open: Boolean): RemoteAnimationTarget { diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/pip/PipDesktopStateTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/pip/PipDesktopStateTest.java deleted file mode 100644 index 25dbc64f83de..000000000000 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/pip/PipDesktopStateTest.java +++ /dev/null @@ -1,184 +0,0 @@ -/* - * Copyright (C) 2025 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.wm.shell.common.pip; - -import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; -import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; -import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; - -import static com.android.window.flags.Flags.FLAG_ENABLE_CONNECTED_DISPLAYS_PIP; -import static com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_PIP; -import static com.android.wm.shell.Flags.FLAG_ENABLE_PIP2; - -import static junit.framework.Assert.assertEquals; -import static junit.framework.Assert.assertFalse; -import static junit.framework.Assert.assertTrue; - -import static org.mockito.Mockito.when; - -import android.app.ActivityManager; -import android.platform.test.annotations.EnableFlags; -import android.testing.AndroidTestingRunner; -import android.testing.TestableLooper; -import android.window.DisplayAreaInfo; -import android.window.WindowContainerToken; - -import androidx.test.filters.SmallTest; - -import com.android.wm.shell.RootTaskDisplayAreaOrganizer; -import com.android.wm.shell.desktopmode.DesktopRepository; -import com.android.wm.shell.desktopmode.DesktopUserRepositories; -import com.android.wm.shell.desktopmode.DragToDesktopTransitionHandler; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.Mockito; -import org.mockito.MockitoAnnotations; - -import java.util.Optional; - -/** - * Unit test against {@link PipDesktopState}. - */ -@SmallTest -@TestableLooper.RunWithLooper -@RunWith(AndroidTestingRunner.class) -@EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_PIP) -public class PipDesktopStateTest { - @Mock private PipDisplayLayoutState mMockPipDisplayLayoutState; - @Mock private Optional<DesktopUserRepositories> mMockDesktopUserRepositoriesOptional; - @Mock private DesktopUserRepositories mMockDesktopUserRepositories; - @Mock private DesktopRepository mMockDesktopRepository; - @Mock - private Optional<DragToDesktopTransitionHandler> mMockDragToDesktopTransitionHandlerOptional; - @Mock private DragToDesktopTransitionHandler mMockDragToDesktopTransitionHandler; - - @Mock private RootTaskDisplayAreaOrganizer mMockRootTaskDisplayAreaOrganizer; - @Mock private ActivityManager.RunningTaskInfo mMockTaskInfo; - - private static final int DISPLAY_ID = 1; - private DisplayAreaInfo mDefaultTda; - private PipDesktopState mPipDesktopState; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - when(mMockDesktopUserRepositoriesOptional.get()).thenReturn(mMockDesktopUserRepositories); - when(mMockDesktopUserRepositories.getCurrent()).thenReturn(mMockDesktopRepository); - when(mMockDesktopUserRepositoriesOptional.isPresent()).thenReturn(true); - - when(mMockDragToDesktopTransitionHandlerOptional.get()).thenReturn( - mMockDragToDesktopTransitionHandler); - when(mMockDragToDesktopTransitionHandlerOptional.isPresent()).thenReturn(true); - - when(mMockTaskInfo.getDisplayId()).thenReturn(DISPLAY_ID); - when(mMockPipDisplayLayoutState.getDisplayId()).thenReturn(DISPLAY_ID); - - mDefaultTda = new DisplayAreaInfo(Mockito.mock(WindowContainerToken.class), DISPLAY_ID, 0); - when(mMockRootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DISPLAY_ID)).thenReturn( - mDefaultTda); - - mPipDesktopState = new PipDesktopState(mMockPipDisplayLayoutState, - mMockDesktopUserRepositoriesOptional, - mMockDragToDesktopTransitionHandlerOptional, - mMockRootTaskDisplayAreaOrganizer); - } - - @Test - public void isDesktopWindowingPipEnabled_returnsTrue() { - assertTrue(mPipDesktopState.isDesktopWindowingPipEnabled()); - } - - @Test - public void isDesktopWindowingPipEnabled_desktopRepositoryEmpty_returnsFalse() { - when(mMockDesktopUserRepositoriesOptional.isPresent()).thenReturn(false); - - assertFalse(mPipDesktopState.isDesktopWindowingPipEnabled()); - } - - @Test - public void isDesktopWindowingPipEnabled_dragToDesktopTransitionHandlerEmpty_returnsFalse() { - when(mMockDragToDesktopTransitionHandlerOptional.isPresent()).thenReturn(false); - - assertFalse(mPipDesktopState.isDesktopWindowingPipEnabled()); - } - - @Test - @EnableFlags({ - FLAG_ENABLE_CONNECTED_DISPLAYS_PIP, FLAG_ENABLE_PIP2 - }) - public void isConnectedDisplaysPipEnabled_returnsTrue() { - assertTrue(mPipDesktopState.isConnectedDisplaysPipEnabled()); - } - - @Test - public void isPipInDesktopMode_anyDeskActive_returnsTrue() { - when(mMockDesktopRepository.isAnyDeskActive(DISPLAY_ID)).thenReturn(true); - - assertTrue(mPipDesktopState.isPipInDesktopMode()); - } - - @Test - public void isPipInDesktopMode_noDeskActive_returnsFalse() { - when(mMockDesktopRepository.isAnyDeskActive(DISPLAY_ID)).thenReturn(false); - - assertFalse(mPipDesktopState.isPipInDesktopMode()); - } - - @Test - public void getOutPipWindowingMode_exitToDesktop_displayFreeform_returnsUndefined() { - when(mMockDesktopRepository.isAnyDeskActive(DISPLAY_ID)).thenReturn(true); - setDisplayWindowingMode(WINDOWING_MODE_FREEFORM); - - assertEquals(WINDOWING_MODE_UNDEFINED, mPipDesktopState.getOutPipWindowingMode()); - } - - @Test - public void getOutPipWindowingMode_exitToDesktop_displayFullscreen_returnsFreeform() { - when(mMockDesktopRepository.isAnyDeskActive(DISPLAY_ID)).thenReturn(true); - setDisplayWindowingMode(WINDOWING_MODE_FULLSCREEN); - - assertEquals(WINDOWING_MODE_FREEFORM, mPipDesktopState.getOutPipWindowingMode()); - } - - @Test - public void getOutPipWindowingMode_exitToFullscreen_displayFullscreen_returnsUndefined() { - setDisplayWindowingMode(WINDOWING_MODE_FULLSCREEN); - - assertEquals(WINDOWING_MODE_UNDEFINED, mPipDesktopState.getOutPipWindowingMode()); - } - - @Test - public void isDragToDesktopInProgress_inProgress_returnsTrue() { - when(mMockDragToDesktopTransitionHandler.getInProgress()).thenReturn(true); - - assertTrue(mPipDesktopState.isDragToDesktopInProgress()); - } - - @Test - public void isDragToDesktopInProgress_notInProgress_returnsFalse() { - when(mMockDragToDesktopTransitionHandler.getInProgress()).thenReturn(false); - - assertFalse(mPipDesktopState.isDragToDesktopInProgress()); - } - - private void setDisplayWindowingMode(int windowingMode) { - mDefaultTda.configuration.windowConfiguration.setWindowingMode(windowingMode); - } -} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/pip/PipDesktopStateTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/pip/PipDesktopStateTest.kt new file mode 100644 index 000000000000..2c50cd9d0c81 --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/pip/PipDesktopStateTest.kt @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.wm.shell.common.pip + +import android.app.ActivityManager +import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM +import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN +import android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED +import android.platform.test.annotations.EnableFlags +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper.RunWithLooper +import android.window.DisplayAreaInfo +import android.window.WindowContainerToken +import androidx.test.filters.SmallTest +import com.android.window.flags.Flags.FLAG_ENABLE_CONNECTED_DISPLAYS_PIP +import com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_PIP +import com.android.wm.shell.Flags.FLAG_ENABLE_PIP2 +import com.android.wm.shell.RootTaskDisplayAreaOrganizer +import com.android.wm.shell.ShellTestCase +import com.android.wm.shell.desktopmode.DesktopRepository +import com.android.wm.shell.desktopmode.DesktopUserRepositories +import com.android.wm.shell.desktopmode.DragToDesktopTransitionHandler +import com.google.common.truth.Truth.assertThat +import java.util.Optional +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.kotlin.mock +import org.mockito.kotlin.whenever + +/** + * Unit test against [PipDesktopState]. + */ +@SmallTest +@RunWithLooper +@RunWith(AndroidTestingRunner::class) +@EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_PIP) +class PipDesktopStateTest : ShellTestCase() { + private val mockPipDisplayLayoutState = mock<PipDisplayLayoutState>() + private val mockDesktopUserRepositories = mock<DesktopUserRepositories>() + private val mockDesktopRepository = mock<DesktopRepository>() + private val mockDragToDesktopTransitionHandler = mock<DragToDesktopTransitionHandler>() + private val mockRootTaskDisplayAreaOrganizer = mock<RootTaskDisplayAreaOrganizer>() + private val mockTaskInfo = mock<ActivityManager.RunningTaskInfo>() + private lateinit var defaultTda: DisplayAreaInfo + private lateinit var pipDesktopState: PipDesktopState + + @Before + fun setUp() { + whenever(mockDesktopUserRepositories.current).thenReturn(mockDesktopRepository) + whenever(mockTaskInfo.getDisplayId()).thenReturn(DISPLAY_ID) + whenever(mockPipDisplayLayoutState.displayId).thenReturn(DISPLAY_ID) + + defaultTda = DisplayAreaInfo(mock<WindowContainerToken>(), DISPLAY_ID, /* featureId = */ 0) + whenever(mockRootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DISPLAY_ID)).thenReturn( + defaultTda + ) + + pipDesktopState = + PipDesktopState( + mockPipDisplayLayoutState, + Optional.of(mockDesktopUserRepositories), + Optional.of(mockDragToDesktopTransitionHandler), + mockRootTaskDisplayAreaOrganizer + ) + } + + @Test + fun isDesktopWindowingPipEnabled_returnsTrue() { + assertThat(pipDesktopState.isDesktopWindowingPipEnabled()).isTrue() + } + + @Test + @EnableFlags( + FLAG_ENABLE_CONNECTED_DISPLAYS_PIP, + FLAG_ENABLE_PIP2 + ) + fun isConnectedDisplaysPipEnabled_returnsTrue() { + assertThat(pipDesktopState.isConnectedDisplaysPipEnabled()).isTrue() + } + + @Test + fun isPipInDesktopMode_anyDeskActive_returnsTrue() { + whenever(mockDesktopRepository.isAnyDeskActive(DISPLAY_ID)).thenReturn(true) + + assertThat(pipDesktopState.isPipInDesktopMode()).isTrue() + } + + @Test + fun isPipInDesktopMode_noDeskActive_returnsFalse() { + whenever(mockDesktopRepository.isAnyDeskActive(DISPLAY_ID)).thenReturn(false) + + assertThat(pipDesktopState.isPipInDesktopMode()).isFalse() + } + + @Test + fun outPipWindowingMode_exitToDesktop_displayFreeform_returnsUndefined() { + whenever(mockDesktopRepository.isAnyDeskActive(DISPLAY_ID)).thenReturn(true) + setDisplayWindowingMode(WINDOWING_MODE_FREEFORM) + + assertThat(pipDesktopState.getOutPipWindowingMode()).isEqualTo(WINDOWING_MODE_UNDEFINED) + } + + @Test + fun outPipWindowingMode_exitToDesktop_displayFullscreen_returnsFreeform() { + whenever(mockDesktopRepository.isAnyDeskActive(DISPLAY_ID)).thenReturn(true) + setDisplayWindowingMode(WINDOWING_MODE_FULLSCREEN) + + assertThat(pipDesktopState.getOutPipWindowingMode()).isEqualTo(WINDOWING_MODE_FREEFORM) + } + + @Test + fun outPipWindowingMode_exitToFullscreen_displayFullscreen_returnsUndefined() { + setDisplayWindowingMode(WINDOWING_MODE_FULLSCREEN) + + assertThat(pipDesktopState.getOutPipWindowingMode()).isEqualTo(WINDOWING_MODE_UNDEFINED) + } + + @Test + fun isDragToDesktopInProgress_inProgress_returnsTrue() { + whenever(mockDragToDesktopTransitionHandler.inProgress).thenReturn(true) + + assertThat(pipDesktopState.isDragToDesktopInProgress()).isTrue() + } + + @Test + fun isDragToDesktopInProgress_notInProgress_returnsFalse() { + whenever(mockDragToDesktopTransitionHandler.inProgress).thenReturn(false) + + assertThat(pipDesktopState.isDragToDesktopInProgress()).isFalse() + } + + private fun setDisplayWindowingMode(windowingMode: Int) { + defaultTda.configuration.windowConfiguration.windowingMode = windowingMode + } + + companion object { + private const val DISPLAY_ID = 1 + } +} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/FlexParallaxSpecTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/FlexParallaxSpecTests.java index 22a85fc49a4b..9f2534eb2662 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/FlexParallaxSpecTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/common/split/FlexParallaxSpecTests.java @@ -71,6 +71,7 @@ public class FlexParallaxSpecTests { when(mockSnapAlgorithm.getMiddleTarget()).thenReturn(mockMiddleTarget); when(mockSnapAlgorithm.getLastSplitTarget()).thenReturn(mockLastTarget); when(mockSnapAlgorithm.getDismissEndTarget()).thenReturn(mockEndEdge); + when(mockSnapAlgorithm.areOffscreenRatiosSupported()).thenReturn(true); when(mockStartEdge.getPosition()).thenReturn(0); when(mockFirstTarget.getPosition()).thenReturn(250); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java index 597e4a55ed0e..9035df28aa7c 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java @@ -680,7 +680,8 @@ public class CompatUIControllerTest extends ShellTestCase { // Create transparent task final TaskInfo taskInfo1 = createTaskInfo(DISPLAY_ID, newTaskId, /* hasSizeCompat= */ true, - /* isVisible */ true, /* isFocused */ true, /* isTopActivityTransparent */ true); + /* isVisible */ true, /* isFocused */ true, /* isTopActivityTransparent */ true, + /* isRestartMenuEnabledForDisplayMove */ true); // Simulate new task being shown mController.updateActiveTaskInfo(taskInfo1); @@ -742,32 +743,38 @@ public class CompatUIControllerTest extends ShellTestCase { @Test @RequiresFlagsDisabled(Flags.FLAG_APP_COMPAT_UI_FRAMEWORK) public void testSendCompatUIRequest_createRestartDialog() { - TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ false); - doReturn(true).when(mMockRestartDialogLayout) - .needsToBeRecreated(any(TaskInfo.class), - any(ShellTaskOrganizer.TaskListener.class)); + final TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, /* hasSizeCompat= */ true, + /* isVisible */ true, /* isFocused */ true, /* isTopActivityTransparent */ false, + /* isRestartMenuEnabledForDisplayMove */ true); doReturn(true).when(mCompatUIConfiguration).isRestartDialogEnabled(); doReturn(true).when(mCompatUIConfiguration).shouldShowRestartDialogAgain(eq(taskInfo)); - mController.sendCompatUIRequest(new CompatUIRequests.DisplayCompatShowRestartDialog( - taskInfo, mMockTaskListener)); + mController.onCompatInfoChanged(new CompatUIInfo(taskInfo, mMockTaskListener)); verify(mController).createRestartDialogWindowManager(any(), eq(taskInfo), eq(mMockTaskListener)); + verify(mMockRestartDialogLayout).setRequestRestartDialog(false); + + mController.sendCompatUIRequest( + new CompatUIRequests.DisplayCompatShowRestartDialog(taskInfo.taskId)); + verify(mMockRestartDialogLayout).setRequestRestartDialog(true); } private static TaskInfo createTaskInfo(int displayId, int taskId, boolean hasSizeCompat) { return createTaskInfo(displayId, taskId, hasSizeCompat, /* isVisible */ false, - /* isFocused */ false, /* isTopActivityTransparent */ false); + /* isFocused */ false, /* isTopActivityTransparent */ false, + /* isRestartMenuEnabledForDisplayMove */ false); } private static TaskInfo createTaskInfo(int displayId, int taskId, boolean hasSizeCompat, boolean isVisible, boolean isFocused) { return createTaskInfo(displayId, taskId, hasSizeCompat, - isVisible, isFocused, /* isTopActivityTransparent */ false); + isVisible, isFocused, /* isTopActivityTransparent */ false, + /* isRestartMenuEnabledForDisplayMove */ false); } private static TaskInfo createTaskInfo(int displayId, int taskId, boolean hasSizeCompat, - boolean isVisible, boolean isFocused, boolean isTopActivityTransparent) { + boolean isVisible, boolean isFocused, boolean isTopActivityTransparent, + boolean isRestartMenuEnabledForDisplayMove) { RunningTaskInfo taskInfo = new RunningTaskInfo(); taskInfo.taskId = taskId; taskInfo.displayId = displayId; @@ -777,6 +784,8 @@ public class CompatUIControllerTest extends ShellTestCase { taskInfo.isTopActivityTransparent = isTopActivityTransparent; taskInfo.appCompatTaskInfo.setLetterboxEducationEnabled(true); taskInfo.appCompatTaskInfo.setTopActivityLetterboxed(true); + taskInfo.appCompatTaskInfo.setRestartMenuEnabledForDisplayMove( + isRestartMenuEnabledForDisplayMove); return taskInfo; } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopActivityOrientationChangeHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopActivityOrientationChangeHandlerTest.kt index d58f8a34c98e..94fe03084989 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopActivityOrientationChangeHandlerTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopActivityOrientationChangeHandlerTest.kt @@ -37,6 +37,8 @@ import com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE import com.android.window.flags.Flags.FLAG_RESPECT_ORIENTATION_CHANGE_FOR_UNRESIZEABLE import com.android.wm.shell.ShellTaskOrganizer import com.android.wm.shell.ShellTestCase +import com.android.wm.shell.common.DisplayController +import com.android.wm.shell.common.DisplayLayout import com.android.wm.shell.common.ShellExecutor import com.android.wm.shell.common.TaskStackListenerImpl import com.android.wm.shell.desktopmode.DesktopTestHelpers.createFreeformTask @@ -96,12 +98,15 @@ class DesktopActivityOrientationChangeHandlerTest : ShellTestCase() { @Mock lateinit var repositoryInitializer: DesktopRepositoryInitializer @Mock lateinit var userManager: UserManager @Mock lateinit var shellController: ShellController + @Mock lateinit var displayController: DisplayController + @Mock lateinit var displayLayout: DisplayLayout private lateinit var mockitoSession: StaticMockitoSession private lateinit var handler: DesktopActivityOrientationChangeHandler private lateinit var shellInit: ShellInit private lateinit var userRepositories: DesktopUserRepositories private lateinit var testScope: CoroutineScope + // Mock running tasks are registered here so we can get the list from mock shell task organizer. private val runningTasks = mutableListOf<RunningTaskInfo>() @@ -131,6 +136,7 @@ class DesktopActivityOrientationChangeHandlerTest : ShellTestCase() { whenever(transitions.startTransition(anyInt(), any(), isNull())).thenAnswer { Binder() } whenever(runBlocking { persistentRepository.readDesktop(any(), any()) }) .thenReturn(Desktop.getDefaultInstance()) + whenever(displayController.getDisplayLayout(anyInt())).thenReturn(displayLayout) handler = DesktopActivityOrientationChangeHandler( @@ -140,6 +146,7 @@ class DesktopActivityOrientationChangeHandlerTest : ShellTestCase() { taskStackListener, resizeTransitionHandler, userRepositories, + displayController, ) shellInit.init() @@ -171,6 +178,7 @@ class DesktopActivityOrientationChangeHandlerTest : ShellTestCase() { taskStackListener, resizeTransitionHandler, userRepositories, + displayController, ) verify(shellInit, never()) @@ -251,6 +259,11 @@ class DesktopActivityOrientationChangeHandlerTest : ShellTestCase() { val oldBounds = task.configuration.windowConfiguration.bounds val newTask = setUpFreeformTask(isResizeable = false, orientation = SCREEN_ORIENTATION_LANDSCAPE) + whenever(displayLayout.height()).thenReturn(800) + whenever(displayLayout.width()).thenReturn(2000) + whenever(displayLayout.getStableBounds(any())).thenAnswer { i -> + (i.arguments.first() as Rect).set(Rect(0, 0, 2000, 800)) + } handler.handleActivityOrientationChange(task, newTask) @@ -279,6 +292,11 @@ class DesktopActivityOrientationChangeHandlerTest : ShellTestCase() { bounds = oldBounds, ) val newTask = setUpFreeformTask(isResizeable = false, bounds = oldBounds) + whenever(displayLayout.height()).thenReturn(2000) + whenever(displayLayout.width()).thenReturn(800) + whenever(displayLayout.getStableBounds(any())).thenAnswer { i -> + (i.arguments.first() as Rect).set(Rect(0, 0, 800, 2000)) + } handler.handleActivityOrientationChange(task, newTask) diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandlerTest.kt index 2aebcdcc3bf5..9268db60aa51 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandlerTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopDisplayEventHandlerTest.kt @@ -24,13 +24,16 @@ import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession import com.android.dx.mockito.inline.extended.ExtendedMockito.never import com.android.dx.mockito.inline.extended.StaticMockitoSession import com.android.window.flags.Flags +import com.android.wm.shell.RootTaskDisplayAreaOrganizer import com.android.wm.shell.ShellTestCase import com.android.wm.shell.common.DisplayController import com.android.wm.shell.common.DisplayController.OnDisplaysChangedListener import com.android.wm.shell.common.ShellExecutor import com.android.wm.shell.desktopmode.persistence.DesktopRepositoryInitializer import com.android.wm.shell.shared.desktopmode.DesktopModeStatus +import com.android.wm.shell.sysui.ShellController import com.android.wm.shell.sysui.ShellInit +import com.android.wm.shell.sysui.UserChangeListener import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.cancel import kotlinx.coroutines.flow.MutableStateFlow @@ -46,6 +49,7 @@ import org.mockito.Mockito.spy import org.mockito.Mockito.times import org.mockito.Mockito.verify import org.mockito.kotlin.argumentCaptor +import org.mockito.kotlin.clearInvocations import org.mockito.kotlin.whenever import org.mockito.quality.Strictness @@ -60,6 +64,8 @@ import org.mockito.quality.Strictness class DesktopDisplayEventHandlerTest : ShellTestCase() { @Mock lateinit var testExecutor: ShellExecutor @Mock lateinit var displayController: DisplayController + @Mock private lateinit var mockShellController: ShellController + @Mock private lateinit var mockRootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer @Mock private lateinit var mockDesktopUserRepositories: DesktopUserRepositories @Mock private lateinit var mockDesktopRepository: DesktopRepository @Mock private lateinit var mockDesktopTasksController: DesktopTasksController @@ -89,7 +95,9 @@ class DesktopDisplayEventHandlerTest : ShellTestCase() { context, shellInit, testScope.backgroundScope, + mockShellController, displayController, + mockRootTaskDisplayAreaOrganizer, desktopRepositoryInitializer, mockDesktopUserRepositories, mockDesktopTasksController, @@ -107,6 +115,7 @@ class DesktopDisplayEventHandlerTest : ShellTestCase() { } @Test + @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND) fun testDisplayAdded_supportsDesks_desktopRepositoryInitialized_createsDesk() = testScope.runTest { whenever(DesktopModeStatus.canEnterDesktopMode(context)).thenReturn(true) @@ -119,6 +128,7 @@ class DesktopDisplayEventHandlerTest : ShellTestCase() { } @Test + @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND) fun testDisplayAdded_supportsDesks_desktopRepositoryNotInitialized_doesNotCreateDesk() = testScope.runTest { whenever(DesktopModeStatus.canEnterDesktopMode(context)).thenReturn(true) @@ -130,6 +140,7 @@ class DesktopDisplayEventHandlerTest : ShellTestCase() { } @Test + @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND) fun testDisplayAdded_supportsDesks_desktopRepositoryInitializedTwice_createsDeskOnce() = testScope.runTest { whenever(DesktopModeStatus.canEnterDesktopMode(context)).thenReturn(true) @@ -143,6 +154,7 @@ class DesktopDisplayEventHandlerTest : ShellTestCase() { } @Test + @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND) fun testDisplayAdded_supportsDesks_desktopRepositoryInitialized_deskExists_doesNotCreateDesk() = testScope.runTest { whenever(DesktopModeStatus.canEnterDesktopMode(context)).thenReturn(true) @@ -156,33 +168,71 @@ class DesktopDisplayEventHandlerTest : ShellTestCase() { } @Test - fun testDisplayAdded_cannotEnterDesktopMode_doesNotCreateDesk() { - whenever(DesktopModeStatus.canEnterDesktopMode(context)).thenReturn(false) + @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND) + fun testDisplayAdded_cannotEnterDesktopMode_doesNotCreateDesk() = + testScope.runTest { + whenever(DesktopModeStatus.canEnterDesktopMode(context)).thenReturn(false) + desktopRepositoryInitializer.initialize(mockDesktopUserRepositories) - onDisplaysChangedListenerCaptor.lastValue.onDisplayAdded(DEFAULT_DISPLAY) + onDisplaysChangedListenerCaptor.lastValue.onDisplayAdded(DEFAULT_DISPLAY) + runCurrent() - verify(mockDesktopTasksController, never()).createDesk(DEFAULT_DISPLAY) - } + verify(mockDesktopTasksController, never()).createDesk(DEFAULT_DISPLAY) + } @Test @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND) - fun testDeskRemoved_noDesksRemain_createsDesk() { - whenever(mockDesktopRepository.getNumberOfDesks(DEFAULT_DISPLAY)).thenReturn(0) + fun testDeskRemoved_noDesksRemain_createsDesk() = + testScope.runTest { + whenever(DesktopModeStatus.canEnterDesktopMode(context)).thenReturn(true) + whenever(mockDesktopRepository.getNumberOfDesks(DEFAULT_DISPLAY)).thenReturn(0) + desktopRepositoryInitializer.initialize(mockDesktopUserRepositories) - handler.onDeskRemoved(DEFAULT_DISPLAY, deskId = 1) + handler.onDeskRemoved(DEFAULT_DISPLAY, deskId = 1) + runCurrent() - verify(mockDesktopTasksController).createDesk(DEFAULT_DISPLAY) - } + verify(mockDesktopTasksController).createDesk(DEFAULT_DISPLAY) + } @Test @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND) - fun testDeskRemoved_desksRemain_doesNotCreateDesk() { - whenever(mockDesktopRepository.getNumberOfDesks(DEFAULT_DISPLAY)).thenReturn(1) + fun testDeskRemoved_desksRemain_doesNotCreateDesk() = + testScope.runTest { + whenever(DesktopModeStatus.canEnterDesktopMode(context)).thenReturn(true) + whenever(mockDesktopRepository.getNumberOfDesks(DEFAULT_DISPLAY)).thenReturn(1) + desktopRepositoryInitializer.initialize(mockDesktopUserRepositories) + + handler.onDeskRemoved(DEFAULT_DISPLAY, deskId = 1) + runCurrent() - handler.onDeskRemoved(DEFAULT_DISPLAY, deskId = 1) + verify(mockDesktopTasksController, never()).createDesk(DEFAULT_DISPLAY) + } - verify(mockDesktopTasksController, never()).createDesk(DEFAULT_DISPLAY) - } + @Test + @EnableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND) + fun testUserChanged_createsDeskWhenNeeded() = + testScope.runTest { + whenever(DesktopModeStatus.canEnterDesktopMode(context)).thenReturn(true) + val userChangeListenerCaptor = argumentCaptor<UserChangeListener>() + verify(mockShellController).addUserChangeListener(userChangeListenerCaptor.capture()) + whenever(mockDesktopRepository.getNumberOfDesks(displayId = 2)).thenReturn(0) + whenever(mockDesktopRepository.getNumberOfDesks(displayId = 3)).thenReturn(0) + whenever(mockDesktopRepository.getNumberOfDesks(displayId = 4)).thenReturn(1) + whenever(mockRootTaskDisplayAreaOrganizer.displayIds).thenReturn(intArrayOf(2, 3, 4)) + desktopRepositoryInitializer.initialize(mockDesktopUserRepositories) + handler.onDisplayAdded(displayId = 2) + handler.onDisplayAdded(displayId = 3) + handler.onDisplayAdded(displayId = 4) + runCurrent() + + clearInvocations(mockDesktopTasksController) + userChangeListenerCaptor.lastValue.onUserChanged(1, context) + runCurrent() + + verify(mockDesktopTasksController).createDesk(displayId = 2) + verify(mockDesktopTasksController).createDesk(displayId = 3) + verify(mockDesktopTasksController, never()).createDesk(displayId = 4) + } @Test fun testConnectExternalDisplay() { diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandlerTest.kt index d510570e8839..e40da5e8498d 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandlerTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandlerTest.kt @@ -50,7 +50,6 @@ import com.android.wm.shell.desktopmode.common.ToggleTaskSizeInteraction import com.android.wm.shell.sysui.ShellInit import com.android.wm.shell.transition.FocusTransitionObserver import com.android.wm.shell.windowdecor.DesktopModeWindowDecorViewModel -import com.google.common.truth.Truth.assertThat import java.util.Optional import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -120,11 +119,11 @@ class DesktopModeKeyGestureHandlerTest : ShellTestCase() { whenever(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)).thenReturn(tda) doAnswer { - keyGestureEventHandler = (it.arguments[0] as KeyGestureEventHandler) + keyGestureEventHandler = (it.arguments[1] as KeyGestureEventHandler) null } .whenever(inputManager) - .registerKeyGestureEventHandler(any()) + .registerKeyGestureEventHandler(any(), any()) shellInit.init() desktopModeKeyGestureHandler = @@ -176,10 +175,9 @@ class DesktopModeKeyGestureHandlerTest : ShellTestCase() { .setKeycodes(intArrayOf(KeyEvent.KEYCODE_D)) .setModifierState(KeyEvent.META_META_ON or KeyEvent.META_CTRL_ON) .build() - val result = keyGestureEventHandler.handleKeyGestureEvent(event, null) + keyGestureEventHandler.handleKeyGestureEvent(event, null) testExecutor.flushAll() - assertThat(result).isTrue() verify(desktopTasksController).moveToNextDisplay(task.taskId) } @@ -197,10 +195,9 @@ class DesktopModeKeyGestureHandlerTest : ShellTestCase() { .setKeycodes(intArrayOf(KeyEvent.KEYCODE_LEFT_BRACKET)) .setModifierState(KeyEvent.META_META_ON) .build() - val result = keyGestureEventHandler.handleKeyGestureEvent(event, null) + keyGestureEventHandler.handleKeyGestureEvent(event, null) testExecutor.flushAll() - assertThat(result).isTrue() verify(desktopModeWindowDecorViewModel) .onSnapResize( task.taskId, @@ -224,10 +221,9 @@ class DesktopModeKeyGestureHandlerTest : ShellTestCase() { .setKeycodes(intArrayOf(KeyEvent.KEYCODE_RIGHT_BRACKET)) .setModifierState(KeyEvent.META_META_ON) .build() - val result = keyGestureEventHandler.handleKeyGestureEvent(event, null) + keyGestureEventHandler.handleKeyGestureEvent(event, null) testExecutor.flushAll() - assertThat(result).isTrue() verify(desktopModeWindowDecorViewModel) .onSnapResize( task.taskId, @@ -251,10 +247,9 @@ class DesktopModeKeyGestureHandlerTest : ShellTestCase() { .setKeycodes(intArrayOf(KeyEvent.KEYCODE_EQUALS)) .setModifierState(KeyEvent.META_META_ON) .build() - val result = keyGestureEventHandler.handleKeyGestureEvent(event, null) + keyGestureEventHandler.handleKeyGestureEvent(event, null) testExecutor.flushAll() - assertThat(result).isTrue() verify(desktopTasksController) .toggleDesktopTaskSize( task, @@ -280,10 +275,9 @@ class DesktopModeKeyGestureHandlerTest : ShellTestCase() { .setKeycodes(intArrayOf(KeyEvent.KEYCODE_MINUS)) .setModifierState(KeyEvent.META_META_ON) .build() - val result = keyGestureEventHandler.handleKeyGestureEvent(event, null) + keyGestureEventHandler.handleKeyGestureEvent(event, null) testExecutor.flushAll() - assertThat(result).isTrue() verify(desktopTasksController).minimizeTask(task, MinimizeReason.KEY_GESTURE) } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/VisualIndicatorViewContainerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/VisualIndicatorViewContainerTest.kt index 3983bfbb2080..75f8d9e819cb 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/VisualIndicatorViewContainerTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/VisualIndicatorViewContainerTest.kt @@ -16,6 +16,7 @@ package com.android.wm.shell.desktopmode +import android.animation.AnimatorTestRule import android.app.ActivityManager import android.app.ActivityManager.RunningTaskInfo import android.graphics.Rect @@ -29,6 +30,7 @@ import android.view.Display.DEFAULT_DISPLAY import android.view.SurfaceControl import android.view.SurfaceControlViewHost import android.view.View +import android.widget.FrameLayout import androidx.test.filters.SmallTest import com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE import com.android.wm.shell.ShellTestCase @@ -43,6 +45,7 @@ import com.android.wm.shell.windowdecor.tiling.SnapEventHandler import com.google.common.truth.Truth.assertThat import kotlin.test.Test import org.junit.Before +import org.junit.Rule import org.junit.runner.RunWith import org.mockito.ArgumentMatchers.anyInt import org.mockito.Mock @@ -67,6 +70,9 @@ import org.mockito.kotlin.whenever @RunWith(AndroidTestingRunner::class) @EnableFlags(FLAG_ENABLE_DESKTOP_WINDOWING_MODE) class VisualIndicatorViewContainerTest : ShellTestCase() { + + @JvmField @Rule val animatorTestRule = AnimatorTestRule(this) + @Mock private lateinit var view: View @Mock private lateinit var displayLayout: DisplayLayout @Mock private lateinit var displayController: DisplayController @@ -297,6 +303,95 @@ class VisualIndicatorViewContainerTest : ShellTestCase() { verify(spyViewContainer, never()).fadeInIndicatorInternal(any(), any(), any(), any()) } + @Test + @EnableFlags( + com.android.wm.shell.Flags.FLAG_ENABLE_BUBBLE_TO_FULLSCREEN, + com.android.wm.shell.Flags.FLAG_ENABLE_CREATE_ANY_BUBBLE, + ) + fun testCreateView_bubblesEnabled_indicatorIsFrameLayout() { + val spyViewContainer = setupSpyViewContainer() + assertThat(spyViewContainer.indicatorView).isInstanceOf(FrameLayout::class.java) + } + + @Test + @EnableFlags( + com.android.wm.shell.Flags.FLAG_ENABLE_BUBBLE_TO_FULLSCREEN, + com.android.wm.shell.Flags.FLAG_ENABLE_CREATE_ANY_BUBBLE, + ) + fun testFadeInOutBubbleIndicator_addAndRemoveBarIndicator() { + setUpBubbleBoundsProvider() + val spyViewContainer = setupSpyViewContainer() + spyViewContainer.fadeInIndicator( + displayLayout, + DesktopModeVisualIndicator.IndicatorType.TO_BUBBLE_RIGHT_INDICATOR, + DEFAULT_DISPLAY, + ) + desktopExecutor.flushAll() + animatorTestRule.advanceTimeBy(200) + assertThat((spyViewContainer.indicatorView as FrameLayout).getChildAt(0)).isNotNull() + + spyViewContainer.fadeOutIndicator( + displayLayout, + DesktopModeVisualIndicator.IndicatorType.TO_BUBBLE_RIGHT_INDICATOR, + finishCallback = null, + DEFAULT_DISPLAY, + snapEventHandler, + ) + desktopExecutor.flushAll() + animatorTestRule.advanceTimeBy(250) + assertThat((spyViewContainer.indicatorView as FrameLayout).getChildAt(0)).isNull() + } + + @Test + @EnableFlags( + com.android.wm.shell.Flags.FLAG_ENABLE_BUBBLE_TO_FULLSCREEN, + com.android.wm.shell.Flags.FLAG_ENABLE_CREATE_ANY_BUBBLE, + ) + fun testTransitionIndicator_fullscreenToBubble_addBarIndicator() { + setUpBubbleBoundsProvider() + val spyViewContainer = setupSpyViewContainer() + + spyViewContainer.transitionIndicator( + taskInfo, + displayController, + DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR, + DesktopModeVisualIndicator.IndicatorType.TO_BUBBLE_RIGHT_INDICATOR, + ) + desktopExecutor.flushAll() + animatorTestRule.advanceTimeBy(200) + + assertThat((spyViewContainer.indicatorView as FrameLayout).getChildAt(0)).isNotNull() + } + + @Test + @EnableFlags( + com.android.wm.shell.Flags.FLAG_ENABLE_BUBBLE_TO_FULLSCREEN, + com.android.wm.shell.Flags.FLAG_ENABLE_CREATE_ANY_BUBBLE, + ) + fun testTransitionIndicator_bubbleToFullscreen_removeBarIndicator() { + setUpBubbleBoundsProvider() + val spyViewContainer = setupSpyViewContainer() + spyViewContainer.fadeInIndicator( + displayLayout, + DesktopModeVisualIndicator.IndicatorType.TO_BUBBLE_RIGHT_INDICATOR, + DEFAULT_DISPLAY, + ) + desktopExecutor.flushAll() + animatorTestRule.advanceTimeBy(200) + assertThat((spyViewContainer.indicatorView as FrameLayout).getChildAt(0)).isNotNull() + + spyViewContainer.transitionIndicator( + taskInfo, + displayController, + DesktopModeVisualIndicator.IndicatorType.TO_BUBBLE_RIGHT_INDICATOR, + DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR, + ) + desktopExecutor.flushAll() + animatorTestRule.advanceTimeBy(200) + + assertThat((spyViewContainer.indicatorView as FrameLayout).getChildAt(0)).isNull() + } + private fun setupSpyViewContainer(): VisualIndicatorViewContainer { val viewContainer = VisualIndicatorViewContainer( @@ -331,7 +426,22 @@ class VisualIndicatorViewContainerTest : ShellTestCase() { .build() } + private fun setUpBubbleBoundsProvider() { + bubbleDropTargetBoundsProvider = + object : BubbleDropTargetBoundsProvider { + override fun getBubbleBarExpandedViewDropTargetBounds(onLeft: Boolean): Rect { + return BUBBLE_INDICATOR_BOUNDS + } + + override fun getBarDropTargetBounds(onLeft: Boolean): Rect { + return BAR_INDICATOR_BOUNDS + } + } + } + companion object { private val DISPLAY_BOUNDS = Rect(0, 0, 1000, 1000) + private val BUBBLE_INDICATOR_BOUNDS = Rect(800, 200, 900, 900) + private val BAR_INDICATOR_BOUNDS = Rect(880, 950, 900, 960) } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializerTest.kt index 4440d4e801fe..601eb315f394 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializerTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/persistence/DesktopRepositoryInitializerTest.kt @@ -27,6 +27,7 @@ import com.android.window.flags.Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND import com.android.wm.shell.ShellTestCase import com.android.wm.shell.common.ShellExecutor import com.android.wm.shell.desktopmode.DesktopUserRepositories +import com.android.wm.shell.desktopmode.persistence.DesktopRepositoryInitializer.DeskRecreationFactory import com.android.wm.shell.sysui.ShellController import com.android.wm.shell.sysui.ShellInit import com.google.common.truth.Truth.assertThat @@ -242,6 +243,36 @@ class DesktopRepositoryInitializerTest : ShellTestCase() { .inOrder() } + @Test + @EnableFlags( + FLAG_ENABLE_DESKTOP_WINDOWING_PERSISTENCE, + FLAG_ENABLE_DESKTOP_WINDOWING_HSUM, + FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND, + ) + fun initWithPersistence_deskRecreationFailed_deskNotAdded() = + runTest(StandardTestDispatcher()) { + whenever(persistentRepository.getUserDesktopRepositoryMap()) + .thenReturn(mapOf(USER_ID_1 to desktopRepositoryState1)) + whenever(persistentRepository.getDesktopRepositoryState(USER_ID_1)) + .thenReturn(desktopRepositoryState1) + whenever(persistentRepository.readDesktop(USER_ID_1, DESKTOP_ID_1)).thenReturn(desktop1) + whenever(persistentRepository.readDesktop(USER_ID_1, DESKTOP_ID_2)).thenReturn(desktop2) + + // Make [DESKTOP_ID_2] re-creation fail. + repositoryInitializer.deskRecreationFactory = + DeskRecreationFactory { userId, destinationDisplayId, deskId -> + if (deskId == DESKTOP_ID_2) { + null + } else { + deskId + } + } + repositoryInitializer.initialize(desktopUserRepositories) + + assertThat(desktopUserRepositories.getProfile(USER_ID_1).getDeskIds(DEFAULT_DISPLAY)) + .containsExactly(DESKTOP_ID_1) + } + @After fun tearDown() { datastoreScope.cancel() diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/GroupedTaskInfoTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/GroupedTaskInfoTest.kt index 75f6bda4d750..4e8812d34ef4 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/GroupedTaskInfoTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/GroupedTaskInfoTest.kt @@ -21,6 +21,7 @@ import android.app.TaskInfo import android.graphics.Rect import android.os.Parcel import android.testing.AndroidTestingRunner +import android.view.Display.DEFAULT_DISPLAY import android.window.IWindowContainerToken import android.window.WindowContainerToken import androidx.test.filters.SmallTest @@ -281,7 +282,8 @@ class GroupedTaskInfoTest : ShellTestCase() { val task2 = createTaskInfo(id = 2) val taskInfo = GroupedTaskInfo.forDeskTasks( - /* deskId = */ 500, listOf(task1, task2), setOf()) + /* deskId = */ 500, DEFAULT_DISPLAY, listOf(task1, task2), setOf() + ) assertThat(taskInfo.deskId).isEqualTo(500) assertThat(taskInfo.getTaskById(1)).isEqualTo(task1) @@ -335,6 +337,7 @@ class GroupedTaskInfoTest : ShellTestCase() { ): GroupedTaskInfo { return GroupedTaskInfo.forDeskTasks( deskId, + DEFAULT_DISPLAY, freeformTaskIds.map { createTaskInfo(it) }.toList(), minimizedTaskIds.toSet()) } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java index a122c3820dcb..55bff09e0ae2 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java @@ -222,7 +222,7 @@ public class HomeTransitionObserverTest extends ShellTestCase { @Test @EnableFlags({FLAG_ENABLE_DRAG_TO_DESKTOP_INCOMING_TRANSITIONS_BUGFIX}) - public void startDragToDesktopAborted_doesNotTriggerCallback() throws RemoteException { + public void startDragToDesktopAborted_triggersCallback() throws RemoteException { TransitionInfo info = mock(TransitionInfo.class); TransitionInfo.Change change = mock(TransitionInfo.Change.class); ActivityManager.RunningTaskInfo taskInfo = mock(ActivityManager.RunningTaskInfo.class); @@ -239,7 +239,7 @@ public class HomeTransitionObserverTest extends ShellTestCase { mHomeTransitionObserver.onTransitionFinished(transition, /* aborted= */ true); - verify(mListener, never()).onHomeVisibilityChanged(/* isVisible= */ anyBoolean()); + verify(mListener).onHomeVisibilityChanged(/* isVisible= */ true); } @Test diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java index 677330790bab..9849b1174d8e 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java @@ -37,6 +37,7 @@ import static android.view.WindowManager.TRANSIT_TO_BACK; import static android.view.WindowManager.TRANSIT_TO_FRONT; import static android.window.TransitionInfo.FLAG_DISPLAY_HAS_ALERT_WINDOWS; import static android.window.TransitionInfo.FLAG_IS_DISPLAY; +import static android.window.TransitionInfo.FLAG_MOVED_TO_TOP; import static android.window.TransitionInfo.FLAG_SYNC; import static android.window.TransitionInfo.FLAG_TRANSLUCENT; @@ -1742,6 +1743,53 @@ public class ShellTransitionTests extends ShellTestCase { eq(R.styleable.WindowAnimation_activityCloseEnterAnimation), anyBoolean()); } + @Test + public void testTransientHideWithMoveToTop() { + Transitions transitions = createTestTransitions(); + transitions.replaceDefaultHandlerForTest(mDefaultHandler); + final TransitionAnimation transitionAnimation = new TransitionAnimation(mContext, false, + Transitions.TAG); + spyOn(transitionAnimation); + + // Prepare for a TO_BACK transition + final RunningTaskInfo taskInfo = createTaskInfo(1); + final IBinder closeTransition = new Binder(); + final SurfaceControl.Transaction closeTransitionFinishT = + mock(SurfaceControl.Transaction.class); + + // Start a TO_BACK transition + transitions.requestStartTransition(closeTransition, + new TransitionRequestInfo(TRANSIT_TO_BACK, null /* trigger */, null /* remote */)); + TransitionInfo closeInfo = new TransitionInfoBuilder(TRANSIT_TO_BACK) + .addChange(TRANSIT_TO_BACK, taskInfo) + .build(); + transitions.onTransitionReady(closeTransition, closeInfo, new StubTransaction(), + closeTransitionFinishT); + + // Verify that the transition hides the task surface in the finish transaction + verify(closeTransitionFinishT).hide(any()); + + // Prepare for a CHANGE transition + final IBinder changeTransition = new Binder(); + final SurfaceControl.Transaction changeTransitionFinishT = + mock(SurfaceControl.Transaction.class); + + // Start a CHANGE transition w/ MOVE_TO_FRONT that is merged into the TO_BACK + mDefaultHandler.setShouldMerge(changeTransition); + transitions.requestStartTransition(changeTransition, + new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */, null /* remote */)); + TransitionInfo changeInfo = new TransitionInfoBuilder(TRANSIT_OPEN) + .addChange(TRANSIT_CHANGE, FLAG_MOVED_TO_TOP, taskInfo) + .build(); + transitions.onTransitionReady(changeTransition, changeInfo, new StubTransaction(), + changeTransitionFinishT); + + // Verify that the transition shows the task surface in the finish transaction so that the + // when the original transition finishes, the finish transaction does not clobber the + // visibility of the merged transition + verify(changeTransitionFinishT).show(any()); + } + class TestTransitionHandler implements Transitions.TransitionHandler { ArrayList<Pair<IBinder, Transitions.TransitionFinishCallback>> mFinishes = new ArrayList<>(); 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 f7b9c3352dea..dd777a5ed270 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 @@ -1916,6 +1916,7 @@ public class DesktopModeWindowDecorationTests extends ShellTestCase { .setTaskDescriptionBuilder(taskDescriptionBuilder) .setVisible(visible) .build(); + taskInfo.isVisibleRequested = visible; taskInfo.realActivity = new ComponentName("com.android.wm.shell.windowdecor", "DesktopModeWindowDecorationTests"); taskInfo.baseActivity = new ComponentName("com.android.wm.shell.windowdecor", diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositionerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositionerTest.kt index 0798613ed632..24a46aacde15 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositionerTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/MultiDisplayVeiledResizeTaskPositionerTest.kt @@ -63,6 +63,7 @@ import org.mockito.Mockito.mock import org.mockito.Mockito.never import org.mockito.Mockito.times import org.mockito.Mockito.verify +import org.mockito.Mockito.verifyNoInteractions import org.mockito.Mockito.`when` import org.mockito.Mockito.`when` as whenever import org.mockito.MockitoAnnotations @@ -210,6 +211,7 @@ class MultiDisplayVeiledResizeTaskPositionerTest : ShellTestCase() { eq(taskPositioner), ) verify(mockDesktopWindowDecoration, never()).hideResizeVeil() + verifyNoInteractions(mockMultiDisplayDragMoveIndicatorController) } @Test @@ -248,6 +250,7 @@ class MultiDisplayVeiledResizeTaskPositionerTest : ShellTestCase() { verify(mockDesktopWindowDecoration, never()).showResizeVeil(any()) verify(mockDesktopWindowDecoration, never()).hideResizeVeil() + verify(mockMultiDisplayDragMoveIndicatorController).onDragEnd(eq(TASK_ID), any()) Assert.assertEquals(rectAfterEnd, endBounds) } @@ -268,6 +271,7 @@ class MultiDisplayVeiledResizeTaskPositionerTest : ShellTestCase() { verify(spyDisplayLayout0, never()).localPxToGlobalDp(any(), any()) verify(spyDisplayLayout0, never()).globalDpToLocalPx(any(), any()) + verify(mockMultiDisplayDragMoveIndicatorController).onDragEnd(eq(TASK_ID), any()) } @Test @@ -290,6 +294,7 @@ class MultiDisplayVeiledResizeTaskPositionerTest : ShellTestCase() { verify(mockDesktopWindowDecoration, never()).showResizeVeil(any()) verify(mockDesktopWindowDecoration, never()).hideResizeVeil() + verify(mockMultiDisplayDragMoveIndicatorController).onDragEnd(eq(TASK_ID), any()) Assert.assertEquals(rectAfterEnd, endBounds) } @@ -346,6 +351,7 @@ class MultiDisplayVeiledResizeTaskPositionerTest : ShellTestCase() { }, eq(taskPositioner), ) + verifyNoInteractions(mockMultiDisplayDragMoveIndicatorController) } @Test diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolderTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolderTest.kt index bc4865a07f7f..2c3009cb8dc4 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolderTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/viewholder/AppHandleViewHolderTest.kt @@ -78,7 +78,8 @@ class AppHandleViewHolderTest : ShellTestCase() { position = captionPosition, width = CAPTION_WIDTH, height = CAPTION_HEIGHT, - showInputLayer = false + showInputLayer = false, + isCaptionVisible = true ) ) diff --git a/libs/androidfw/CursorWindow.cpp b/libs/androidfw/CursorWindow.cpp index a592749c5398..6e11d430c5ea 100644 --- a/libs/androidfw/CursorWindow.cpp +++ b/libs/androidfw/CursorWindow.cpp @@ -55,7 +55,7 @@ status_t CursorWindow::create(const String8 &name, size_t inflatedSize, CursorWi window->mName = name; window->mSize = std::min(kInlineSize, inflatedSize); window->mInflatedSize = inflatedSize; - window->mData = malloc(window->mSize); + window->mData = calloc(window->mSize, 1); if (!window->mData) goto fail; window->mReadOnly = false; diff --git a/libs/hostgraphics/include/gui/BufferItemConsumer.h b/libs/hostgraphics/include/gui/BufferItemConsumer.h index 5c96c82e061c..b9ff0a774805 100644 --- a/libs/hostgraphics/include/gui/BufferItemConsumer.h +++ b/libs/hostgraphics/include/gui/BufferItemConsumer.h @@ -48,6 +48,10 @@ public: return mConsumer->acquireBuffer(item, presentWhen, 0); } + status_t attachBuffer(BufferItem*, const sp<GraphicBuffer>&) { + return INVALID_OPERATION; + } + status_t releaseBuffer(const BufferItem& item, const sp<Fence>& releaseFence = Fence::NO_FENCE) { return OK; diff --git a/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp index a67aea466c1c..0cd9c53c830f 100644 --- a/libs/hwui/renderthread/VulkanManager.cpp +++ b/libs/hwui/renderthread/VulkanManager.cpp @@ -238,6 +238,7 @@ void VulkanManager::setupDevice(skgpu::VulkanExtensions& grExtensions, for (uint32_t i = 0; i < queueCount; i++) { queuePriorityProps[i].sType = VK_STRUCTURE_TYPE_QUEUE_FAMILY_GLOBAL_PRIORITY_PROPERTIES_EXT; queuePriorityProps[i].pNext = nullptr; + queueProps[i].sType = VK_STRUCTURE_TYPE_QUEUE_FAMILY_PROPERTIES_2; queueProps[i].pNext = &queuePriorityProps[i]; } mGetPhysicalDeviceQueueFamilyProperties2(mPhysicalDevice, &queueCount, queueProps.get()); diff --git a/media/java/android/media/MediaCodecInfo.java b/media/java/android/media/MediaCodecInfo.java index f3b21bfdaa3c..3b560b7a880e 100644 --- a/media/java/android/media/MediaCodecInfo.java +++ b/media/java/android/media/MediaCodecInfo.java @@ -219,13 +219,14 @@ public final class MediaCodecInfo { private static final int DEFAULT_MAX_SUPPORTED_INSTANCES = 32; private static final int MAX_SUPPORTED_INSTANCES_LIMIT = 256; - private static final class LazyHolder { - private static final Range<Integer> SIZE_RANGE = Process.is64Bit() - ? Range.create(1, 32768) - : Range.create(1, MediaProperties.resolution_limit_32bit().orElse(4096)); - } - private static Range<Integer> getSizeRange() { - return LazyHolder.SIZE_RANGE; + private static Range<Integer> SIZE_RANGE; + private static synchronized Range<Integer> getSizeRange() { + if (SIZE_RANGE == null) { + SIZE_RANGE = Process.is64Bit() + ? Range.create(1, 32768) + : Range.create(1, MediaProperties.resolution_limit_32bit().orElse(4096)); + } + return SIZE_RANGE; } // found stuff that is not supported by framework (=> this should not happen) diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java index 7af78b81cda5..03bcc6afc1b7 100644 --- a/media/java/android/media/MediaRecorder.java +++ b/media/java/android/media/MediaRecorder.java @@ -1299,6 +1299,7 @@ public class MediaRecorder implements AudioRouting, * start() or before setOutputFormat(). * @throws IOException if prepare fails otherwise. */ + @RequiresPermission(value = android.Manifest.permission.RECORD_AUDIO, conditional = true) public void prepare() throws IllegalStateException, IOException { if (mPath != null) { @@ -1337,6 +1338,7 @@ public class MediaRecorder implements AudioRouting, * @throws IllegalStateException if it is called before * prepare() or when the camera is already in use by another app. */ + @RequiresPermission(value = android.Manifest.permission.RECORD_AUDIO, conditional = true) public native void start() throws IllegalStateException; /** diff --git a/media/java/android/media/quality/MediaQualityContract.java b/media/java/android/media/quality/MediaQualityContract.java index fccdba8e727f..ece87a66556f 100644 --- a/media/java/android/media/quality/MediaQualityContract.java +++ b/media/java/android/media/quality/MediaQualityContract.java @@ -72,6 +72,43 @@ public class MediaQualityContract { */ public static final String LEVEL_OFF = "level_off"; + /** @hide */ + @Retention(RetentionPolicy.SOURCE) + @StringDef(prefix = "COLOR_TEMP", value = { + COLOR_TEMP_USER, + COLOR_TEMP_COOL, + COLOR_TEMP_STANDARD, + COLOR_TEMP_WARM, + COLOR_TEMP_USER_HDR10PLUS, + COLOR_TEMP_COOL_HDR10PLUS, + COLOR_TEMP_STANDARD_HDR10PLUS, + COLOR_TEMP_WARM_HDR10PLUS, + COLOR_TEMP_FMMSDR, + COLOR_TEMP_FMMHDR, + }) + public @interface ColorTempValue {} + + /** @hide */ + public static final String COLOR_TEMP_USER = "color_temp_user"; + /** @hide */ + public static final String COLOR_TEMP_COOL = "color_temp_cool"; + /** @hide */ + public static final String COLOR_TEMP_STANDARD = "color_temp_standard"; + /** @hide */ + public static final String COLOR_TEMP_WARM = "color_temp_warm"; + /** @hide */ + public static final String COLOR_TEMP_USER_HDR10PLUS = "color_temp_user_hdr10plus"; + /** @hide */ + public static final String COLOR_TEMP_COOL_HDR10PLUS = "color_temp_cool_hdr10plus"; + /** @hide */ + public static final String COLOR_TEMP_STANDARD_HDR10PLUS = "color_temp_standard_hdr10plus"; + /** @hide */ + public static final String COLOR_TEMP_WARM_HDR10PLUS = "color_temp_warm_hdr10plus"; + /** @hide */ + public static final String COLOR_TEMP_FMMSDR = "color_temp_fmmsdr"; + /** @hide */ + public static final String COLOR_TEMP_FMMHDR = "color_temp_fmmhdr"; + /** * @hide @@ -82,7 +119,6 @@ public class MediaQualityContract { String PARAMETER_NAME = "_name"; String PARAMETER_PACKAGE = "_package"; String PARAMETER_INPUT_ID = "_input_id"; - String VENDOR_PARAMETERS = "_vendor_parameters"; } /** diff --git a/media/tests/projection/Android.bp b/media/tests/projection/Android.bp index 94db2c02eb28..48621e4e2094 100644 --- a/media/tests/projection/Android.bp +++ b/media/tests/projection/Android.bp @@ -16,7 +16,6 @@ android_test { name: "MediaProjectionTests", srcs: ["**/*.java"], - libs: [ "android.test.base.stubs.system", "android.test.mock.stubs.system", @@ -30,6 +29,7 @@ android_test { "frameworks-base-testutils", "mockito-target-extended-minus-junit4", "platform-test-annotations", + "cts-mediaprojection-common", "testng", "testables", "truth", @@ -42,7 +42,11 @@ android_test { "libstaticjvmtiagent", ], - test_suites: ["device-tests"], + data: [ + ":CtsMediaProjectionTestCasesHelperApp", + ], + + test_suites: ["general-tests"], platform_apis: true, diff --git a/media/tests/projection/AndroidManifest.xml b/media/tests/projection/AndroidManifest.xml index 0c9760400ce0..514fb5f689c9 100644 --- a/media/tests/projection/AndroidManifest.xml +++ b/media/tests/projection/AndroidManifest.xml @@ -20,6 +20,8 @@ android:sharedUserId="com.android.uid.test"> <uses-permission android:name="android.permission.READ_COMPAT_CHANGE_CONFIG" /> <uses-permission android:name="android.permission.MANAGE_MEDIA_PROJECTION" /> + <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> + <uses-permission android:name="android.permission.READ_PHONE_STATE" /> <application android:debuggable="true" android:testOnly="true"> diff --git a/media/tests/projection/AndroidTest.xml b/media/tests/projection/AndroidTest.xml index f64930a0eb3f..99b42d1cd263 100644 --- a/media/tests/projection/AndroidTest.xml +++ b/media/tests/projection/AndroidTest.xml @@ -22,6 +22,15 @@ <option name="install-arg" value="-t" /> <option name="test-file-name" value="MediaProjectionTests.apk" /> </target_preparer> + <target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller"> + <option name="cleanup-apks" value="true" /> + <option name="force-install-mode" value="FULL"/>ss + <option name="test-file-name" value="CtsMediaProjectionTestCasesHelperApp.apk" /> + </target_preparer> + <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer"> + <option name="run-command" value="input keyevent KEYCODE_WAKEUP" /> + <option name="run-command" value="wm dismiss-keyguard" /> + </target_preparer> <option name="test-tag" value="MediaProjectionTests" /> <test class="com.android.tradefed.testtype.AndroidJUnitTest"> diff --git a/media/tests/projection/src/android/media/projection/MediaProjectionStoppingTest.java b/media/tests/projection/src/android/media/projection/MediaProjectionStoppingTest.java new file mode 100644 index 000000000000..0b84e01c4d02 --- /dev/null +++ b/media/tests/projection/src/android/media/projection/MediaProjectionStoppingTest.java @@ -0,0 +1,293 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.media.projection; + +import static com.android.compatibility.common.util.FeatureUtil.isAutomotive; +import static com.android.compatibility.common.util.FeatureUtil.isTV; +import static com.android.compatibility.common.util.FeatureUtil.isWatch; +import static com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity; + +import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth.assertWithMessage; + +import static org.junit.Assert.fail; +import static org.junit.Assume.assumeFalse; +import static org.junit.Assume.assumeTrue; + +import android.Manifest; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.media.cts.MediaProjectionRule; +import android.os.UserHandle; +import android.platform.test.annotations.RequiresFlagsDisabled; +import android.platform.test.annotations.RequiresFlagsEnabled; +import android.telecom.TelecomManager; +import android.telephony.TelephonyCallback; +import android.telephony.TelephonyManager; +import android.util.Log; + +import androidx.test.platform.app.InstrumentationRegistry; +import androidx.test.uiautomator.By; +import androidx.test.uiautomator.UiDevice; +import androidx.test.uiautomator.Until; + +import com.android.compatibility.common.util.ApiTest; +import com.android.compatibility.common.util.FrameworkSpecificTest; +import com.android.media.projection.flags.Flags; + +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; + +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; + +/** + * Test {@link MediaProjection} stopping behavior. + * + * Run with: + * atest MediaProjectionTests:MediaProjectionStoppingTest + */ +@FrameworkSpecificTest +public class MediaProjectionStoppingTest { + private static final String TAG = "MediaProjectionStoppingTest"; + private static final int STOP_DIALOG_WAIT_TIMEOUT_MS = 5000; + private static final String CALL_HELPER_START_CALL = "start_call"; + private static final String CALL_HELPER_STOP_CALL = "stop_call"; + private static final String STOP_DIALOG_TITLE_RES_ID = "android:id/alertTitle"; + private static final String STOP_DIALOG_CLOSE_BUTTON_RES_ID = "android:id/button2"; + + @Rule public MediaProjectionRule mMediaProjectionRule = new MediaProjectionRule(); + + private Context mContext; + private int mTimeoutMs; + private TelecomManager mTelecomManager; + private TelephonyManager mTelephonyManager; + private TestCallStateListener mTestCallStateListener; + + @Before + public void setUp() throws InterruptedException { + mContext = InstrumentationRegistry.getInstrumentation().getContext(); + runWithShellPermissionIdentity( + () -> { + mContext.getPackageManager() + .revokeRuntimePermission( + mContext.getPackageName(), + Manifest.permission.SYSTEM_ALERT_WINDOW, + new UserHandle(mContext.getUserId())); + }); + mTimeoutMs = 1000; + + mTestCallStateListener = new TestCallStateListener(mContext); + } + + @After + public void cleanup() { + mTestCallStateListener.release(); + } + + @Test + @RequiresFlagsEnabled(Flags.FLAG_STOP_MEDIA_PROJECTION_ON_CALL_END) + @ApiTest(apis = "android.media.projection.MediaProjection.Callback#onStop") + public void testMediaProjectionStop_callStartedAfterMediaProjection_doesNotStop() + throws Exception { + assumeTrue(mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELECOM)); + + mMediaProjectionRule.startMediaProjection(); + + CountDownLatch latch = new CountDownLatch(1); + mMediaProjectionRule.registerCallback( + new MediaProjection.Callback() { + @Override + public void onStop() { + latch.countDown(); + } + }); + mMediaProjectionRule.createVirtualDisplay(); + + try { + startPhoneCall(); + } finally { + endPhoneCall(); + } + + assertWithMessage("MediaProjection should not be stopped on call end") + .that(latch.await(mTimeoutMs, TimeUnit.MILLISECONDS)).isFalse(); + } + + @Test + @RequiresFlagsEnabled(Flags.FLAG_STOP_MEDIA_PROJECTION_ON_CALL_END) + @RequiresFlagsDisabled(Flags.FLAG_SHOW_STOP_DIALOG_POST_CALL_END) + @ApiTest(apis = "android.media.projection.MediaProjection.Callback#onStop") + public void + testMediaProjectionStop_callStartedBeforeMediaProjection_stopDialogFlagDisabled__shouldStop() + throws Exception { + assumeTrue(mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELECOM)); + CountDownLatch latch = new CountDownLatch(1); + try { + startPhoneCall(); + + mMediaProjectionRule.startMediaProjection(); + + mMediaProjectionRule.registerCallback( + new MediaProjection.Callback() { + @Override + public void onStop() { + latch.countDown(); + } + }); + mMediaProjectionRule.createVirtualDisplay(); + + } finally { + endPhoneCall(); + } + + assertWithMessage("MediaProjection was not stopped after call end") + .that(latch.await(mTimeoutMs, TimeUnit.MILLISECONDS)).isTrue(); + } + + @Test + @RequiresFlagsEnabled({ + Flags.FLAG_STOP_MEDIA_PROJECTION_ON_CALL_END, + Flags.FLAG_SHOW_STOP_DIALOG_POST_CALL_END + }) + public void + callEnds_mediaProjectionStartedDuringCallAndIsActive_stopDialogFlagEnabled_showsStopDialog() + throws Exception { + // MediaProjection stop Dialog is only available on phones. + assumeFalse(isWatch()); + assumeFalse(isAutomotive()); + assumeFalse(isTV()); + + assumeTrue(mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELECOM)); + + try { + startPhoneCall(); + mMediaProjectionRule.startMediaProjection(); + + mMediaProjectionRule.registerCallback( + new MediaProjection.Callback() { + @Override + public void onStop() { + fail( + "MediaProjection should not be stopped when" + + " FLAG_SHOW_STOP_DIALOG_POST_CALL_END is enabled"); + } + }); + mMediaProjectionRule.createVirtualDisplay(); + + } finally { + endPhoneCall(); + } + + UiDevice device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); + boolean isDialogShown = + device.wait( + Until.hasObject(By.res(STOP_DIALOG_TITLE_RES_ID)), + STOP_DIALOG_WAIT_TIMEOUT_MS); + assertWithMessage("Stop dialog should be visible").that(isDialogShown).isTrue(); + + // Find and click the "Close" button + boolean hasCloseButton = + device.wait( + Until.hasObject(By.res(STOP_DIALOG_CLOSE_BUTTON_RES_ID)), + STOP_DIALOG_WAIT_TIMEOUT_MS); + if (hasCloseButton) { + device.findObject(By.res(STOP_DIALOG_CLOSE_BUTTON_RES_ID)).click(); + Log.d(TAG, "Clicked on 'Close' button to dismiss the stop dialog."); + } else { + fail("Close button not found, unable to dismiss stop dialog."); + } + } + + private void startPhoneCall() throws InterruptedException { + mTestCallStateListener.assertCallState(false); + mContext.startActivity(getCallHelperIntent(CALL_HELPER_START_CALL)); + mTestCallStateListener.waitForNextCallState(true, mTimeoutMs, TimeUnit.MILLISECONDS); + } + + private void endPhoneCall() throws InterruptedException { + mTestCallStateListener.assertCallState(true); + mContext.startActivity(getCallHelperIntent(CALL_HELPER_STOP_CALL)); + mTestCallStateListener.waitForNextCallState(false, mTimeoutMs, TimeUnit.MILLISECONDS); + } + + private Intent getCallHelperIntent(String action) { + return new Intent(action) + .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK) + .setComponent( + new ComponentName( + "android.media.projection.cts.helper", + "android.media.projection.cts.helper.CallHelperActivity")); + } + + private static final class TestCallStateListener extends TelephonyCallback + implements TelephonyCallback.CallStateListener { + private final BlockingQueue<Boolean> mCallStates = new LinkedBlockingQueue<>(); + private final TelecomManager mTelecomManager; + private final TelephonyManager mTelephonyManager; + + private TestCallStateListener(Context context) throws InterruptedException { + mTelecomManager = context.getSystemService(TelecomManager.class); + mTelephonyManager = context.getSystemService(TelephonyManager.class); + mCallStates.offer(isInCall()); + + assertThat(mCallStates.take()).isFalse(); + + runWithShellPermissionIdentity( + () -> + mTelephonyManager.registerTelephonyCallback( + context.getMainExecutor(), this)); + } + + public void release() { + runWithShellPermissionIdentity( + () -> mTelephonyManager.unregisterTelephonyCallback(this)); + } + + @Override + public void onCallStateChanged(int state) { + mCallStates.offer(isInCall()); + } + + public void waitForNextCallState(boolean expectedCallState, long timeout, TimeUnit unit) + throws InterruptedException { + String message = + String.format( + "Call was not %s after timeout", + expectedCallState ? "started" : "ended"); + + boolean value; + do { + value = mCallStates.poll(timeout, unit); + } while (value != expectedCallState); + assertWithMessage(message).that(value).isEqualTo(expectedCallState); + } + + private boolean isInCall() { + return runWithShellPermissionIdentity(mTelecomManager::isInCall); + } + + public void assertCallState(boolean expected) { + assertWithMessage("Unexpected call state").that(isInCall()).isEqualTo(expected); + } + } +} diff --git a/packages/CompanionDeviceManager/res/values-gl/strings.xml b/packages/CompanionDeviceManager/res/values-gl/strings.xml index ce23be8e8fbb..bc9ad8dea004 100644 --- a/packages/CompanionDeviceManager/res/values-gl/strings.xml +++ b/packages/CompanionDeviceManager/res/values-gl/strings.xml @@ -25,7 +25,7 @@ <string name="discovery_mixed" msgid="7071466134150760127">"Bluetooth e wifi"</string> <string name="profile_name_watch" msgid="576290739483672360">"reloxo"</string> <string name="chooser_title_non_profile" msgid="6035023914517087400">"Escolle un dispositivo para que o xestione a aplicación <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong>"</string> - <string name="chooser_title" msgid="2235819929238267637">"Escolle o perfil (<xliff:g id="PROFILE_NAME">%1$s</xliff:g>) que queiras configurar"</string> + <string name="chooser_title" msgid="2235819929238267637">"Escolle o <xliff:g id="PROFILE_NAME">%1$s</xliff:g> que queiras configurar"</string> <string name="single_device_title" msgid="4199861437545438606">"Buscando <xliff:g id="PROFILE_NAME">%1$s</xliff:g>"</string> <string name="summary_watch" msgid="8134580124808507407">"Esta aplicación poderá sincronizar información (por exemplo, o nome de quen chama) e acceder a estes permisos do dispositivo (<xliff:g id="DEVICE_TYPE">%1$s</xliff:g>)"</string> <string name="confirmation_title_glasses" msgid="8288346850537727333">"Queres permitir que <strong><xliff:g id="APP_NAME">%1$s</xliff:g></strong> xestione o dispositivo (<strong><xliff:g id="DEVICE_NAME">%2$s</xliff:g></strong>)?"</string> diff --git a/packages/CredentialManager/wear/AndroidManifest.xml b/packages/CredentialManager/wear/AndroidManifest.xml index b480ac30d2cb..c91bf13bf98e 100644 --- a/packages/CredentialManager/wear/AndroidManifest.xml +++ b/packages/CredentialManager/wear/AndroidManifest.xml @@ -32,7 +32,8 @@ android:dataExtractionRules="@xml/data_extraction_rules" android:fullBackupContent="@xml/backup_rules" android:label="@string/app_name" - android:supportsRtl="true"> + android:supportsRtl="true" + android:theme="@style/Theme.CredentialSelector"> <!-- Activity called by GMS has to be exactly: com.android.credentialmanager.CredentialSelectorActivity --> @@ -42,7 +43,8 @@ android:exported="true" android:label="@string/app_name" android:launchMode="singleTop" - android:permission="android.permission.LAUNCH_CREDENTIAL_SELECTOR" /> + android:permission="android.permission.LAUNCH_CREDENTIAL_SELECTOR" + android:theme="@style/Theme.CredentialSelector"/> </application> </manifest> diff --git a/packages/CredentialManager/wear/res/values/themes.xml b/packages/CredentialManager/wear/res/values/themes.xml new file mode 100644 index 000000000000..22329e9ff2ce --- /dev/null +++ b/packages/CredentialManager/wear/res/values/themes.xml @@ -0,0 +1,24 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2023 The Android Open Source Project + ~ + ~ Licensed under the Apache License, Version 2.0 (the "License"); + ~ you may not use this file except in compliance with the License. + ~ You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, software + ~ distributed under the License is distributed on an "AS IS" BASIS, + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + ~ See the License for the specific language governing permissions and + ~ limitations under the License. + --> +<resources> + <style name="Theme.CredentialSelector" parent="@*android:style/ThemeOverlay.DeviceDefault.Accent.DayNight"> + <item name="android:windowContentOverlay">@null</item> + <item name="android:windowNoTitle">true</item> + <item name="android:windowBackground">@android:color/transparent</item> + <item name="android:windowIsTranslucent">true</item> + </style> +</resources>
\ No newline at end of file diff --git a/packages/SettingsLib/ButtonPreference/src/com/android/settingslib/widget/ButtonPreference.java b/packages/SettingsLib/ButtonPreference/src/com/android/settingslib/widget/ButtonPreference.java index be711accd542..89e5372b530e 100644 --- a/packages/SettingsLib/ButtonPreference/src/com/android/settingslib/widget/ButtonPreference.java +++ b/packages/SettingsLib/ButtonPreference/src/com/android/settingslib/widget/ButtonPreference.java @@ -27,6 +27,7 @@ import android.widget.Button; import android.widget.LinearLayout; import androidx.annotation.GravityInt; +import androidx.annotation.IntDef; import androidx.preference.Preference; import androidx.preference.PreferenceViewHolder; @@ -34,21 +35,46 @@ import com.android.settingslib.widget.preference.button.R; import com.google.android.material.button.MaterialButton; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + /** * A preference handled a button */ public class ButtonPreference extends Preference implements GroupSectionDividerMixin { + public static final int TYPE_FILLED = 0; + public static final int TYPE_TONAL = 1; + public static final int TYPE_OUTLINE = 2; + + @IntDef({TYPE_FILLED, TYPE_TONAL, TYPE_OUTLINE}) + @Retention(RetentionPolicy.SOURCE) + public @interface Type { + } + + public static final int SIZE_NORMAL = 0; + public static final int SIZE_LARGE = 1; + public static final int SIZE_EXTRA_LARGE = 2; + + @IntDef({SIZE_NORMAL, SIZE_LARGE, SIZE_EXTRA_LARGE}) + @Retention(RetentionPolicy.SOURCE) + public @interface Size { + } + enum ButtonStyle { - FILLED_NORMAL(0, 0, R.layout.settingslib_expressive_button_filled), - FILLED_LARGE(0, 1, R.layout.settingslib_expressive_button_filled_large), - FILLED_EXTRA(0, 2, R.layout.settingslib_expressive_button_filled_extra), - TONAL_NORMAL(1, 0, R.layout.settingslib_expressive_button_tonal), - TONAL_LARGE(1, 1, R.layout.settingslib_expressive_button_tonal_large), - TONAL_EXTRA(1, 2, R.layout.settingslib_expressive_button_tonal_extra), - OUTLINE_NORMAL(2, 0, R.layout.settingslib_expressive_button_outline), - OUTLINE_LARGE(2, 1, R.layout.settingslib_expressive_button_outline_large), - OUTLINE_EXTRA(2, 2, R.layout.settingslib_expressive_button_outline_extra); + FILLED_NORMAL(TYPE_FILLED, SIZE_NORMAL, R.layout.settingslib_expressive_button_filled), + FILLED_LARGE(TYPE_FILLED, SIZE_LARGE, R.layout.settingslib_expressive_button_filled_large), + FILLED_EXTRA(TYPE_FILLED, SIZE_EXTRA_LARGE, + R.layout.settingslib_expressive_button_filled_extra), + TONAL_NORMAL(TYPE_TONAL, SIZE_NORMAL, R.layout.settingslib_expressive_button_tonal), + TONAL_LARGE(TYPE_TONAL, SIZE_LARGE, R.layout.settingslib_expressive_button_tonal_large), + TONAL_EXTRA(TYPE_TONAL, SIZE_EXTRA_LARGE, + R.layout.settingslib_expressive_button_tonal_extra), + OUTLINE_NORMAL(TYPE_OUTLINE, SIZE_NORMAL, R.layout.settingslib_expressive_button_outline), + OUTLINE_LARGE(TYPE_OUTLINE, SIZE_LARGE, + R.layout.settingslib_expressive_button_outline_large), + OUTLINE_EXTRA(TYPE_OUTLINE, SIZE_EXTRA_LARGE, + R.layout.settingslib_expressive_button_outline_extra); private final int mType; private final int mSize; @@ -60,7 +86,7 @@ public class ButtonPreference extends Preference implements GroupSectionDividerM this.mLayoutId = layoutId; } - static int getLayoutId(int type, int size) { + static int getLayoutId(@Type int type, @Size int size) { for (ButtonStyle style : values()) { if (style.mType == type && style.mSize == size) { return style.mLayoutId; @@ -266,7 +292,7 @@ public class ButtonPreference extends Preference implements GroupSectionDividerM * <li>2: extra large</li> * </ul> */ - public void setButtonStyle(int type, int size) { + public void setButtonStyle(@Type int type, @Size int size) { int layoutId = ButtonStyle.getLayoutId(type, size); setLayoutResource(layoutId); notifyChanged(); diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v36/settingslib_expressive_collapsing_toolbar_content_layout.xml b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v36/settingslib_expressive_collapsing_toolbar_content_layout.xml index 3db0ac653848..b1e42c0aebff 100644 --- a/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v36/settingslib_expressive_collapsing_toolbar_content_layout.xml +++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/res/layout-v36/settingslib_expressive_collapsing_toolbar_content_layout.xml @@ -35,6 +35,7 @@ android:layout_height="@dimen/settingslib_toolbar_layout_height" app:layout_scrollFlags="scroll|exitUntilCollapsed|snap" app:toolbarId="@id/action_bar" + app:maxLines="2" style="@style/SettingsLibCollapsingToolbarLayoutStyle.Expressive"> <Toolbar @@ -44,7 +45,24 @@ android:layout_marginStart="@dimen/settingslib_expressive_space_extrasmall4" android:theme="?android:attr/actionBarTheme" android:transitionName="shared_element_view" - app:layout_collapseMode="pin"/> + app:layout_collapseMode="pin"> + + <LinearLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:paddingStart="@dimen/settingslib_expressive_space_extrasmall6" + android:layout_marginEnd="@dimen/settingslib_expressive_space_small4" + android:layout_gravity="end"> + + <com.google.android.material.button.MaterialButton + android:id="@+id/action_button" + android:layout_width="wrap_content" + android:layout_height="match_parent" + style="@style/SettingsLibButtonStyle.Expressive.Filled" + android:visibility="gone"/> + </LinearLayout> + + </Toolbar> </com.google.android.material.appbar.CollapsingToolbarLayout> </com.google.android.material.appbar.AppBarLayout> diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarAppCompatActivity.java b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarAppCompatActivity.java index feacecbd5d0c..ec1df447995c 100644 --- a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarAppCompatActivity.java +++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarAppCompatActivity.java @@ -24,6 +24,7 @@ import android.view.View; import android.view.ViewGroup; import android.widget.Toolbar; +import androidx.annotation.DrawableRes; import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity; @@ -132,6 +133,29 @@ public class CollapsingToolbarAppCompatActivity extends AppCompatActivity { setTitle(getText(titleId)); } + /** + * Show/Hide the action button on the Toolbar. + * @param enabled true to show the button, otherwise it's hidden. + */ + public void setActionButtonEnabled(boolean enabled) { + getToolbarDelegate().setActionButtonEnabled(enabled); + } + + /** Set the icon to the action button */ + public void setActionButtonIcon(@DrawableRes int drawableRes) { + getToolbarDelegate().setActionButtonIcon(this, drawableRes); + } + + /** Set the text to the action button */ + public void setActionButtonText(@Nullable CharSequence text) { + getToolbarDelegate().setActionButtonText(text); + } + + /** Set the OnClick listener to the action button */ + public void setActionButtonListener(@Nullable View.OnClickListener listener) { + getToolbarDelegate().setActionButtonOnClickListener(listener); + } + @Override public boolean onSupportNavigateUp() { if (getSupportFragmentManager().getBackStackEntryCount() > 0) { diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java index 0f9d94e9cc3d..b1b8c983b033 100644 --- a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java +++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarBaseActivity.java @@ -25,6 +25,7 @@ import android.view.View; import android.view.ViewGroup; import android.widget.Toolbar; +import androidx.annotation.DrawableRes; import androidx.annotation.Nullable; import androidx.fragment.app.FragmentActivity; @@ -124,6 +125,29 @@ public class CollapsingToolbarBaseActivity extends FragmentActivity { setTitle(getText(titleId)); } + /** + * Show/Hide the action button on the Toolbar. + * @param enabled true to show the button, otherwise it's hidden. + */ + public void setActionButtonEnabled(boolean enabled) { + getToolbarDelegate().setActionButtonEnabled(enabled); + } + + /** Set the icon to the action button */ + public void setActionButtonIcon(@DrawableRes int drawableRes) { + getToolbarDelegate().setActionButtonIcon(this, drawableRes); + } + + /** Set the text to the action button */ + public void setActionButtonText(@Nullable CharSequence text) { + getToolbarDelegate().setActionButtonText(text); + } + + /** Set the OnClick listener to the action button */ + public void setActionButtonListener(@Nullable View.OnClickListener listener) { + getToolbarDelegate().setActionButtonOnClickListener(listener); + } + @Override public boolean onNavigateUp() { if (getSupportFragmentManager().getBackStackEntryCount() > 0) { diff --git a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarDelegate.java b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarDelegate.java index de0d60916b3d..072b36549093 100644 --- a/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarDelegate.java +++ b/packages/SettingsLib/CollapsingToolbarBaseActivity/src/com/android/settingslib/collapsingtoolbar/CollapsingToolbarDelegate.java @@ -33,6 +33,7 @@ import android.view.ViewGroup; import android.widget.FrameLayout; import android.widget.Toolbar; +import androidx.annotation.DrawableRes; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity; @@ -42,6 +43,7 @@ import com.android.settingslib.widget.SettingsThemeHelper; import com.google.android.material.appbar.AppBarLayout; import com.google.android.material.appbar.CollapsingToolbarLayout; +import com.google.android.material.button.MaterialButton; /** * A delegate that allows to use the collapsing toolbar layout in hosts that doesn't want/need to @@ -80,6 +82,8 @@ public class CollapsingToolbarDelegate { private AppBarLayout mAppBarLayout; @NonNull private Toolbar mToolbar; + @Nullable + private MaterialButton mActionButton; @NonNull private FrameLayout mContentFrameLayout; @NonNull @@ -154,6 +158,7 @@ public class CollapsingToolbarDelegate { } autoSetCollapsingToolbarLayoutScrolling(); mContentFrameLayout = view.findViewById(R.id.content_frame); + mActionButton = view.findViewById(R.id.action_button); if (activity instanceof AppCompatActivity) { Log.d(TAG, "onCreateView: from AppCompatActivity and sub-class."); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { @@ -215,6 +220,42 @@ public class CollapsingToolbarDelegate { } } + /** + * Show/Hide the action button on the Toolbar. + * @param enabled true to show the button, otherwise it's hidden. + */ + public void setActionButtonEnabled(boolean enabled) { + if (mActionButton == null) { + return; + } + int visibility = enabled ? View.VISIBLE : View.GONE; + mActionButton.setVisibility(visibility); + } + + /** Set the icon to the action button */ + public void setActionButtonIcon(@NonNull Context context, @DrawableRes int drawableRes) { + if (mActionButton == null) { + return; + } + mActionButton.setIcon(context.getResources().getDrawable(drawableRes, context.getTheme())); + } + + /** Set the text to the action button */ + public void setActionButtonText(@Nullable CharSequence text) { + if (mActionButton == null) { + return; + } + mActionButton.setText(text); + } + + /** Set the OnClick listener to the action button */ + public void setActionButtonOnClickListener(@Nullable View.OnClickListener listener) { + if (mActionButton == null) { + return; + } + mActionButton.setOnClickListener(listener); + } + /** Return an instance of CoordinatorLayout. */ @Nullable public CoordinatorLayout getCoordinatorLayout() { diff --git a/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsPreferenceGroupAdapter.kt b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsPreferenceGroupAdapter.kt index 2672787a0519..d1c88de3f399 100644 --- a/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsPreferenceGroupAdapter.kt +++ b/packages/SettingsLib/SettingsTheme/src/com/android/settingslib/widget/SettingsPreferenceGroupAdapter.kt @@ -71,18 +71,27 @@ open class SettingsPreferenceGroupAdapter(preferenceGroup: PreferenceGroup) : override fun onPreferenceHierarchyChange(preference: Preference) { super.onPreferenceHierarchyChange(preference) - // Post after super class has posted their sync runnable to update preferences. - mHandler.removeCallbacks(syncRunnable) - mHandler.post(syncRunnable) + if (SettingsThemeHelper.isExpressiveTheme(preference.context)) { + // Post after super class has posted their sync runnable to update preferences. + mHandler.removeCallbacks(syncRunnable) + mHandler.post(syncRunnable) + } } @SuppressLint("RestrictedApi") override fun onBindViewHolder(holder: PreferenceViewHolder, position: Int) { super.onBindViewHolder(holder, position) - updateBackground(holder, position) + + if (SettingsThemeHelper.isExpressiveTheme(holder.itemView.context)) { + updateBackground(holder, position) + } } private fun updatePreferencesList() { + if (!SettingsThemeHelper.isExpressiveTheme(mPreferenceGroup.context)) { + return + } + val oldList = ArrayList(mRoundCornerMappingList) mRoundCornerMappingList = ArrayList() mappingPreferenceGroup(mRoundCornerMappingList, mPreferenceGroup) diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Category.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Category.kt index f10f96afd389..395328f86047 100644 --- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Category.kt +++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/ui/Category.kt @@ -78,6 +78,8 @@ fun CategoryTitle(title: String) { /** * A container that is used to group similar items. A [Category] displays a [CategoryTitle] and * visually separates groups of items. + * + * @param content The content of the category. */ @Composable fun Category( @@ -126,7 +128,8 @@ fun Category( * be decided by the index. * @param bottomPadding Optional. Bottom outside padding of the category. * @param state Optional. State of LazyList. - * @param content Optional. Content to be shown at the top of the category. + * @param footer Optional. Content to be shown at the bottom of the category. + * @param header Optional. Content to be shown at the top of the category. */ @Composable fun LazyCategory( @@ -136,7 +139,8 @@ fun LazyCategory( title: ((Int) -> String?)? = null, bottomPadding: Dp = SettingsDimension.paddingSmall, state: LazyListState = rememberLazyListState(), - content: @Composable () -> Unit, + footer: @Composable () -> Unit = {}, + header: @Composable () -> Unit, ) { Column( Modifier.padding( @@ -154,12 +158,14 @@ fun LazyCategory( verticalArrangement = Arrangement.spacedBy(SettingsDimension.paddingTiny), state = state, ) { - item { CompositionLocalProvider(LocalIsInCategory provides true) { content() } } + item { CompositionLocalProvider(LocalIsInCategory provides true) { header() } } items(count = list.size, key = key) { title?.invoke(it)?.let { title -> CategoryTitle(title) } CompositionLocalProvider(LocalIsInCategory provides true) { entry(it)() } } + + item { CompositionLocalProvider(LocalIsInCategory provides true) { footer() } } } } } @@ -189,3 +195,28 @@ private fun CategoryPreview() { } } } + +@Preview +@Composable +private fun LazyCategoryPreview() { + SettingsTheme { + LazyCategory( + list = listOf(1, 2, 3), + entry = { key -> + @Composable { + Preference( + object : PreferenceModel { + override val title = key.toString() + } + ) + } + }, + footer = @Composable { + Footer("Footer") + }, + header = @Composable { + Text("Header") + }, + ) + } +} diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/ui/CategoryTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/ui/CategoryTest.kt index 4b4a8c20b39e..7d199511044a 100644 --- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/ui/CategoryTest.kt +++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/ui/CategoryTest.kt @@ -71,10 +71,17 @@ class CategoryTest { } @Test - fun lazyCategory_content_displayed() { + fun lazyCategory_headerDisplayed() { composeTestRule.setContent { TestLazyCategory() } - composeTestRule.onNodeWithText("text").assertExists() + composeTestRule.onNodeWithText("Header").assertExists() + } + + @Test + fun lazyCategory_footerDisplayed() { + composeTestRule.setContent { TestLazyCategory() } + + composeTestRule.onNodeWithText("Footer").assertExists() } @Test @@ -102,8 +109,8 @@ private fun TestLazyCategory() { list = list, entry = { index: Int -> @Composable { Preference(list[index]) } }, title = { index: Int -> if (index == 0) "LazyCategory $index" else null }, - ) { - Text("text") - } + footer = @Composable { Footer("Footer") }, + header = @Composable { Text("Header") }, + ) } } diff --git a/packages/SettingsLib/StatusBannerPreference/res/layout/settingslib_expressive_preference_statusbanner.xml b/packages/SettingsLib/StatusBannerPreference/res/layout/settingslib_expressive_preference_statusbanner.xml index 083b862e8a5c..c778fb03e04f 100644 --- a/packages/SettingsLib/StatusBannerPreference/res/layout/settingslib_expressive_preference_statusbanner.xml +++ b/packages/SettingsLib/StatusBannerPreference/res/layout/settingslib_expressive_preference_statusbanner.xml @@ -55,6 +55,27 @@ android:layout_gravity="center" android:scaleType="centerInside"/> + <com.google.android.material.progressindicator.CircularProgressIndicator + android:id="@+id/progress_indicator" + style="@style/Widget.Material3.CircularProgressIndicator" + android:layout_width="@dimen/settingslib_expressive_space_medium4" + android:layout_height="@dimen/settingslib_expressive_space_medium4" + android:layout_gravity="center" + android:scaleType="centerInside" + android:indeterminate="false" + android:max="100" + android:progress="0" + android:visibility="gone" /> + + <com.google.android.material.loadingindicator.LoadingIndicator + android:id="@+id/loading_indicator" + style="@style/Widget.Material3.LoadingIndicator" + android:layout_width="@dimen/settingslib_expressive_space_medium4" + android:layout_height="@dimen/settingslib_expressive_space_medium4" + android:layout_gravity="center" + android:scaleType="centerInside" + android:visibility="gone" /> + </FrameLayout> <LinearLayout diff --git a/packages/SettingsLib/StatusBannerPreference/res/values/attrs.xml b/packages/SettingsLib/StatusBannerPreference/res/values/attrs.xml index deda2586c2e0..bb9a5ad689cd 100644 --- a/packages/SettingsLib/StatusBannerPreference/res/values/attrs.xml +++ b/packages/SettingsLib/StatusBannerPreference/res/values/attrs.xml @@ -22,6 +22,8 @@ <enum name="medium" value="2"/> <enum name="high" value="3"/> <enum name="off" value="4"/> + <enum name="loading_determinate" value="5"/> + <enum name="loading_indeterminate" value="6"/> </attr> <attr name="buttonLevel" format="enum"> <enum name="generic" value="0"/> diff --git a/packages/SettingsLib/StatusBannerPreference/src/com/android/settingslib/widget/StatusBannerPreference.kt b/packages/SettingsLib/StatusBannerPreference/src/com/android/settingslib/widget/StatusBannerPreference.kt index eda281c07053..e6c6638f7de4 100644 --- a/packages/SettingsLib/StatusBannerPreference/src/com/android/settingslib/widget/StatusBannerPreference.kt +++ b/packages/SettingsLib/StatusBannerPreference/src/com/android/settingslib/widget/StatusBannerPreference.kt @@ -28,6 +28,7 @@ import androidx.preference.Preference import androidx.preference.PreferenceViewHolder import com.android.settingslib.widget.preference.statusbanner.R import com.google.android.material.button.MaterialButton +import com.google.android.material.progressindicator.CircularProgressIndicator class StatusBannerPreference @JvmOverloads constructor( context: Context, @@ -41,7 +42,9 @@ class StatusBannerPreference @JvmOverloads constructor( LOW, MEDIUM, HIGH, - OFF + OFF, + LOADING_DETERMINATE, // The loading progress is set by the caller. + LOADING_INDETERMINATE // No loading progress. Just loading animation } var iconLevel: BannerStatus = BannerStatus.GENERIC set(value) { @@ -60,6 +63,8 @@ class StatusBannerPreference @JvmOverloads constructor( } private var listener: View.OnClickListener? = null + private var circularProgressIndicator: CircularProgressIndicator? = null + init { layoutResource = R.layout.settingslib_expressive_preference_statusbanner isSelectable = false @@ -89,6 +94,8 @@ class StatusBannerPreference @JvmOverloads constructor( 2 -> BannerStatus.MEDIUM 3 -> BannerStatus.HIGH 4 -> BannerStatus.OFF + 5 -> BannerStatus.LOADING_DETERMINATE + 6 -> BannerStatus.LOADING_INDETERMINATE else -> BannerStatus.GENERIC } @@ -102,7 +109,38 @@ class StatusBannerPreference @JvmOverloads constructor( } holder.findViewById(android.R.id.icon_frame)?.apply { - visibility = if (icon != null) View.VISIBLE else View.GONE + visibility = + if ( + icon != null || iconLevel == BannerStatus.LOADING_DETERMINATE || + iconLevel == BannerStatus.LOADING_INDETERMINATE + ) + View.VISIBLE + else View.GONE + } + + holder.findViewById(android.R.id.icon)?.apply { + visibility = + if (iconLevel == BannerStatus.LOADING_DETERMINATE || + iconLevel == BannerStatus.LOADING_INDETERMINATE) + View.GONE + else View.VISIBLE + } + + circularProgressIndicator = holder.findViewById(R.id.progress_indicator) + as? CircularProgressIndicator + + (circularProgressIndicator)?.apply { + visibility = + if (iconLevel == BannerStatus.LOADING_DETERMINATE) + View.VISIBLE + else View.GONE + } + + holder.findViewById(R.id.loading_indicator)?.apply { + visibility = + if (iconLevel == BannerStatus.LOADING_INDETERMINATE) + View.VISIBLE + else View.GONE } (holder.findViewById(R.id.status_banner_button) as? MaterialButton)?.apply { @@ -116,6 +154,10 @@ class StatusBannerPreference @JvmOverloads constructor( } } + fun getProgressIndicator(): CircularProgressIndicator? { + return circularProgressIndicator + } + /** * Sets the text to be displayed in button. */ @@ -203,7 +245,7 @@ class StatusBannerPreference @JvmOverloads constructor( R.drawable.settingslib_expressive_background_level_high ) - // GENERIC and OFF are using the same background drawable. + // Using the same background drawable for other levels. else -> ContextCompat.getDrawable( context, R.drawable.settingslib_expressive_background_generic diff --git a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java index 7f4bebcf4a62..335526632383 100644 --- a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java +++ b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/Tile.java @@ -46,6 +46,8 @@ import android.os.UserHandle; import android.text.TextUtils; import android.util.Log; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import java.util.ArrayList; @@ -326,6 +328,15 @@ public abstract class Tile implements Parcelable { return false; } + /** Returns the icon color scheme. */ + @Nullable + public String getIconColorScheme(@NonNull Context context) { + ensureMetadataNotStale(context); + return mMetaData != null + ? mMetaData.getString(TileUtils.META_DATA_PREFERENCE_ICON_COLOR_SCHEME, null) + : null; + } + /** Whether the {@link Activity} should be launched in a separate task. */ public boolean isNewTask() { if (mMetaData != null && mMetaData.containsKey(META_DATA_NEW_TASK)) { diff --git a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java index ac0b9b45aba6..d62ed2f60ed0 100644 --- a/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java +++ b/packages/SettingsLib/Tile/src/com/android/settingslib/drawer/TileUtils.java @@ -135,6 +135,13 @@ public class TileUtils { public static final String META_DATA_PREFERENCE_ICON = "com.android.settings.icon"; /** + * Name of the meta-data item that should be set in the AndroidManifest.xml to specify the icon + * color scheme. Only available for preferences on the homepage. + */ + public static final String META_DATA_PREFERENCE_ICON_COLOR_SCHEME = + "com.android.settings.icon_color_scheme"; + + /** * Name of the meta-data item that should be set in the AndroidManifest.xml * to specify the icon background color. The value may or may not be used by Settings app. */ diff --git a/packages/SettingsLib/aconfig/settingslib.aconfig b/packages/SettingsLib/aconfig/settingslib.aconfig index 349d13a29b05..90e5a010416c 100644 --- a/packages/SettingsLib/aconfig/settingslib.aconfig +++ b/packages/SettingsLib/aconfig/settingslib.aconfig @@ -37,16 +37,6 @@ flag { } flag { - name: "enable_set_preferred_transport_for_le_audio_device" - namespace: "bluetooth" - description: "Enable setting preferred transport for Le Audio device" - bug: "330581926" - metadata { - purpose: PURPOSE_BUGFIX - } -} - -flag { name: "enable_determining_advanced_details_header_with_metadata" namespace: "pixel_cross_device_control" description: "Use metadata instead of device type to determine whether a bluetooth device should use advanced details header." diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml index 58ddc723b77a..f1859f2fbd6a 100644 --- a/packages/SettingsLib/res/values-af/strings.xml +++ b/packages/SettingsLib/res/values-af/strings.xml @@ -539,8 +539,7 @@ <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Beheer deur Beperkte Instellings"</string> <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Onbeskikbaar tydens oproepe"</string> <string name="disabled" msgid="8017887509554714950">"Gedeaktiveer"</string> - <!-- no translation found for enabled (3997122818554810678) --> - <skip /> + <string name="enabled" msgid="3997122818554810678">"Geaktiveer"</string> <string name="external_source_trusted" msgid="1146522036773132905">"Toegelaat"</string> <string name="external_source_untrusted" msgid="5037891688911672227">"Nie toegelaat nie"</string> <string name="install_other_apps" msgid="3232595082023199454">"Installeer onbekende apps"</string> @@ -612,7 +611,7 @@ <string name="media_transfer_this_phone" msgid="7194341457812151531">"Hierdie foon"</string> <string name="media_transfer_digital_line_name" msgid="312091711951124301">"S/PDIF"</string> <string name="media_transfer_analog_line_name" msgid="1841163866716302104">"Analoog"</string> - <string name="media_transfer_aux_line_name" msgid="894135835967856689">"AANVULLENDE"</string> + <string name="media_transfer_aux_line_name" msgid="894135835967856689">"Aanvullend"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Kan nie op hierdie toestel speel nie"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Gradeer rekening op om oor te skakel"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Kan nie aflaaie hier speel nie"</string> @@ -628,6 +627,8 @@ <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Ingeboude luidspreker"</string> <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"TV-oudio"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Kan nie koppel nie. Skakel toestel af en weer aan"</string> + <!-- no translation found for bluetooth_key_missing_subtext (1003639333895028298) --> + <skip /> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Bedrade oudiotoestel"</string> <string name="help_label" msgid="3528360748637781274">"Hulp en terugvoer"</string> <string name="storage_category" msgid="2287342585424631813">"Berging"</string> diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml index 3844a4b69d86..77009d747bad 100644 --- a/packages/SettingsLib/res/values-am/strings.xml +++ b/packages/SettingsLib/res/values-am/strings.xml @@ -539,8 +539,7 @@ <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"በተገደበ ቅንብር ቁጥጥር የሚደረግበት"</string> <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"በጥሪዎች ጊዜ አይገኝም"</string> <string name="disabled" msgid="8017887509554714950">"ቦዝኗል"</string> - <!-- no translation found for enabled (3997122818554810678) --> - <skip /> + <string name="enabled" msgid="3997122818554810678">"ነቅቷል"</string> <string name="external_source_trusted" msgid="1146522036773132905">"ይፈቀዳል"</string> <string name="external_source_untrusted" msgid="5037891688911672227">"አይፈቀድም"</string> <string name="install_other_apps" msgid="3232595082023199454">"ያልታወቁ መተግበሪያዎችን ይጫኑ"</string> @@ -628,6 +627,8 @@ <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"አብሮ የተሰራ ድምፅ ማውጫ"</string> <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"የTV ኦዲዮ"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"መገናኘት ላይ ችግር። መሳሪያውን ያጥፉት እና እንደገና ያብሩት"</string> + <!-- no translation found for bluetooth_key_missing_subtext (1003639333895028298) --> + <skip /> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"ባለገመድ የኦዲዮ መሣሪያ"</string> <string name="help_label" msgid="3528360748637781274">"እገዛ እና ግብረመልስ"</string> <string name="storage_category" msgid="2287342585424631813">"ማከማቻ"</string> diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml index 1344a25672d2..a522e07baaec 100644 --- a/packages/SettingsLib/res/values-ar/strings.xml +++ b/packages/SettingsLib/res/values-ar/strings.xml @@ -627,6 +627,8 @@ <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"مكبِّر الصوت المُدمَج"</string> <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"صوت التلفزيون"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"حدثت مشكلة أثناء الاتصال. يُرجى إيقاف الجهاز ثم إعادة تشغيله."</string> + <!-- no translation found for bluetooth_key_missing_subtext (1003639333895028298) --> + <skip /> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"جهاز سماعي سلكي"</string> <string name="help_label" msgid="3528360748637781274">"المساعدة والملاحظات"</string> <string name="storage_category" msgid="2287342585424631813">"مساحة التخزين"</string> diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml index 56f73df512a1..0ed118be8ae3 100644 --- a/packages/SettingsLib/res/values-as/strings.xml +++ b/packages/SettingsLib/res/values-as/strings.xml @@ -539,8 +539,7 @@ <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"প্ৰতিবন্ধিত ছেটিঙৰ দ্বাৰা নিয়ন্ত্ৰিত"</string> <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"কল চলি থকাৰ সময়ত উপলব্ধ নহয়"</string> <string name="disabled" msgid="8017887509554714950">"নিষ্ক্ৰিয়"</string> - <!-- no translation found for enabled (3997122818554810678) --> - <skip /> + <string name="enabled" msgid="3997122818554810678">"সক্ষম কৰা আছে"</string> <string name="external_source_trusted" msgid="1146522036773132905">"অনুমতি দিয়া হৈছে"</string> <string name="external_source_untrusted" msgid="5037891688911672227">"অনুমতি দিয়া হোৱা নাই"</string> <string name="install_other_apps" msgid="3232595082023199454">"অজ্ঞাত এপ্ ইনষ্টল কৰক"</string> @@ -612,7 +611,7 @@ <string name="media_transfer_this_phone" msgid="7194341457812151531">"এই ফ’নটো"</string> <string name="media_transfer_digital_line_name" msgid="312091711951124301">"S/PDIF"</string> <string name="media_transfer_analog_line_name" msgid="1841163866716302104">"এনালগ"</string> - <string name="media_transfer_aux_line_name" msgid="894135835967856689">"অক্স"</string> + <string name="media_transfer_aux_line_name" msgid="894135835967856689">"AUX"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"এই ডিভাইচটো প্লে\' কৰিব নোৱাৰি"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"সলনি কৰিবলৈ একাউণ্ট আপগ্ৰে’ড কৰক"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"ইয়াত ডাউনল’ডসমূহ প্লে’ কৰিব নোৱাৰি"</string> @@ -628,6 +627,8 @@ <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"বিল্ট-ইন স্পীকাৰ"</string> <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"টিভিৰ অডিঅ’"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"সংযোগ হোৱাত সমস্যা হৈছে। ডিভাইচটো অফ কৰি পুনৰ অন কৰক"</string> + <!-- no translation found for bluetooth_key_missing_subtext (1003639333895028298) --> + <skip /> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"তাঁৰযুক্ত অডিঅ’ ডিভাইচ"</string> <string name="help_label" msgid="3528360748637781274">"সহায় আৰু মতামত"</string> <string name="storage_category" msgid="2287342585424631813">"ষ্ট’ৰেজ"</string> diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml index 878d8972e9a8..6ba40f7fcbac 100644 --- a/packages/SettingsLib/res/values-az/strings.xml +++ b/packages/SettingsLib/res/values-az/strings.xml @@ -627,6 +627,8 @@ <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Daxili dinamik"</string> <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"TV audiosu"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Qoşulmaqla bağlı problem. Cihazı deaktiv edin, sonra yenidən aktiv edin"</string> + <!-- no translation found for bluetooth_key_missing_subtext (1003639333895028298) --> + <skip /> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Simli audio cihaz"</string> <string name="help_label" msgid="3528360748637781274">"Yardım və rəy"</string> <string name="storage_category" msgid="2287342585424631813">"Yaddaş"</string> diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml index 97873e5a2d2a..0ebeb554c758 100644 --- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml +++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml @@ -539,8 +539,7 @@ <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Kontrolišu ograničena podešavanja"</string> <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Nedostupno tokom poziva"</string> <string name="disabled" msgid="8017887509554714950">"Onemogućeno"</string> - <!-- no translation found for enabled (3997122818554810678) --> - <skip /> + <string name="enabled" msgid="3997122818554810678">"Omogućeno"</string> <string name="external_source_trusted" msgid="1146522036773132905">"Dozvoljeno"</string> <string name="external_source_untrusted" msgid="5037891688911672227">"Nije dozvoljeno"</string> <string name="install_other_apps" msgid="3232595082023199454">"Instaliranje nepoznatih aplikacija"</string> @@ -628,6 +627,8 @@ <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Ugrađeni zvučnik"</string> <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"Zvuk TV-a"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problem pri povezivanju. Isključite uređaj, pa ga ponovo uključite"</string> + <!-- no translation found for bluetooth_key_missing_subtext (1003639333895028298) --> + <skip /> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Žičani audio uređaj"</string> <string name="help_label" msgid="3528360748637781274">"Pomoć i povratne informacije"</string> <string name="storage_category" msgid="2287342585424631813">"Memorijski prostor"</string> diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml index 78a5eaf388f0..8e1fd28ee242 100644 --- a/packages/SettingsLib/res/values-be/strings.xml +++ b/packages/SettingsLib/res/values-be/strings.xml @@ -539,8 +539,7 @@ <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Пад кіраваннем Абмежаванага наладжвання"</string> <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Недаступна падчас выклікаў"</string> <string name="disabled" msgid="8017887509554714950">"Адключанае"</string> - <!-- no translation found for enabled (3997122818554810678) --> - <skip /> + <string name="enabled" msgid="3997122818554810678">"Уключана"</string> <string name="external_source_trusted" msgid="1146522036773132905">"Дазволена"</string> <string name="external_source_untrusted" msgid="5037891688911672227">"Забаронена"</string> <string name="install_other_apps" msgid="3232595082023199454">"Усталёўка невядомых праграм"</string> @@ -628,6 +627,8 @@ <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Убудаваны дынамік"</string> <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"Аўдыя тэлевізара"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Праблема з падключэннем. Выключыце і зноў уключыце прыладу"</string> + <!-- no translation found for bluetooth_key_missing_subtext (1003639333895028298) --> + <skip /> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Правадная аўдыяпрылада"</string> <string name="help_label" msgid="3528360748637781274">"Даведка і водгукі"</string> <string name="storage_category" msgid="2287342585424631813">"Сховішча"</string> diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml index f49b1d2b5adc..bf10d1d94a39 100644 --- a/packages/SettingsLib/res/values-bg/strings.xml +++ b/packages/SettingsLib/res/values-bg/strings.xml @@ -610,7 +610,7 @@ <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Свързано устройство"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Този телефон"</string> <string name="media_transfer_digital_line_name" msgid="312091711951124301">"S/PDIF"</string> - <string name="media_transfer_analog_line_name" msgid="1841163866716302104">"Аналогов"</string> + <string name="media_transfer_analog_line_name" msgid="1841163866716302104">"Аналогов аудиоизход"</string> <string name="media_transfer_aux_line_name" msgid="894135835967856689">"AUX"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Възпроизвеждането не е възможно на това устройство"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Надстройте профила, за да превключите"</string> @@ -627,6 +627,8 @@ <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Вграден високоговорител"</string> <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"Аудио на телевизора"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"При свързването възникна проблем. Изключете устройството и го включете отново"</string> + <!-- no translation found for bluetooth_key_missing_subtext (1003639333895028298) --> + <skip /> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Аудиоустройство с кабел"</string> <string name="help_label" msgid="3528360748637781274">"Помощ и отзиви"</string> <string name="storage_category" msgid="2287342585424631813">"Хранилище"</string> diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml index 79e4ed9600aa..5f37d44c3626 100644 --- a/packages/SettingsLib/res/values-bn/strings.xml +++ b/packages/SettingsLib/res/values-bn/strings.xml @@ -539,8 +539,7 @@ <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"এটি বিধিনিষেধ সেটিং থেকে নিয়ন্ত্রণ করা হয়"</string> <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"কল চলাকালীন উপলভ্য হবে না"</string> <string name="disabled" msgid="8017887509554714950">"অক্ষম হয়েছে"</string> - <!-- no translation found for enabled (3997122818554810678) --> - <skip /> + <string name="enabled" msgid="3997122818554810678">"চালু করা আছে"</string> <string name="external_source_trusted" msgid="1146522036773132905">"অনুমোদিত"</string> <string name="external_source_untrusted" msgid="5037891688911672227">"অনুমোদিত নয়"</string> <string name="install_other_apps" msgid="3232595082023199454">"অজানা অ্যাপ ইনস্টল করা"</string> @@ -628,6 +627,8 @@ <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"বিল্ট-ইন স্পিকার"</string> <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"টিভি অডিও"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"কানেক্ট করতে সমস্যা হচ্ছে। ডিভাইস বন্ধ করে আবার চালু করুন"</string> + <!-- no translation found for bluetooth_key_missing_subtext (1003639333895028298) --> + <skip /> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"ওয়্যার অডিও ডিভাইস"</string> <string name="help_label" msgid="3528360748637781274">"সহায়তা ও মতামত"</string> <string name="storage_category" msgid="2287342585424631813">"স্টোরেজ"</string> diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml index d06ce341fa99..8cc8199ce07e 100644 --- a/packages/SettingsLib/res/values-bs/strings.xml +++ b/packages/SettingsLib/res/values-bs/strings.xml @@ -539,8 +539,7 @@ <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Kontrolira ograničena postavka"</string> <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Nije dostupno tokom poziva"</string> <string name="disabled" msgid="8017887509554714950">"Onemogućeno"</string> - <!-- no translation found for enabled (3997122818554810678) --> - <skip /> + <string name="enabled" msgid="3997122818554810678">"Omogućeno"</string> <string name="external_source_trusted" msgid="1146522036773132905">"Dozvoljeno"</string> <string name="external_source_untrusted" msgid="5037891688911672227">"Nije dozvoljeno"</string> <string name="install_other_apps" msgid="3232595082023199454">"Instaliranje nepoznatih aplikacija"</string> @@ -628,6 +627,8 @@ <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Ugrađeni zvučnik"</string> <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"Zvuk TV-a"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Došlo je do problema prilikom povezivanja. Isključite, pa ponovo uključite uređaj"</string> + <!-- no translation found for bluetooth_key_missing_subtext (1003639333895028298) --> + <skip /> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Žičani audio uređaj"</string> <string name="help_label" msgid="3528360748637781274">"Pomoć i povratne informacije"</string> <string name="storage_category" msgid="2287342585424631813">"Pohrana"</string> diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml index 776caae3b828..ee683e57b783 100644 --- a/packages/SettingsLib/res/values-ca/strings.xml +++ b/packages/SettingsLib/res/values-ca/strings.xml @@ -539,8 +539,7 @@ <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Controlat per l\'opció de configuració restringida"</string> <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"No està disponible durant les trucades"</string> <string name="disabled" msgid="8017887509554714950">"Desactivat"</string> - <!-- no translation found for enabled (3997122818554810678) --> - <skip /> + <string name="enabled" msgid="3997122818554810678">"Activat"</string> <string name="external_source_trusted" msgid="1146522036773132905">"Amb permís"</string> <string name="external_source_untrusted" msgid="5037891688911672227">"Sense permís"</string> <string name="install_other_apps" msgid="3232595082023199454">"Instal·la aplicacions desconegudes"</string> @@ -628,6 +627,8 @@ <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Altaveu integrat"</string> <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"Àudio de la televisió"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Hi ha hagut un problema amb la connexió. Apaga el dispositiu i torna\'l a encendre."</string> + <!-- no translation found for bluetooth_key_missing_subtext (1003639333895028298) --> + <skip /> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispositiu d\'àudio amb cable"</string> <string name="help_label" msgid="3528360748637781274">"Ajuda i suggeriments"</string> <string name="storage_category" msgid="2287342585424631813">"Emmagatzematge"</string> diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml index 02722fd98959..58cdc19c5c73 100644 --- a/packages/SettingsLib/res/values-cs/strings.xml +++ b/packages/SettingsLib/res/values-cs/strings.xml @@ -539,8 +539,7 @@ <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Spravováno omezeným nastavením"</string> <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Při volání nedostupné"</string> <string name="disabled" msgid="8017887509554714950">"Deaktivováno"</string> - <!-- no translation found for enabled (3997122818554810678) --> - <skip /> + <string name="enabled" msgid="3997122818554810678">"Zapnuto"</string> <string name="external_source_trusted" msgid="1146522036773132905">"Povoleno"</string> <string name="external_source_untrusted" msgid="5037891688911672227">"Není povoleno"</string> <string name="install_other_apps" msgid="3232595082023199454">"Instalace neznámých aplikací"</string> @@ -611,7 +610,7 @@ <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Připojené zařízení"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Tento telefon"</string> <string name="media_transfer_digital_line_name" msgid="312091711951124301">"S/PDIF"</string> - <string name="media_transfer_analog_line_name" msgid="1841163866716302104">"Analogové"</string> + <string name="media_transfer_analog_line_name" msgid="1841163866716302104">"Analogový výstup"</string> <string name="media_transfer_aux_line_name" msgid="894135835967856689">"AUX"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"V zařízení nelze přehrávat"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Účet je třeba upgradovat"</string> @@ -628,6 +627,8 @@ <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Vestavěný reproduktor"</string> <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"Zvuk televize"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problém s připojením. Vypněte zařízení a znovu jej zapněte"</string> + <!-- no translation found for bluetooth_key_missing_subtext (1003639333895028298) --> + <skip /> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Kabelové audiozařízení"</string> <string name="help_label" msgid="3528360748637781274">"Nápověda a zpětná vazba"</string> <string name="storage_category" msgid="2287342585424631813">"Úložiště"</string> diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml index 4d37578c68b9..257f673c93dd 100644 --- a/packages/SettingsLib/res/values-da/strings.xml +++ b/packages/SettingsLib/res/values-da/strings.xml @@ -539,8 +539,7 @@ <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Styres af en begrænset indstilling"</string> <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Kan ikke bruges under opkald"</string> <string name="disabled" msgid="8017887509554714950">"Deaktiveret"</string> - <!-- no translation found for enabled (3997122818554810678) --> - <skip /> + <string name="enabled" msgid="3997122818554810678">"Aktiveret"</string> <string name="external_source_trusted" msgid="1146522036773132905">"Tilladt"</string> <string name="external_source_untrusted" msgid="5037891688911672227">"Ikke tilladt"</string> <string name="install_other_apps" msgid="3232595082023199454">"Installer ukendte apps"</string> @@ -628,6 +627,8 @@ <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Indbygget højttaler"</string> <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"TV-lyd"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Der kunne ikke oprettes forbindelse. Sluk og tænd enheden"</string> + <!-- no translation found for bluetooth_key_missing_subtext (1003639333895028298) --> + <skip /> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Lydenhed med ledning"</string> <string name="help_label" msgid="3528360748637781274">"Hjælp og feedback"</string> <string name="storage_category" msgid="2287342585424631813">"Lager"</string> diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml index 3e57002a7a59..8bfaa0e0aeb9 100644 --- a/packages/SettingsLib/res/values-de/strings.xml +++ b/packages/SettingsLib/res/values-de/strings.xml @@ -539,8 +539,7 @@ <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Gesteuert durch eingeschränkte Einstellung"</string> <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Während Anrufen nicht verfügbar"</string> <string name="disabled" msgid="8017887509554714950">"Deaktiviert"</string> - <!-- no translation found for enabled (3997122818554810678) --> - <skip /> + <string name="enabled" msgid="3997122818554810678">"Aktiviert"</string> <string name="external_source_trusted" msgid="1146522036773132905">"Zugelassen"</string> <string name="external_source_untrusted" msgid="5037891688911672227">"Nicht zugelassen"</string> <string name="install_other_apps" msgid="3232595082023199454">"Installieren unbekannter Apps"</string> @@ -628,6 +627,8 @@ <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Integrierter Lautsprecher"</string> <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"TV‑Audio"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Verbindung kann nicht hergestellt werden. Schalte das Gerät aus und wieder ein."</string> + <!-- no translation found for bluetooth_key_missing_subtext (1003639333895028298) --> + <skip /> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Netzbetriebenes Audiogerät"</string> <string name="help_label" msgid="3528360748637781274">"Hilfe und Feedback"</string> <string name="storage_category" msgid="2287342585424631813">"Speicher"</string> diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml index be1135690e62..83fb6ff6ed70 100644 --- a/packages/SettingsLib/res/values-el/strings.xml +++ b/packages/SettingsLib/res/values-el/strings.xml @@ -539,8 +539,7 @@ <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Ελέγχεται από τη Ρύθμιση με περιορισμό"</string> <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Μη διαθέσιμη κατά τη διάρκεια κλήσεων"</string> <string name="disabled" msgid="8017887509554714950">"Απενεργοποιημένη"</string> - <!-- no translation found for enabled (3997122818554810678) --> - <skip /> + <string name="enabled" msgid="3997122818554810678">"Ενεργό"</string> <string name="external_source_trusted" msgid="1146522036773132905">"Επιτρέπεται"</string> <string name="external_source_untrusted" msgid="5037891688911672227">"Δεν επιτρέπεται"</string> <string name="install_other_apps" msgid="3232595082023199454">"Εγκατ. άγνωστων εφ."</string> @@ -611,7 +610,7 @@ <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Συνδεδεμένη συσκευή"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Αυτό το τηλέφ."</string> <string name="media_transfer_digital_line_name" msgid="312091711951124301">"S/PDIF"</string> - <string name="media_transfer_analog_line_name" msgid="1841163866716302104">"Αναλογικός"</string> + <string name="media_transfer_analog_line_name" msgid="1841163866716302104">"Αναλογική έξοδος"</string> <string name="media_transfer_aux_line_name" msgid="894135835967856689">"AUX"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Δεν είναι δυνατή η αναπαραγωγή σε αυτήν τη συσκευή"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Αναβαθμίστε τον λογαριασμό για εναλλαγή"</string> @@ -628,6 +627,8 @@ <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Ενσωματωμένο ηχείο"</string> <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"Ήχος τηλεόρασης"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Πρόβλημα κατά τη σύνδεση. Απενεργοποιήστε τη συσκευή και ενεργοποιήστε την ξανά"</string> + <!-- no translation found for bluetooth_key_missing_subtext (1003639333895028298) --> + <skip /> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Ενσύρματη συσκευή ήχου"</string> <string name="help_label" msgid="3528360748637781274">"Βοήθεια και σχόλια"</string> <string name="storage_category" msgid="2287342585424631813">"Αποθηκευτικός χώρος"</string> diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml index 8c819ac742a1..e2e3851eb3fd 100644 --- a/packages/SettingsLib/res/values-en-rAU/strings.xml +++ b/packages/SettingsLib/res/values-en-rAU/strings.xml @@ -539,8 +539,7 @@ <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Controlled by restricted setting"</string> <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Unavailable during calls"</string> <string name="disabled" msgid="8017887509554714950">"Disabled"</string> - <!-- no translation found for enabled (3997122818554810678) --> - <skip /> + <string name="enabled" msgid="3997122818554810678">"Enabled"</string> <string name="external_source_trusted" msgid="1146522036773132905">"Allowed"</string> <string name="external_source_untrusted" msgid="5037891688911672227">"Not allowed"</string> <string name="install_other_apps" msgid="3232595082023199454">"Install unknown apps"</string> @@ -628,6 +627,8 @@ <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Built-in speaker"</string> <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"TV audio"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problem connecting. Turn device off and back on"</string> + <!-- no translation found for bluetooth_key_missing_subtext (1003639333895028298) --> + <skip /> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Wired audio device"</string> <string name="help_label" msgid="3528360748637781274">"Help and feedback"</string> <string name="storage_category" msgid="2287342585424631813">"Storage"</string> diff --git a/packages/SettingsLib/res/values-en-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml index 2faf2feed627..002b1049cb13 100644 --- a/packages/SettingsLib/res/values-en-rCA/strings.xml +++ b/packages/SettingsLib/res/values-en-rCA/strings.xml @@ -627,6 +627,7 @@ <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Built-in speaker"</string> <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"TV Audio"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problem connecting. Turn device off & back on"</string> + <string name="bluetooth_key_missing_subtext" msgid="1003639333895028298">"Can’t connect"</string> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Wired audio device"</string> <string name="help_label" msgid="3528360748637781274">"Help and feedback"</string> <string name="storage_category" msgid="2287342585424631813">"Storage"</string> diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml index 8c819ac742a1..e2e3851eb3fd 100644 --- a/packages/SettingsLib/res/values-en-rGB/strings.xml +++ b/packages/SettingsLib/res/values-en-rGB/strings.xml @@ -539,8 +539,7 @@ <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Controlled by restricted setting"</string> <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Unavailable during calls"</string> <string name="disabled" msgid="8017887509554714950">"Disabled"</string> - <!-- no translation found for enabled (3997122818554810678) --> - <skip /> + <string name="enabled" msgid="3997122818554810678">"Enabled"</string> <string name="external_source_trusted" msgid="1146522036773132905">"Allowed"</string> <string name="external_source_untrusted" msgid="5037891688911672227">"Not allowed"</string> <string name="install_other_apps" msgid="3232595082023199454">"Install unknown apps"</string> @@ -628,6 +627,8 @@ <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Built-in speaker"</string> <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"TV audio"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problem connecting. Turn device off and back on"</string> + <!-- no translation found for bluetooth_key_missing_subtext (1003639333895028298) --> + <skip /> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Wired audio device"</string> <string name="help_label" msgid="3528360748637781274">"Help and feedback"</string> <string name="storage_category" msgid="2287342585424631813">"Storage"</string> diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml index 8c819ac742a1..e2e3851eb3fd 100644 --- a/packages/SettingsLib/res/values-en-rIN/strings.xml +++ b/packages/SettingsLib/res/values-en-rIN/strings.xml @@ -539,8 +539,7 @@ <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Controlled by restricted setting"</string> <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Unavailable during calls"</string> <string name="disabled" msgid="8017887509554714950">"Disabled"</string> - <!-- no translation found for enabled (3997122818554810678) --> - <skip /> + <string name="enabled" msgid="3997122818554810678">"Enabled"</string> <string name="external_source_trusted" msgid="1146522036773132905">"Allowed"</string> <string name="external_source_untrusted" msgid="5037891688911672227">"Not allowed"</string> <string name="install_other_apps" msgid="3232595082023199454">"Install unknown apps"</string> @@ -628,6 +627,8 @@ <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Built-in speaker"</string> <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"TV audio"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problem connecting. Turn device off and back on"</string> + <!-- no translation found for bluetooth_key_missing_subtext (1003639333895028298) --> + <skip /> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Wired audio device"</string> <string name="help_label" msgid="3528360748637781274">"Help and feedback"</string> <string name="storage_category" msgid="2287342585424631813">"Storage"</string> diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml index 0e83356e185f..17909f984640 100644 --- a/packages/SettingsLib/res/values-es-rUS/strings.xml +++ b/packages/SettingsLib/res/values-es-rUS/strings.xml @@ -627,6 +627,8 @@ <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Bocina integrada"</string> <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"Audio de la TV"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Error al establecer la conexión. Apaga el dispositivo y vuelve a encenderlo."</string> + <!-- no translation found for bluetooth_key_missing_subtext (1003639333895028298) --> + <skip /> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispositivo de audio con cable"</string> <string name="help_label" msgid="3528360748637781274">"Ayuda y comentarios"</string> <string name="storage_category" msgid="2287342585424631813">"Almacenamiento"</string> diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml index 66d81d27a807..536f0995a001 100644 --- a/packages/SettingsLib/res/values-es/strings.xml +++ b/packages/SettingsLib/res/values-es/strings.xml @@ -539,8 +539,7 @@ <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Controlado por ajustes restringidos"</string> <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"No disponible durante las llamadas"</string> <string name="disabled" msgid="8017887509554714950">"Inhabilitada"</string> - <!-- no translation found for enabled (3997122818554810678) --> - <skip /> + <string name="enabled" msgid="3997122818554810678">"Habilitado"</string> <string name="external_source_trusted" msgid="1146522036773132905">"Autorizadas"</string> <string name="external_source_untrusted" msgid="5037891688911672227">"No autorizadas"</string> <string name="install_other_apps" msgid="3232595082023199454">"Instalar aplicaciones desconocidas"</string> @@ -628,6 +627,8 @@ <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Altavoz integrado"</string> <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"Audio de la televisión"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"No se ha podido conectar; reinicia el dispositivo"</string> + <!-- no translation found for bluetooth_key_missing_subtext (1003639333895028298) --> + <skip /> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispositivo de audio con cable"</string> <string name="help_label" msgid="3528360748637781274">"Ayuda y comentarios"</string> <string name="storage_category" msgid="2287342585424631813">"Almacenamiento"</string> diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml index 59e1e4b4bc95..86ccf28c65a1 100644 --- a/packages/SettingsLib/res/values-et/strings.xml +++ b/packages/SettingsLib/res/values-et/strings.xml @@ -539,8 +539,7 @@ <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Haldavad piiranguga seaded"</string> <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Pole kõnede ajal saadaval"</string> <string name="disabled" msgid="8017887509554714950">"Keelatud"</string> - <!-- no translation found for enabled (3997122818554810678) --> - <skip /> + <string name="enabled" msgid="3997122818554810678">"Lubatud"</string> <string name="external_source_trusted" msgid="1146522036773132905">"Lubatud"</string> <string name="external_source_untrusted" msgid="5037891688911672227">"Pole lubatud"</string> <string name="install_other_apps" msgid="3232595082023199454">"Tundmatute rakenduste installimine"</string> @@ -628,6 +627,8 @@ <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Sisseehitatud kõlar"</string> <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"Teleri heli"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Probleem ühendamisel. Lülitage seade välja ja uuesti sisse"</string> + <!-- no translation found for bluetooth_key_missing_subtext (1003639333895028298) --> + <skip /> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Juhtmega heliseade"</string> <string name="help_label" msgid="3528360748637781274">"Abi ja tagasiside"</string> <string name="storage_category" msgid="2287342585424631813">"Salvestusruum"</string> diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml index 6fd7e24b8c8b..4de40f53df0e 100644 --- a/packages/SettingsLib/res/values-eu/strings.xml +++ b/packages/SettingsLib/res/values-eu/strings.xml @@ -628,6 +628,8 @@ <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Bozgorailu integratua"</string> <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"Telebistako audioa"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Arazo bat izan da konektatzean. Itzali gailua eta pitz ezazu berriro."</string> + <!-- no translation found for bluetooth_key_missing_subtext (1003639333895028298) --> + <skip /> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Audio-gailu kableduna"</string> <string name="help_label" msgid="3528360748637781274">"Laguntza eta iritziak"</string> <string name="storage_category" msgid="2287342585424631813">"Biltegiratzea"</string> diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml index 483e76ab4369..f3fa38b1ded1 100644 --- a/packages/SettingsLib/res/values-fa/strings.xml +++ b/packages/SettingsLib/res/values-fa/strings.xml @@ -627,6 +627,8 @@ <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"بلندگوی داخلی"</string> <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"صدای تلویزیون"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"مشکل در اتصال. دستگاه را خاموش و دوباره روشن کنید"</string> + <!-- no translation found for bluetooth_key_missing_subtext (1003639333895028298) --> + <skip /> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"دستگاه صوتی سیمی"</string> <string name="help_label" msgid="3528360748637781274">"راهنما و بازخورد"</string> <string name="storage_category" msgid="2287342585424631813">"فضای ذخیرهسازی"</string> diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml index f6af16e74ecb..e212f0911530 100644 --- a/packages/SettingsLib/res/values-fi/strings.xml +++ b/packages/SettingsLib/res/values-fi/strings.xml @@ -539,8 +539,7 @@ <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Rajoitettujen asetusten mukaisesti"</string> <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Ei käytettävissä puhelujen aikana"</string> <string name="disabled" msgid="8017887509554714950">"Pois päältä"</string> - <!-- no translation found for enabled (3997122818554810678) --> - <skip /> + <string name="enabled" msgid="3997122818554810678">"Käytössä"</string> <string name="external_source_trusted" msgid="1146522036773132905">"Sallittu"</string> <string name="external_source_untrusted" msgid="5037891688911672227">"Ei sallittu"</string> <string name="install_other_apps" msgid="3232595082023199454">"Asenna tuntemattomia sovelluksia"</string> @@ -628,6 +627,8 @@ <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Sisäänrakennettu kaiutin"</string> <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"TV:n audio"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Yhteysvirhe. Sammuta laite ja käynnistä se uudelleen."</string> + <!-- no translation found for bluetooth_key_missing_subtext (1003639333895028298) --> + <skip /> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Langallinen äänilaite"</string> <string name="help_label" msgid="3528360748637781274">"Ohjeet ja palaute"</string> <string name="storage_category" msgid="2287342585424631813">"Tallennustila"</string> diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml index a5b212b432cb..7c3c84408f4a 100644 --- a/packages/SettingsLib/res/values-fr-rCA/strings.xml +++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml @@ -627,6 +627,8 @@ <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Haut-parleur intégré"</string> <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"Sortie audio du téléviseur"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problème de connexion. Éteignez et rallumez l\'appareil"</string> + <!-- no translation found for bluetooth_key_missing_subtext (1003639333895028298) --> + <skip /> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Appareil audio à câble"</string> <string name="help_label" msgid="3528360748637781274">"Aide et commentaires"</string> <string name="storage_category" msgid="2287342585424631813">"Stockage"</string> diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml index 4cc8a9a37632..1053e15d23c3 100644 --- a/packages/SettingsLib/res/values-fr/strings.xml +++ b/packages/SettingsLib/res/values-fr/strings.xml @@ -539,8 +539,7 @@ <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Contrôlé par les paramètres restreints"</string> <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Indisponible pendant les appels"</string> <string name="disabled" msgid="8017887509554714950">"Désactivée"</string> - <!-- no translation found for enabled (3997122818554810678) --> - <skip /> + <string name="enabled" msgid="3997122818554810678">"Activé"</string> <string name="external_source_trusted" msgid="1146522036773132905">"Autorisé"</string> <string name="external_source_untrusted" msgid="5037891688911672227">"Non autorisé"</string> <string name="install_other_apps" msgid="3232595082023199454">"Installation d\'applis inconnues"</string> @@ -628,6 +627,8 @@ <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Haut-parleur intégré"</string> <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"Audio TV"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problème de connexion. Éteignez l\'appareil, puis rallumez-le"</string> + <!-- no translation found for bluetooth_key_missing_subtext (1003639333895028298) --> + <skip /> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Appareil audio filaire"</string> <string name="help_label" msgid="3528360748637781274">"Aide et commentaires"</string> <string name="storage_category" msgid="2287342585424631813">"Stockage"</string> diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml index 352b6082081a..618dd62d248b 100644 --- a/packages/SettingsLib/res/values-gl/strings.xml +++ b/packages/SettingsLib/res/values-gl/strings.xml @@ -539,8 +539,7 @@ <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Baixo o control de opcións restrinxidas"</string> <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Non dispoñible durante as chamadas"</string> <string name="disabled" msgid="8017887509554714950">"Desactivada"</string> - <!-- no translation found for enabled (3997122818554810678) --> - <skip /> + <string name="enabled" msgid="3997122818554810678">"Opción activada"</string> <string name="external_source_trusted" msgid="1146522036773132905">"Permiso concedido"</string> <string name="external_source_untrusted" msgid="5037891688911672227">"Permiso non concedido"</string> <string name="install_other_apps" msgid="3232595082023199454">"Instalar aplicacións descoñecidas"</string> @@ -626,8 +625,10 @@ <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Conectado mediante ARC"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Conectado mediante eARC"</string> <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Altofalante integrado"</string> - <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"Audio da televisión"</string> + <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"Saída de audio da televisión"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Produciuse un problema coa conexión. Apaga e acende o dispositivo."</string> + <!-- no translation found for bluetooth_key_missing_subtext (1003639333895028298) --> + <skip /> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispositivo de audio con cable"</string> <string name="help_label" msgid="3528360748637781274">"Axuda e comentarios"</string> <string name="storage_category" msgid="2287342585424631813">"Almacenamento"</string> diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml index aa27f2a8a09b..9f8fc1046920 100644 --- a/packages/SettingsLib/res/values-gu/strings.xml +++ b/packages/SettingsLib/res/values-gu/strings.xml @@ -539,8 +539,7 @@ <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"પ્રતિબંધિત સેટિંગ દ્વારા નિયંત્રિત"</string> <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"કૉલ દરમિયાન અનુપલબ્ધ"</string> <string name="disabled" msgid="8017887509554714950">"બંધ કરી"</string> - <!-- no translation found for enabled (3997122818554810678) --> - <skip /> + <string name="enabled" msgid="3997122818554810678">"ચાલુ છે"</string> <string name="external_source_trusted" msgid="1146522036773132905">"મંજૂરી છે"</string> <string name="external_source_untrusted" msgid="5037891688911672227">"મંજૂરી નથી"</string> <string name="install_other_apps" msgid="3232595082023199454">"અજાણી ઍપ ઇન્સ્ટૉલ કરો"</string> @@ -612,7 +611,7 @@ <string name="media_transfer_this_phone" msgid="7194341457812151531">"આ ફોન"</string> <string name="media_transfer_digital_line_name" msgid="312091711951124301">"S/PDIF"</string> <string name="media_transfer_analog_line_name" msgid="1841163866716302104">"એનાલોગ"</string> - <string name="media_transfer_aux_line_name" msgid="894135835967856689">"ઑગ્ઝિલરી"</string> + <string name="media_transfer_aux_line_name" msgid="894135835967856689">"AUX"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"આ ડિવાઇસ પર ચલાવી શકતા નથી"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"સ્વિચ કરવા માટે એકાઉન્ટ અપગ્રેડ કરો"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"ડાઉનલોડ કરેલું કન્ટેન્ટ અહીં ચલાવી શકતા નથી"</string> @@ -628,6 +627,8 @@ <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"બિલ્ટ-ઇન સ્પીકર"</string> <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"ટીવીનો ઑડિયો"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"કનેક્ટ કરવામાં સમસ્યા આવી રહી છે. ડિવાઇસને બંધ કરીને ફરી ચાલુ કરો"</string> + <!-- no translation found for bluetooth_key_missing_subtext (1003639333895028298) --> + <skip /> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"વાયરવાળો ઑડિયો ડિવાઇસ"</string> <string name="help_label" msgid="3528360748637781274">"સહાય અને પ્રતિસાદ"</string> <string name="storage_category" msgid="2287342585424631813">"સ્ટોરેજ"</string> diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml index 7c4d64f32cda..cb6e3c90e7ca 100644 --- a/packages/SettingsLib/res/values-hi/strings.xml +++ b/packages/SettingsLib/res/values-hi/strings.xml @@ -539,8 +539,7 @@ <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"इसे पाबंदी मोड वाली सेटिंग से कंट्रोल किया जाता है"</string> <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"कॉल के दौरान उपलब्ध नहीं है"</string> <string name="disabled" msgid="8017887509554714950">"बंद किया गया"</string> - <!-- no translation found for enabled (3997122818554810678) --> - <skip /> + <string name="enabled" msgid="3997122818554810678">"चालू है"</string> <string name="external_source_trusted" msgid="1146522036773132905">"अनुमति है"</string> <string name="external_source_untrusted" msgid="5037891688911672227">"अनुमति नहीं है"</string> <string name="install_other_apps" msgid="3232595082023199454">"अनजान ऐप्लिकेशन इंस्टॉल करने की अनुमति"</string> @@ -612,7 +611,7 @@ <string name="media_transfer_this_phone" msgid="7194341457812151531">"यह फ़ोन"</string> <string name="media_transfer_digital_line_name" msgid="312091711951124301">"S/PDIF"</string> <string name="media_transfer_analog_line_name" msgid="1841163866716302104">"ऐनालॉग"</string> - <string name="media_transfer_aux_line_name" msgid="894135835967856689">"ऑक्स"</string> + <string name="media_transfer_aux_line_name" msgid="894135835967856689">"सहायक डिवाइस"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"इस डिवाइस पर मीडिया नहीं चलाया जा सकता"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"प्रीमियम खाते में स्विच करने के लिए, अपना खाता अपग्रेड करें"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"डाउनलोड किए गए वीडियो यहां नहीं चलाए जा सकते"</string> @@ -628,6 +627,8 @@ <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"डिवाइस में पहले से मौजूद स्पीकर"</string> <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"टीवी ऑडियो"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"कनेक्ट करने में समस्या हो रही है. डिवाइस को बंद करके चालू करें"</string> + <!-- no translation found for bluetooth_key_missing_subtext (1003639333895028298) --> + <skip /> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"वायर वाला ऑडियो डिवाइस"</string> <string name="help_label" msgid="3528360748637781274">"सहायता और सुझाव"</string> <string name="storage_category" msgid="2287342585424631813">"डिवाइस का स्टोरेज"</string> diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml index ba5650f1d7a5..10863cf62551 100644 --- a/packages/SettingsLib/res/values-hr/strings.xml +++ b/packages/SettingsLib/res/values-hr/strings.xml @@ -539,8 +539,7 @@ <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Kontrolira ograničena postavka"</string> <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Nije dostupno tijekom poziva"</string> <string name="disabled" msgid="8017887509554714950">"Onemogućeno"</string> - <!-- no translation found for enabled (3997122818554810678) --> - <skip /> + <string name="enabled" msgid="3997122818554810678">"Omogućeno"</string> <string name="external_source_trusted" msgid="1146522036773132905">"Dopušteno"</string> <string name="external_source_untrusted" msgid="5037891688911672227">"Nije dopušteno"</string> <string name="install_other_apps" msgid="3232595082023199454">"Instalacija nepoznatih aplikacija"</string> @@ -628,6 +627,8 @@ <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Ugrađeni zvučnik"</string> <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"TV zvuk"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problem s povezivanjem. Isključite i ponovo uključite uređaj"</string> + <!-- no translation found for bluetooth_key_missing_subtext (1003639333895028298) --> + <skip /> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Žičani audiouređaj"</string> <string name="help_label" msgid="3528360748637781274">"Pomoć i povratne informacije"</string> <string name="storage_category" msgid="2287342585424631813">"Pohrana"</string> diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml index faeec8d6ed24..104e4bca7bd0 100644 --- a/packages/SettingsLib/res/values-hu/strings.xml +++ b/packages/SettingsLib/res/values-hu/strings.xml @@ -539,8 +539,7 @@ <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Korlátozott beállítás vezérli"</string> <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Nem áll rendelkezésre hívások közben"</string> <string name="disabled" msgid="8017887509554714950">"Letiltva"</string> - <!-- no translation found for enabled (3997122818554810678) --> - <skip /> + <string name="enabled" msgid="3997122818554810678">"Engedélyezve"</string> <string name="external_source_trusted" msgid="1146522036773132905">"Engedélyezett"</string> <string name="external_source_untrusted" msgid="5037891688911672227">"Nem engedélyezett"</string> <string name="install_other_apps" msgid="3232595082023199454">"Ismeretlen alkalmazások telepítése"</string> @@ -628,6 +627,8 @@ <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Beépített hangszóró"</string> <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"Tévés hangkimenet"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Sikertelen csatlakozás. Kapcsolja ki az eszközt, majd kapcsolja be újra."</string> + <!-- no translation found for bluetooth_key_missing_subtext (1003639333895028298) --> + <skip /> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Vezetékes audioeszköz"</string> <string name="help_label" msgid="3528360748637781274">"Súgó és visszajelzés"</string> <string name="storage_category" msgid="2287342585424631813">"Tárhely"</string> diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml index eedd262af0f4..a5a15f98b631 100644 --- a/packages/SettingsLib/res/values-hy/strings.xml +++ b/packages/SettingsLib/res/values-hy/strings.xml @@ -627,6 +627,8 @@ <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Ներկառուցված բարձրախոս"</string> <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"Հեռուստացույցի աուդիո"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Կապի խնդիր կա: Սարքն անջատեք և նորից միացրեք:"</string> + <!-- no translation found for bluetooth_key_missing_subtext (1003639333895028298) --> + <skip /> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Լարով աուդիո սարք"</string> <string name="help_label" msgid="3528360748637781274">"Օգնություն և հետադարձ կապ"</string> <string name="storage_category" msgid="2287342585424631813">"Տարածք"</string> diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml index 7af3de0da4fe..c8d7481ecd33 100644 --- a/packages/SettingsLib/res/values-in/strings.xml +++ b/packages/SettingsLib/res/values-in/strings.xml @@ -539,8 +539,7 @@ <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Dikontrol oleh Setelan Terbatas"</string> <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Tidak tersedia selama panggilan berlangsung"</string> <string name="disabled" msgid="8017887509554714950">"Dinonaktifkan"</string> - <!-- no translation found for enabled (3997122818554810678) --> - <skip /> + <string name="enabled" msgid="3997122818554810678">"Aktif"</string> <string name="external_source_trusted" msgid="1146522036773132905">"Diizinkan"</string> <string name="external_source_untrusted" msgid="5037891688911672227">"Tidak diizinkan"</string> <string name="install_other_apps" msgid="3232595082023199454">"Instal aplikasi tidak dikenal"</string> @@ -628,6 +627,8 @@ <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Speaker bawaan"</string> <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"Audio TV"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Ada masalah saat menghubungkan. Nonaktifkan perangkat & aktifkan kembali"</string> + <!-- no translation found for bluetooth_key_missing_subtext (1003639333895028298) --> + <skip /> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Perangkat audio berkabel"</string> <string name="help_label" msgid="3528360748637781274">"Bantuan & masukan"</string> <string name="storage_category" msgid="2287342585424631813">"Penyimpanan"</string> diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml index e6ef20fcd908..f85c20b4b9fb 100644 --- a/packages/SettingsLib/res/values-is/strings.xml +++ b/packages/SettingsLib/res/values-is/strings.xml @@ -539,8 +539,7 @@ <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Stýrt af takmarkaði stillingu"</string> <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Ekki í boði á meðan á símtölum stendur"</string> <string name="disabled" msgid="8017887509554714950">"Óvirkt"</string> - <!-- no translation found for enabled (3997122818554810678) --> - <skip /> + <string name="enabled" msgid="3997122818554810678">"Kveikt"</string> <string name="external_source_trusted" msgid="1146522036773132905">"Heimilað"</string> <string name="external_source_untrusted" msgid="5037891688911672227">"Ekki heimilað"</string> <string name="install_other_apps" msgid="3232595082023199454">"Setja upp óþekkt forrit"</string> @@ -628,6 +627,8 @@ <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Innbyggður hátalari"</string> <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"Sjónvarpshljóð"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Vandamál í tengingu. Slökktu og kveiktu á tækinu"</string> + <!-- no translation found for bluetooth_key_missing_subtext (1003639333895028298) --> + <skip /> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Snúrutengt hljómtæki"</string> <string name="help_label" msgid="3528360748637781274">"Hjálp og ábendingar"</string> <string name="storage_category" msgid="2287342585424631813">"Geymsla"</string> diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml index 24e7a4cfe45d..1b31ae012b67 100644 --- a/packages/SettingsLib/res/values-it/strings.xml +++ b/packages/SettingsLib/res/values-it/strings.xml @@ -539,8 +539,7 @@ <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Gestita tramite impostazioni con restrizioni"</string> <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Non disponibile durante le chiamate"</string> <string name="disabled" msgid="8017887509554714950">"Disattivato"</string> - <!-- no translation found for enabled (3997122818554810678) --> - <skip /> + <string name="enabled" msgid="3997122818554810678">"Attivata"</string> <string name="external_source_trusted" msgid="1146522036773132905">"Autorizzazione concessa"</string> <string name="external_source_untrusted" msgid="5037891688911672227">"Autorizzazione non concessa"</string> <string name="install_other_apps" msgid="3232595082023199454">"Installa app sconosciute"</string> @@ -611,7 +610,7 @@ <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Dispositivo connesso"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Questo smartphone"</string> <string name="media_transfer_digital_line_name" msgid="312091711951124301">"S/PDIF"</string> - <string name="media_transfer_analog_line_name" msgid="1841163866716302104">"Analogico"</string> + <string name="media_transfer_analog_line_name" msgid="1841163866716302104">"Analogica"</string> <string name="media_transfer_aux_line_name" msgid="894135835967856689">"AUX"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Impossibile riprodurre su questo dispositivo"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Esegui l\'upgrade dell\'account per cambiare"</string> @@ -628,6 +627,8 @@ <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Altoparlante integrato"</string> <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"Audio della TV"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problema di connessione. Spegni e riaccendi il dispositivo"</string> + <!-- no translation found for bluetooth_key_missing_subtext (1003639333895028298) --> + <skip /> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispositivo audio cablato"</string> <string name="help_label" msgid="3528360748637781274">"Guida e feedback"</string> <string name="storage_category" msgid="2287342585424631813">"Archiviazione"</string> diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml index 82d90430090b..0da5795be3c6 100644 --- a/packages/SettingsLib/res/values-iw/strings.xml +++ b/packages/SettingsLib/res/values-iw/strings.xml @@ -539,8 +539,7 @@ <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"בשליטה של הגדרה מוגבלת"</string> <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"ההעדפה הזו לא זמינה במהלך שיחות"</string> <string name="disabled" msgid="8017887509554714950">"מושבת"</string> - <!-- no translation found for enabled (3997122818554810678) --> - <skip /> + <string name="enabled" msgid="3997122818554810678">"מופעלת"</string> <string name="external_source_trusted" msgid="1146522036773132905">"מורשה"</string> <string name="external_source_untrusted" msgid="5037891688911672227">"לא מורשה"</string> <string name="install_other_apps" msgid="3232595082023199454">"התקנת אפליקציות לא מוכרות"</string> @@ -611,7 +610,7 @@ <string name="media_transfer_default_device_name" msgid="4315604017399871828">"המכשיר המחובר"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"הטלפון הזה"</string> <string name="media_transfer_digital_line_name" msgid="312091711951124301">"S/PDIF"</string> - <string name="media_transfer_analog_line_name" msgid="1841163866716302104">"אנלוגי"</string> + <string name="media_transfer_analog_line_name" msgid="1841163866716302104">"חיבור אנלוגי"</string> <string name="media_transfer_aux_line_name" msgid="894135835967856689">"AUX"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"לא ניתן להפעיל במכשיר"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"יש לשדרג חשבון כדי לעבור"</string> @@ -628,6 +627,8 @@ <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"רמקול מובנה"</string> <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"אודיו בטלוויזיה"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"יש בעיה בחיבור. עליך לכבות את המכשיר ולהפעיל אותו מחדש"</string> + <!-- no translation found for bluetooth_key_missing_subtext (1003639333895028298) --> + <skip /> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"התקן אודיו חוטי"</string> <string name="help_label" msgid="3528360748637781274">"עזרה ומשוב"</string> <string name="storage_category" msgid="2287342585424631813">"אחסון"</string> diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml index adf5b71a7ea3..78c16de0f63b 100644 --- a/packages/SettingsLib/res/values-ja/strings.xml +++ b/packages/SettingsLib/res/values-ja/strings.xml @@ -627,6 +627,8 @@ <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"内蔵スピーカー"</string> <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"TV オーディオ"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"接続エラーです。デバイスを OFF にしてから ON に戻してください"</string> + <!-- no translation found for bluetooth_key_missing_subtext (1003639333895028298) --> + <skip /> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"有線オーディオ デバイス"</string> <string name="help_label" msgid="3528360748637781274">"ヘルプとフィードバック"</string> <string name="storage_category" msgid="2287342585424631813">"ストレージ"</string> diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml index e9368aa05904..a7599bedd83d 100644 --- a/packages/SettingsLib/res/values-ka/strings.xml +++ b/packages/SettingsLib/res/values-ka/strings.xml @@ -627,6 +627,8 @@ <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"ჩაშენებული დინამიკი"</string> <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"TV Audio"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"დაკავშირებისას წარმოიქმნა პრობლემა. გამორთეთ და კვლავ ჩართეთ მოწყობილობა"</string> + <!-- no translation found for bluetooth_key_missing_subtext (1003639333895028298) --> + <skip /> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"სადენიანი აუდიო მოწყობილობა"</string> <string name="help_label" msgid="3528360748637781274">"დახმარება და გამოხმაურება"</string> <string name="storage_category" msgid="2287342585424631813">"საცავი"</string> diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml index 16b7c22b6bfe..65ad80ab18b7 100644 --- a/packages/SettingsLib/res/values-kk/strings.xml +++ b/packages/SettingsLib/res/values-kk/strings.xml @@ -539,8 +539,7 @@ <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Шектелген параметрлер арқылы басқарылады."</string> <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Қоңырау шалу кезінде қолжетімді емес."</string> <string name="disabled" msgid="8017887509554714950">"Өшірілген"</string> - <!-- no translation found for enabled (3997122818554810678) --> - <skip /> + <string name="enabled" msgid="3997122818554810678">"Қосулы"</string> <string name="external_source_trusted" msgid="1146522036773132905">"Рұқсат берілген"</string> <string name="external_source_untrusted" msgid="5037891688911672227">"Рұқсат етілмеген"</string> <string name="install_other_apps" msgid="3232595082023199454">"Белгісіз қолданбаларды орнату"</string> @@ -628,6 +627,8 @@ <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Ендірілген динамик"</string> <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"Теледидардың аудио шығысы"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Байланыс орнату қатесі шығуып жатыр. Құрылғыны өшіріп, қайта қосыңыз."</string> + <!-- no translation found for bluetooth_key_missing_subtext (1003639333895028298) --> + <skip /> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Сымды аудио құрылғысы"</string> <string name="help_label" msgid="3528360748637781274">"Анықтама және пікір"</string> <string name="storage_category" msgid="2287342585424631813">"Жад"</string> diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml index b183dc926c2d..b8a8355a7249 100644 --- a/packages/SettingsLib/res/values-km/strings.xml +++ b/packages/SettingsLib/res/values-km/strings.xml @@ -624,9 +624,11 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"បានភ្ជាប់តាមរយៈ ARC"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"បានភ្ជាប់តាមរយៈ eARC"</string> - <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"ឧបករណ៍បំពងសំឡេងភ្ជាប់មកជាមួយ"</string> + <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"ឧបករណ៍សំឡេងមកជាមួយ"</string> <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"សំឡេងទូរទស្សន៍"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"មានបញ្ហាក្នុងការភ្ជាប់។ បិទ រួចបើកឧបករណ៍វិញ"</string> + <!-- no translation found for bluetooth_key_missing_subtext (1003639333895028298) --> + <skip /> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"ឧបករណ៍សំឡេងប្រើខ្សែ"</string> <string name="help_label" msgid="3528360748637781274">"ជំនួយ និងមតិកែលម្អ"</string> <string name="storage_category" msgid="2287342585424631813">"ទំហំផ្ទុក"</string> diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml index a4e01d96761a..4a8a4135e98e 100644 --- a/packages/SettingsLib/res/values-kn/strings.xml +++ b/packages/SettingsLib/res/values-kn/strings.xml @@ -627,6 +627,8 @@ <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"ಅಂತರ್ ನಿರ್ಮಿತ ಧ್ವನಿ ವರ್ಧಕ"</string> <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"TV ಆಡಿಯೊ"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"ಕನೆಕ್ಟ್ ಮಾಡುವಾಗ ಸಮಸ್ಯೆ ಎದುರಾಗಿದೆ ಸಾಧನವನ್ನು ಆಫ್ ಮಾಡಿ ಹಾಗೂ ನಂತರ ಪುನಃ ಆನ್ ಮಾಡಿ"</string> + <!-- no translation found for bluetooth_key_missing_subtext (1003639333895028298) --> + <skip /> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"ವೈರ್ ಹೊಂದಿರುವ ಆಡಿಯೋ ಸಾಧನ"</string> <string name="help_label" msgid="3528360748637781274">"ಸಹಾಯ ಮತ್ತು ಪ್ರತಿಕ್ರಿಯೆ"</string> <string name="storage_category" msgid="2287342585424631813">"ಸಂಗ್ರಹಣೆ"</string> diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml index 0488543f7790..ee99bebf7663 100644 --- a/packages/SettingsLib/res/values-ko/strings.xml +++ b/packages/SettingsLib/res/values-ko/strings.xml @@ -539,8 +539,7 @@ <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"제한된 설정으로 제어됨"</string> <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"통화 중에는 사용할 수 없습니다."</string> <string name="disabled" msgid="8017887509554714950">"사용 안함"</string> - <!-- no translation found for enabled (3997122818554810678) --> - <skip /> + <string name="enabled" msgid="3997122818554810678">"사용 설정됨"</string> <string name="external_source_trusted" msgid="1146522036773132905">"허용됨"</string> <string name="external_source_untrusted" msgid="5037891688911672227">"허용되지 않음"</string> <string name="install_other_apps" msgid="3232595082023199454">"알 수 없는 앱 설치"</string> @@ -628,6 +627,8 @@ <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"내장 스피커"</string> <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"TV 오디오"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"연결 중에 문제가 발생했습니다. 기기를 껐다가 다시 켜 보세요."</string> + <!-- no translation found for bluetooth_key_missing_subtext (1003639333895028298) --> + <skip /> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"유선 오디오 기기"</string> <string name="help_label" msgid="3528360748637781274">"고객센터"</string> <string name="storage_category" msgid="2287342585424631813">"저장용량"</string> diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml index b04883747ee7..5a8a66f7fee9 100644 --- a/packages/SettingsLib/res/values-ky/strings.xml +++ b/packages/SettingsLib/res/values-ky/strings.xml @@ -539,8 +539,7 @@ <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Чектелген параметр аркылуу көзөмөлдөнөт"</string> <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Сүйлөшүп жаткан учурда жеткиликсиз"</string> <string name="disabled" msgid="8017887509554714950">"Өчүрүлгөн"</string> - <!-- no translation found for enabled (3997122818554810678) --> - <skip /> + <string name="enabled" msgid="3997122818554810678">"Иштетилди"</string> <string name="external_source_trusted" msgid="1146522036773132905">"Уруксат берилген"</string> <string name="external_source_untrusted" msgid="5037891688911672227">"Тыюу салынган"</string> <string name="install_other_apps" msgid="3232595082023199454">"Белгисиз колдонмолорду орнотуу"</string> @@ -612,7 +611,7 @@ <string name="media_transfer_this_phone" msgid="7194341457812151531">"Ушул телефон"</string> <string name="media_transfer_digital_line_name" msgid="312091711951124301">"S/PDIF"</string> <string name="media_transfer_analog_line_name" msgid="1841163866716302104">"Аналог"</string> - <string name="media_transfer_aux_line_name" msgid="894135835967856689">"КШМЧ"</string> + <string name="media_transfer_aux_line_name" msgid="894135835967856689">"AUX"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Бул түзмөктө ойнотууга болбойт"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Премиум аккаунтка которулуу керек"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"Жүктөлүп алынгандар ойнотулбайт"</string> @@ -628,6 +627,8 @@ <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Алдын ала орнотулган динамик"</string> <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"Сыналгы аудиосу"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Туташууда маселе келип чыкты. Түзмөктү өчүрүп, кайра күйгүзүп көрүңүз"</string> + <!-- no translation found for bluetooth_key_missing_subtext (1003639333895028298) --> + <skip /> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Зымдуу аудио түзмөк"</string> <string name="help_label" msgid="3528360748637781274">"Жардам/Пикир билдирүү"</string> <string name="storage_category" msgid="2287342585424631813">"Сактагыч"</string> diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml index ebba474c243e..c60c04d668e8 100644 --- a/packages/SettingsLib/res/values-lo/strings.xml +++ b/packages/SettingsLib/res/values-lo/strings.xml @@ -627,6 +627,8 @@ <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"ລຳໂພງທີ່ມີໃນຕົວ"</string> <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"ສຽງນີ້"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"ເກີດບັນຫາໃນການເຊື່ອມຕໍ່. ປິດອຸປະກອນແລ້ວເປີດກັບຄືນມາໃໝ່"</string> + <!-- no translation found for bluetooth_key_missing_subtext (1003639333895028298) --> + <skip /> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"ອຸປະກອນສຽງແບບມີສາຍ"</string> <string name="help_label" msgid="3528360748637781274">"ຊ່ວຍເຫຼືອ ແລະ ຕິຊົມ"</string> <string name="storage_category" msgid="2287342585424631813">"ບ່ອນເກັບຂໍ້ມູນ"</string> diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml index 058ca607a078..6467e89840e2 100644 --- a/packages/SettingsLib/res/values-lt/strings.xml +++ b/packages/SettingsLib/res/values-lt/strings.xml @@ -627,6 +627,8 @@ <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Įtaisytas garsiakalbis"</string> <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"TV garso įrašas"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Prisijungiant kilo problema. Išjunkite įrenginį ir vėl jį įjunkite"</string> + <!-- no translation found for bluetooth_key_missing_subtext (1003639333895028298) --> + <skip /> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Laidinis garso įrenginys"</string> <string name="help_label" msgid="3528360748637781274">"Pagalba ir atsiliepimai"</string> <string name="storage_category" msgid="2287342585424631813">"Saugykla"</string> diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml index cdca19b8e0c8..ab9dfd54d6ab 100644 --- a/packages/SettingsLib/res/values-lv/strings.xml +++ b/packages/SettingsLib/res/values-lv/strings.xml @@ -539,8 +539,7 @@ <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Kontrolē ierobežots iestatījums"</string> <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Ierīce nav pieejama zvanu laikā"</string> <string name="disabled" msgid="8017887509554714950">"Atspējots"</string> - <!-- no translation found for enabled (3997122818554810678) --> - <skip /> + <string name="enabled" msgid="3997122818554810678">"Iespējots"</string> <string name="external_source_trusted" msgid="1146522036773132905">"Atļauts"</string> <string name="external_source_untrusted" msgid="5037891688911672227">"Nav atļauts"</string> <string name="install_other_apps" msgid="3232595082023199454">"Nezināmu lietotņu instalēšana"</string> @@ -628,6 +627,8 @@ <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Iebūvēts skaļrunis"</string> <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"Televizora audio"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Radās problēma ar savienojuma izveidi. Izslēdziet un atkal ieslēdziet ierīci."</string> + <!-- no translation found for bluetooth_key_missing_subtext (1003639333895028298) --> + <skip /> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Vadu audioierīce"</string> <string name="help_label" msgid="3528360748637781274">"Palīdzība un atsauksmes"</string> <string name="storage_category" msgid="2287342585424631813">"Krātuve"</string> diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml index e51ca04022a7..71d9b76e2f38 100644 --- a/packages/SettingsLib/res/values-mk/strings.xml +++ b/packages/SettingsLib/res/values-mk/strings.xml @@ -539,8 +539,7 @@ <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Контролирано со ограничени поставки"</string> <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Недостапно при повици"</string> <string name="disabled" msgid="8017887509554714950">"Оневозможено"</string> - <!-- no translation found for enabled (3997122818554810678) --> - <skip /> + <string name="enabled" msgid="3997122818554810678">"Овозможено"</string> <string name="external_source_trusted" msgid="1146522036773132905">"Со дозвола"</string> <string name="external_source_untrusted" msgid="5037891688911672227">"Без дозвола"</string> <string name="install_other_apps" msgid="3232595082023199454">"Инсталирање непознати апликации"</string> @@ -628,6 +627,8 @@ <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Вграден звучник"</string> <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"Аудио на телевизор"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Проблем со поврзување. Исклучете го уредот и повторно вклучете го"</string> + <!-- no translation found for bluetooth_key_missing_subtext (1003639333895028298) --> + <skip /> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Жичен аудиоуред"</string> <string name="help_label" msgid="3528360748637781274">"Помош и повратни информации"</string> <string name="storage_category" msgid="2287342585424631813">"Простор"</string> diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml index 54b6ff8ab524..d9c9629a5d40 100644 --- a/packages/SettingsLib/res/values-ml/strings.xml +++ b/packages/SettingsLib/res/values-ml/strings.xml @@ -539,8 +539,7 @@ <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"നിയന്ത്രിത ക്രമീകരണം ഉപയോഗിച്ച് നിയന്ത്രിക്കുന്നത്"</string> <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"കോളുകൾ ചെയ്യുമ്പോൾ ലഭ്യമല്ല"</string> <string name="disabled" msgid="8017887509554714950">"പ്രവർത്തനരഹിതമാക്കി"</string> - <!-- no translation found for enabled (3997122818554810678) --> - <skip /> + <string name="enabled" msgid="3997122818554810678">"പ്രവർത്തനക്ഷമമാക്കി"</string> <string name="external_source_trusted" msgid="1146522036773132905">"അനുവദനീയം"</string> <string name="external_source_untrusted" msgid="5037891688911672227">"അനുവദിച്ചിട്ടില്ല"</string> <string name="install_other_apps" msgid="3232595082023199454">"പരിചയമില്ലാത്ത ആപ്പുകൾ ഇൻസ്റ്റാൾ ചെയ്യുക"</string> @@ -628,6 +627,8 @@ <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"ബിൽട്ട്-ഇൻ സ്പീക്കർ"</string> <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"ടിവി ഓഡിയോ"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"കണക്റ്റ് ചെയ്യുന്നതിൽ പ്രശ്നമുണ്ടായി. ഉപകരണം ഓഫാക്കി വീണ്ടും ഓണാക്കുക"</string> + <!-- no translation found for bluetooth_key_missing_subtext (1003639333895028298) --> + <skip /> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"വയർ മുഖേന ബന്ധിപ്പിച്ച ഓഡിയോ ഉപകരണം"</string> <string name="help_label" msgid="3528360748637781274">"സഹായവും ഫീഡ്ബാക്കും"</string> <string name="storage_category" msgid="2287342585424631813">"സ്റ്റോറേജ്"</string> diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml index ed42362e5c1e..829b1d7181df 100644 --- a/packages/SettingsLib/res/values-mn/strings.xml +++ b/packages/SettingsLib/res/values-mn/strings.xml @@ -539,8 +539,7 @@ <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Хязгаарлагдсан тохиргоогоор хянадаг"</string> <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Дуудлагын үер боломжгүй"</string> <string name="disabled" msgid="8017887509554714950">"Идэвхгүйжүүлсэн"</string> - <!-- no translation found for enabled (3997122818554810678) --> - <skip /> + <string name="enabled" msgid="3997122818554810678">"Идэвхжүүлсэн"</string> <string name="external_source_trusted" msgid="1146522036773132905">"Зөвшөөрсөн"</string> <string name="external_source_untrusted" msgid="5037891688911672227">"Зөвшөөрөөгүй"</string> <string name="install_other_apps" msgid="3232595082023199454">"Тодорхойгүй апп суулгах"</string> @@ -628,6 +627,8 @@ <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Бүрэлдэхүүн чанга яригч"</string> <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"ТВ-ийн аудио"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Холбогдоход асуудал гарлаа. Төхөөрөмжийг унтраагаад дахин асаана уу"</string> + <!-- no translation found for bluetooth_key_missing_subtext (1003639333895028298) --> + <skip /> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Утастай аудио төхөөрөмж"</string> <string name="help_label" msgid="3528360748637781274">"Тусламж, санал хүсэлт"</string> <string name="storage_category" msgid="2287342585424631813">"Хадгалах сан"</string> diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml index 6e7cd02b1e61..8207174f0a0f 100644 --- a/packages/SettingsLib/res/values-mr/strings.xml +++ b/packages/SettingsLib/res/values-mr/strings.xml @@ -539,8 +539,7 @@ <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"प्रतिबंधित केलेल्या सेटिंग द्वारे नियंत्रित"</string> <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"कॉल दरम्यान उपलब्ध नाही"</string> <string name="disabled" msgid="8017887509554714950">"अक्षम"</string> - <!-- no translation found for enabled (3997122818554810678) --> - <skip /> + <string name="enabled" msgid="3997122818554810678">"सुरू केले आहे"</string> <string name="external_source_trusted" msgid="1146522036773132905">"अनुमती आहे"</string> <string name="external_source_untrusted" msgid="5037891688911672227">"अनुमती नाही"</string> <string name="install_other_apps" msgid="3232595082023199454">"अज्ञात अॅप्स इंस्टॉल करा"</string> @@ -628,6 +627,8 @@ <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"बिल्ट-इन स्पीकर"</string> <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"टीव्ही ऑडिओ"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"कनेक्ट करण्यात समस्या आली. डिव्हाइस बंद करा आणि नंतर सुरू करा"</string> + <!-- no translation found for bluetooth_key_missing_subtext (1003639333895028298) --> + <skip /> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"वायर असलेले ऑडिओ डिव्हाइस"</string> <string name="help_label" msgid="3528360748637781274">"मदत आणि फीडबॅक"</string> <string name="storage_category" msgid="2287342585424631813">"स्टोरेज"</string> diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml index 384635a97ef8..ea3d78e4aa93 100644 --- a/packages/SettingsLib/res/values-ms/strings.xml +++ b/packages/SettingsLib/res/values-ms/strings.xml @@ -627,6 +627,8 @@ <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Pembesar suara terbina dalam"</string> <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"Audio TV"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Masalah penyambungan. Matikan & hidupkan kembali peranti"</string> + <!-- no translation found for bluetooth_key_missing_subtext (1003639333895028298) --> + <skip /> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Peranti audio berwayar"</string> <string name="help_label" msgid="3528360748637781274">"Bantuan & maklum balas"</string> <string name="storage_category" msgid="2287342585424631813">"Storan"</string> diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml index fc26db0df2e0..c484a741bc2f 100644 --- a/packages/SettingsLib/res/values-my/strings.xml +++ b/packages/SettingsLib/res/values-my/strings.xml @@ -624,9 +624,11 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"ARC မှတစ်ဆင့် ချိတ်ဆက်ထားသည်"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"eARC မှတစ်ဆင့် ချိတ်ဆက်ထားသည်"</string> - <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"မူလပါရှိသည့် စပီကာ"</string> + <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"အသင့်ပါ စပီကာ"</string> <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"TV အသံ"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"ချိတ်ဆက်ရာတွင် ပြဿနာရှိပါသည်။ စက်ကိုပိတ်ပြီး ပြန်ဖွင့်ပါ"</string> + <!-- no translation found for bluetooth_key_missing_subtext (1003639333895028298) --> + <skip /> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"ကြိုးတပ် အသံစက်ပစ္စည်း"</string> <string name="help_label" msgid="3528360748637781274">"အကူအညီနှင့် အကြံပြုချက်"</string> <string name="storage_category" msgid="2287342585424631813">"သိုလှောင်ခန်း"</string> diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml index c8fc415528b4..3c06c685aa7b 100644 --- a/packages/SettingsLib/res/values-nb/strings.xml +++ b/packages/SettingsLib/res/values-nb/strings.xml @@ -539,8 +539,7 @@ <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Kontrollert av en begrenset innstilling"</string> <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Utilgjengelig under samtaler"</string> <string name="disabled" msgid="8017887509554714950">"Deaktivert"</string> - <!-- no translation found for enabled (3997122818554810678) --> - <skip /> + <string name="enabled" msgid="3997122818554810678">"Slått på"</string> <string name="external_source_trusted" msgid="1146522036773132905">"Tillatt"</string> <string name="external_source_untrusted" msgid="5037891688911672227">"Ikke tillatt"</string> <string name="install_other_apps" msgid="3232595082023199454">"Installer ukjente apper"</string> @@ -628,6 +627,8 @@ <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Innebygd høyttaler"</string> <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"TV-lyd"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Tilkoblingsproblemer. Slå enheten av og på igjen"</string> + <!-- no translation found for bluetooth_key_missing_subtext (1003639333895028298) --> + <skip /> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Lydenhet med kabel"</string> <string name="help_label" msgid="3528360748637781274">"Hjelp og tilbakemelding"</string> <string name="storage_category" msgid="2287342585424631813">"Lagring"</string> diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml index 5f09635e19c7..fe743e8330fb 100644 --- a/packages/SettingsLib/res/values-ne/strings.xml +++ b/packages/SettingsLib/res/values-ne/strings.xml @@ -539,8 +539,7 @@ <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"प्रतिबन्धित सेटिङले नियन्त्रण गरेको"</string> <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"कल चलिरहेका बेला उपलब्ध छैन"</string> <string name="disabled" msgid="8017887509554714950">"असक्षम पारियो"</string> - <!-- no translation found for enabled (3997122818554810678) --> - <skip /> + <string name="enabled" msgid="3997122818554810678">"अन गरियो"</string> <string name="external_source_trusted" msgid="1146522036773132905">"अनुमति छ"</string> <string name="external_source_untrusted" msgid="5037891688911672227">"अनुमति छैन"</string> <string name="install_other_apps" msgid="3232595082023199454">"अज्ञात एप इन्स्टल गर्ने अनुमति"</string> @@ -628,6 +627,8 @@ <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"अन्तर्निर्मित स्पिकर"</string> <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"टिभीको अडियो"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"जोड्ने क्रममा समस्या भयो। यन्त्रलाई निष्क्रिय पारेर फेरि अन गर्नुहोस्"</string> + <!-- no translation found for bluetooth_key_missing_subtext (1003639333895028298) --> + <skip /> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"तारयुक्त अडियो यन्त्र"</string> <string name="help_label" msgid="3528360748637781274">"मद्दत र प्रतिक्रिया"</string> <string name="storage_category" msgid="2287342585424631813">"भण्डारण"</string> diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml index 78dfdcead634..f49829f2e834 100644 --- a/packages/SettingsLib/res/values-nl/strings.xml +++ b/packages/SettingsLib/res/values-nl/strings.xml @@ -539,8 +539,7 @@ <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Beheerd door beperkte instelling"</string> <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Niet beschikbaar tijdens gesprekken"</string> <string name="disabled" msgid="8017887509554714950">"Uitgezet"</string> - <!-- no translation found for enabled (3997122818554810678) --> - <skip /> + <string name="enabled" msgid="3997122818554810678">"Aangezet"</string> <string name="external_source_trusted" msgid="1146522036773132905">"Toegestaan"</string> <string name="external_source_untrusted" msgid="5037891688911672227">"Niet toegestaan"</string> <string name="install_other_apps" msgid="3232595082023199454">"Onbekende apps installeren"</string> @@ -628,6 +627,8 @@ <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Ingebouwde speaker"</string> <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"Tv-audio"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Probleem bij verbinding maken. Zet het apparaat uit en weer aan."</string> + <!-- no translation found for bluetooth_key_missing_subtext (1003639333895028298) --> + <skip /> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Bedraad audioapparaat"</string> <string name="help_label" msgid="3528360748637781274">"Hulp en feedback"</string> <string name="storage_category" msgid="2287342585424631813">"Opslag"</string> diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml index d6e26314a0f9..ed202fae1b7a 100644 --- a/packages/SettingsLib/res/values-or/strings.xml +++ b/packages/SettingsLib/res/values-or/strings.xml @@ -539,8 +539,7 @@ <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"ପ୍ରତିବନ୍ଧିତ ସେଟିଂ ଦ୍ୱାରା ନିୟନ୍ତ୍ରଣ କରାଯାଇଛି"</string> <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"କଲ କରିବାବେଳେ ଉପଲବ୍ଧ ନଥାଏ"</string> <string name="disabled" msgid="8017887509554714950">"ଅକ୍ଷମ ହୋଇଛି"</string> - <!-- no translation found for enabled (3997122818554810678) --> - <skip /> + <string name="enabled" msgid="3997122818554810678">"ସକ୍ଷମ କରାଯାଇଛି"</string> <string name="external_source_trusted" msgid="1146522036773132905">"ଅନୁମତି ଦିଆଯାଇଛି"</string> <string name="external_source_untrusted" msgid="5037891688911672227">"ଅନୁମତି ନାହିଁ"</string> <string name="install_other_apps" msgid="3232595082023199454">"ଅଜଣା ଆପ ଇନଷ୍ଟଲ କରନ୍ତୁ"</string> @@ -612,7 +611,7 @@ <string name="media_transfer_this_phone" msgid="7194341457812151531">"ଏହି ଫୋନ୍"</string> <string name="media_transfer_digital_line_name" msgid="312091711951124301">"S/PDIF"</string> <string name="media_transfer_analog_line_name" msgid="1841163866716302104">"ଆନାଲଗ"</string> - <string name="media_transfer_aux_line_name" msgid="894135835967856689">"ଅକ୍ସିଲାରି"</string> + <string name="media_transfer_aux_line_name" msgid="894135835967856689">"AUX"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"ଏହି ଡିଭାଇସରେ ପ୍ଲେ କରାଯାଇପାରିବ ନାହିଁ"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"ସ୍ୱିଚ କରିବା ପାଇଁ ଆକାଉଣ୍ଟକୁ ଅପଗ୍ରେଡ କରନ୍ତୁ"</string> <string name="media_output_status_not_support_downloads" msgid="4523828729240373315">"ଏଠାରେ ଡାଉନଲୋଡଗୁଡ଼ିକୁ ପ୍ଲେ କରାଯାଇପାରିବ ନାହିଁ"</string> @@ -628,6 +627,8 @@ <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"ବିଲ୍ଟ-ଇନ ସ୍ପିକର"</string> <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"ଟିଭି ଅଡିଓ"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"ସଂଯୋଗ କରିବାରେ ସମସ୍ୟା ହେଉଛି। ଡିଭାଇସ୍ ବନ୍ଦ କରି ପୁଣି ଚାଲୁ କରନ୍ତୁ"</string> + <!-- no translation found for bluetooth_key_missing_subtext (1003639333895028298) --> + <skip /> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"ତାରଯୁକ୍ତ ଅଡିଓ ଡିଭାଇସ୍"</string> <string name="help_label" msgid="3528360748637781274">"ସାହାଯ୍ୟ ଓ ମତାମତ"</string> <string name="storage_category" msgid="2287342585424631813">"ଷ୍ଟୋରେଜ"</string> diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml index ab5f4afd07f3..ee0f761e00f0 100644 --- a/packages/SettingsLib/res/values-pa/strings.xml +++ b/packages/SettingsLib/res/values-pa/strings.xml @@ -539,8 +539,7 @@ <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"ਪ੍ਰਤਿਬੰਧਿਤ ਸੈਟਿੰਗ ਰਾਹੀਂ ਕੰਟਰੋਲ ਕੀਤੀ ਜਾਂਦੀ ਹੈ"</string> <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"ਕਾਲਾਂ ਦੌਰਾਨ ਉਪਲਬਧ ਨਹੀਂ ਹੈ"</string> <string name="disabled" msgid="8017887509554714950">"ਅਯੋਗ ਬਣਾਇਆ"</string> - <!-- no translation found for enabled (3997122818554810678) --> - <skip /> + <string name="enabled" msgid="3997122818554810678">"ਚਾਲੂ ਹੈ"</string> <string name="external_source_trusted" msgid="1146522036773132905">"ਮਨਜ਼ੂਰਸ਼ੁਦਾ"</string> <string name="external_source_untrusted" msgid="5037891688911672227">"ਗੈਰ-ਮਨਜ਼ੂਰਸ਼ੁਦਾ"</string> <string name="install_other_apps" msgid="3232595082023199454">"ਅਗਿਆਤ ਐਪਾਂ ਦੀ ਸਥਾਪਨਾ"</string> @@ -628,6 +627,8 @@ <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"ਬਿਲਟ-ਇਨ ਸਪੀਕਰ"</string> <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"ਟੀਵੀ ਆਡੀਓ"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"ਕਨੈਕਟ ਕਰਨ ਵਿੱਚ ਸਮੱਸਿਆ ਆਈ। ਡੀਵਾਈਸ ਨੂੰ ਬੰਦ ਕਰਕੇ ਵਾਪਸ ਚਾਲੂ ਕਰੋ"</string> + <!-- no translation found for bluetooth_key_missing_subtext (1003639333895028298) --> + <skip /> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"ਤਾਰ ਵਾਲਾ ਆਡੀਓ ਡੀਵਾਈਸ"</string> <string name="help_label" msgid="3528360748637781274">"ਮਦਦ ਅਤੇ ਵਿਚਾਰ"</string> <string name="storage_category" msgid="2287342585424631813">"ਸਟੋਰੇਜ"</string> diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml index 462142468e26..fec5bf1469d8 100644 --- a/packages/SettingsLib/res/values-pl/strings.xml +++ b/packages/SettingsLib/res/values-pl/strings.xml @@ -539,8 +539,7 @@ <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Obowiązują ustawienia z ograniczonym dostępem"</string> <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Niedostępne w trakcie połączeń"</string> <string name="disabled" msgid="8017887509554714950">"Wyłączona"</string> - <!-- no translation found for enabled (3997122818554810678) --> - <skip /> + <string name="enabled" msgid="3997122818554810678">"Włączono"</string> <string name="external_source_trusted" msgid="1146522036773132905">"Dozwolone"</string> <string name="external_source_untrusted" msgid="5037891688911672227">"Niedozwolone"</string> <string name="install_other_apps" msgid="3232595082023199454">"Instalowanie nieznanych aplikacji"</string> @@ -626,8 +625,10 @@ <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Połączono przez ARC"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Połączono przez eARC"</string> <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Wbudowany głośnik"</string> - <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"Telewizyjne urządzenie audio"</string> + <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"Audio z telewizora"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problem z połączeniem. Wyłącz i ponownie włącz urządzenie"</string> + <!-- no translation found for bluetooth_key_missing_subtext (1003639333895028298) --> + <skip /> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Przewodowe urządzenie audio"</string> <string name="help_label" msgid="3528360748637781274">"Pomoc i opinie"</string> <string name="storage_category" msgid="2287342585424631813">"Pamięć wewnętrzna"</string> diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml index 482f47931f4a..62d879b8e663 100644 --- a/packages/SettingsLib/res/values-pt-rBR/strings.xml +++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml @@ -539,8 +539,7 @@ <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Controlada pelas configurações restritas"</string> <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Indisponível durante ligações"</string> <string name="disabled" msgid="8017887509554714950">"Desativado"</string> - <!-- no translation found for enabled (3997122818554810678) --> - <skip /> + <string name="enabled" msgid="3997122818554810678">"Ativado"</string> <string name="external_source_trusted" msgid="1146522036773132905">"Permitido"</string> <string name="external_source_untrusted" msgid="5037891688911672227">"Não permitido"</string> <string name="install_other_apps" msgid="3232595082023199454">"Instalar apps desconhecidos"</string> @@ -628,6 +627,8 @@ <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Alto-falante integrado"</string> <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"Áudio da TV"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Ocorreu um problema na conexão. Desligue o dispositivo e ligue-o novamente"</string> + <!-- no translation found for bluetooth_key_missing_subtext (1003639333895028298) --> + <skip /> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispositivo de áudio com fio"</string> <string name="help_label" msgid="3528360748637781274">"Ajuda e feedback"</string> <string name="storage_category" msgid="2287342585424631813">"Armazenamento"</string> diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml index d6ac53390403..3486037ba240 100644 --- a/packages/SettingsLib/res/values-pt-rPT/strings.xml +++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml @@ -610,7 +610,7 @@ <string name="media_transfer_default_device_name" msgid="4315604017399871828">"Dispositivo associado"</string> <string name="media_transfer_this_phone" msgid="7194341457812151531">"Este telemóvel"</string> <string name="media_transfer_digital_line_name" msgid="312091711951124301">"S/PDIF"</string> - <string name="media_transfer_analog_line_name" msgid="1841163866716302104">"Analógico"</string> + <string name="media_transfer_analog_line_name" msgid="1841163866716302104">"Analógica"</string> <string name="media_transfer_aux_line_name" msgid="894135835967856689">"AUX"</string> <string name="media_output_status_unknown_error" msgid="5098565887497902222">"Não é possível reproduzir neste dispositivo"</string> <string name="media_output_status_require_premium" msgid="8411255800047014822">"Atualize a conta para mudar"</string> @@ -627,6 +627,8 @@ <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Altifalante integrado"</string> <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"Áudio da TV"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problema ao ligar. Desligue e volte a ligar o dispositivo."</string> + <!-- no translation found for bluetooth_key_missing_subtext (1003639333895028298) --> + <skip /> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispositivo de áudio com fios"</string> <string name="help_label" msgid="3528360748637781274">"Ajuda e feedback"</string> <string name="storage_category" msgid="2287342585424631813">"Armazenamento"</string> diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml index 482f47931f4a..62d879b8e663 100644 --- a/packages/SettingsLib/res/values-pt/strings.xml +++ b/packages/SettingsLib/res/values-pt/strings.xml @@ -539,8 +539,7 @@ <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Controlada pelas configurações restritas"</string> <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Indisponível durante ligações"</string> <string name="disabled" msgid="8017887509554714950">"Desativado"</string> - <!-- no translation found for enabled (3997122818554810678) --> - <skip /> + <string name="enabled" msgid="3997122818554810678">"Ativado"</string> <string name="external_source_trusted" msgid="1146522036773132905">"Permitido"</string> <string name="external_source_untrusted" msgid="5037891688911672227">"Não permitido"</string> <string name="install_other_apps" msgid="3232595082023199454">"Instalar apps desconhecidos"</string> @@ -628,6 +627,8 @@ <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Alto-falante integrado"</string> <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"Áudio da TV"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Ocorreu um problema na conexão. Desligue o dispositivo e ligue-o novamente"</string> + <!-- no translation found for bluetooth_key_missing_subtext (1003639333895028298) --> + <skip /> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispositivo de áudio com fio"</string> <string name="help_label" msgid="3528360748637781274">"Ajuda e feedback"</string> <string name="storage_category" msgid="2287342585424631813">"Armazenamento"</string> diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml index 757fee9e110e..8965a0cfdd2f 100644 --- a/packages/SettingsLib/res/values-ro/strings.xml +++ b/packages/SettingsLib/res/values-ro/strings.xml @@ -539,8 +539,7 @@ <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Controlată de setarea restricționată"</string> <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Indisponibil în timpul apelurilor"</string> <string name="disabled" msgid="8017887509554714950">"Dezactivată"</string> - <!-- no translation found for enabled (3997122818554810678) --> - <skip /> + <string name="enabled" msgid="3997122818554810678">"Activată"</string> <string name="external_source_trusted" msgid="1146522036773132905">"Permise"</string> <string name="external_source_untrusted" msgid="5037891688911672227">"Nepermise"</string> <string name="install_other_apps" msgid="3232595082023199454">"Instalarea aplicațiilor necunoscute"</string> @@ -628,6 +627,8 @@ <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Difuzor încorporat"</string> <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"Audio TV"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problemă la conectare. Oprește și repornește dispozitivul."</string> + <!-- no translation found for bluetooth_key_missing_subtext (1003639333895028298) --> + <skip /> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Dispozitiv audio cu fir"</string> <string name="help_label" msgid="3528360748637781274">"Ajutor și feedback"</string> <string name="storage_category" msgid="2287342585424631813">"Stocare"</string> diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml index 9e922a0b85b3..2da67085adf6 100644 --- a/packages/SettingsLib/res/values-ru/strings.xml +++ b/packages/SettingsLib/res/values-ru/strings.xml @@ -539,8 +539,7 @@ <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Контролируется настройками с ограниченным доступом"</string> <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Недоступно во время вызовов"</string> <string name="disabled" msgid="8017887509554714950">"Отключено"</string> - <!-- no translation found for enabled (3997122818554810678) --> - <skip /> + <string name="enabled" msgid="3997122818554810678">"Включено"</string> <string name="external_source_trusted" msgid="1146522036773132905">"Разрешено"</string> <string name="external_source_untrusted" msgid="5037891688911672227">"Запрещено"</string> <string name="install_other_apps" msgid="3232595082023199454">"Установка неизвестных приложений"</string> @@ -628,6 +627,8 @@ <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Встроенный динамик"</string> <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"Аудиовыход телевизора"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Ошибка подключения. Выключите и снова включите устройство."</string> + <!-- no translation found for bluetooth_key_missing_subtext (1003639333895028298) --> + <skip /> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Проводное аудиоустройство"</string> <string name="help_label" msgid="3528360748637781274">"Справка/отзыв"</string> <string name="storage_category" msgid="2287342585424631813">"Хранилище"</string> diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml index 517b00c61efb..1c57acd16887 100644 --- a/packages/SettingsLib/res/values-si/strings.xml +++ b/packages/SettingsLib/res/values-si/strings.xml @@ -539,8 +539,7 @@ <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"සීමා කළ සැකසීම මගින් පාලනය වේ"</string> <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"ඇමතුම් අතරතුර නොපවතී"</string> <string name="disabled" msgid="8017887509554714950">"අබල කර ඇත"</string> - <!-- no translation found for enabled (3997122818554810678) --> - <skip /> + <string name="enabled" msgid="3997122818554810678">"සබලයි"</string> <string name="external_source_trusted" msgid="1146522036773132905">"ඉඩ දුන්"</string> <string name="external_source_untrusted" msgid="5037891688911672227">"ඉඩ නොදෙන"</string> <string name="install_other_apps" msgid="3232595082023199454">"නොදන්නා යෙදුම් ස්ථාපනය"</string> @@ -628,6 +627,8 @@ <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"එකට තැනූ ශබ්දවාහිනීය"</string> <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"රූපවාහිනී ශ්රව්ය"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"සම්බන්ධ කිරීමේ ගැටලුවකි උපාංගය ක්රියාවිරහිත කර & ආපසු ක්රියාත්මක කරන්න"</string> + <!-- no translation found for bluetooth_key_missing_subtext (1003639333895028298) --> + <skip /> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"රැහැන්ගත කළ ඕඩියෝ උපාංගය"</string> <string name="help_label" msgid="3528360748637781274">"උදවු & ප්රතිපෝෂණ"</string> <string name="storage_category" msgid="2287342585424631813">"ගබඩාව"</string> diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml index e8fb53fdcef6..5534ed081057 100644 --- a/packages/SettingsLib/res/values-sk/strings.xml +++ b/packages/SettingsLib/res/values-sk/strings.xml @@ -539,8 +539,7 @@ <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Ovládané obmedzeným nastavením"</string> <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Počas hovorov nie je k dispozícii"</string> <string name="disabled" msgid="8017887509554714950">"Deaktivované"</string> - <!-- no translation found for enabled (3997122818554810678) --> - <skip /> + <string name="enabled" msgid="3997122818554810678">"Zapnuté"</string> <string name="external_source_trusted" msgid="1146522036773132905">"Povolené"</string> <string name="external_source_untrusted" msgid="5037891688911672227">"Nie je povolené"</string> <string name="install_other_apps" msgid="3232595082023199454">"Inštalácia neznámych aplikácií"</string> @@ -628,6 +627,8 @@ <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Vstavaný reproduktor"</string> <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"Zvuk televízora"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Pri pripájaní sa vyskytol problém. Zariadenie vypnite a znova zapnite."</string> + <!-- no translation found for bluetooth_key_missing_subtext (1003639333895028298) --> + <skip /> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Audio zariadenie s káblom"</string> <string name="help_label" msgid="3528360748637781274">"Pomocník a spätná väzba"</string> <string name="storage_category" msgid="2287342585424631813">"Priestor"</string> diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml index bae7722cc6e9..be7b11587468 100644 --- a/packages/SettingsLib/res/values-sl/strings.xml +++ b/packages/SettingsLib/res/values-sl/strings.xml @@ -627,6 +627,8 @@ <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Vgrajen zvočnik"</string> <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"Zvok televizorja"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Težava pri povezovanju. Napravo izklopite in znova vklopite."</string> + <!-- no translation found for bluetooth_key_missing_subtext (1003639333895028298) --> + <skip /> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Žična zvočna naprava"</string> <string name="help_label" msgid="3528360748637781274">"Pomoč in povratne informacije"</string> <string name="storage_category" msgid="2287342585424631813">"Shramba"</string> diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml index 8a711d5ebcee..06da1722ad75 100644 --- a/packages/SettingsLib/res/values-sq/strings.xml +++ b/packages/SettingsLib/res/values-sq/strings.xml @@ -539,8 +539,7 @@ <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Kontrollohet nga \"Cilësimet e kufizuara\""</string> <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Nuk ofrohet gjatë telefonatave"</string> <string name="disabled" msgid="8017887509554714950">"Çaktivizuar"</string> - <!-- no translation found for enabled (3997122818554810678) --> - <skip /> + <string name="enabled" msgid="3997122818554810678">"Aktivizuar"</string> <string name="external_source_trusted" msgid="1146522036773132905">"Lejohet"</string> <string name="external_source_untrusted" msgid="5037891688911672227">"Nuk lejohet"</string> <string name="install_other_apps" msgid="3232595082023199454">"Instalo aplikacione të panjohura"</string> @@ -628,6 +627,8 @@ <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Altoparlanti i integruar"</string> <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"Audioja e televizorit"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Problem me lidhjen. Fike dhe ndize përsëri pajisjen"</string> + <!-- no translation found for bluetooth_key_missing_subtext (1003639333895028298) --> + <skip /> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Pajisja audio me tel"</string> <string name="help_label" msgid="3528360748637781274">"Ndihma dhe komentet"</string> <string name="storage_category" msgid="2287342585424631813">"Hapësira ruajtëse"</string> diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml index d57eb5c4c830..63d1502c7809 100644 --- a/packages/SettingsLib/res/values-sr/strings.xml +++ b/packages/SettingsLib/res/values-sr/strings.xml @@ -539,8 +539,7 @@ <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Контролишу ограничена подешавања"</string> <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Недоступно током позива"</string> <string name="disabled" msgid="8017887509554714950">"Онемогућено"</string> - <!-- no translation found for enabled (3997122818554810678) --> - <skip /> + <string name="enabled" msgid="3997122818554810678">"Омогућено"</string> <string name="external_source_trusted" msgid="1146522036773132905">"Дозвољено"</string> <string name="external_source_untrusted" msgid="5037891688911672227">"Није дозвољено"</string> <string name="install_other_apps" msgid="3232595082023199454">"Инсталирање непознатих апликација"</string> @@ -628,6 +627,8 @@ <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Уграђени звучник"</string> <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"Звук ТВ-а"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Проблем при повезивању. Искључите уређај, па га поново укључите"</string> + <!-- no translation found for bluetooth_key_missing_subtext (1003639333895028298) --> + <skip /> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Жичани аудио уређај"</string> <string name="help_label" msgid="3528360748637781274">"Помоћ и повратне информације"</string> <string name="storage_category" msgid="2287342585424631813">"Меморијски простор"</string> diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml index 5d27839daa6a..131cca795bb2 100644 --- a/packages/SettingsLib/res/values-sv/strings.xml +++ b/packages/SettingsLib/res/values-sv/strings.xml @@ -539,8 +539,7 @@ <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Styrs av spärrad inställning"</string> <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Ej tillgänglig under samtal"</string> <string name="disabled" msgid="8017887509554714950">"Inaktiverad"</string> - <!-- no translation found for enabled (3997122818554810678) --> - <skip /> + <string name="enabled" msgid="3997122818554810678">"Aktiverat"</string> <string name="external_source_trusted" msgid="1146522036773132905">"Tillåts"</string> <string name="external_source_untrusted" msgid="5037891688911672227">"Tillåts inte"</string> <string name="install_other_apps" msgid="3232595082023199454">"Installera okända appar"</string> @@ -628,6 +627,8 @@ <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Inbyggd högtalare"</string> <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"Tv-ljud"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Det gick inte att ansluta. Stäng av enheten och slå på den igen"</string> + <!-- no translation found for bluetooth_key_missing_subtext (1003639333895028298) --> + <skip /> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Ljudenhet med kabelanslutning"</string> <string name="help_label" msgid="3528360748637781274">"Hjälp och feedback"</string> <string name="storage_category" msgid="2287342585424631813">"Lagring"</string> diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml index 9ddb7ff129ad..1cb95a7ccde6 100644 --- a/packages/SettingsLib/res/values-sw/strings.xml +++ b/packages/SettingsLib/res/values-sw/strings.xml @@ -539,8 +539,7 @@ <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Imedhibitiwa na Mpangilio wenye Mipaka"</string> <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Haipatikani wakati unaongea kwa simu"</string> <string name="disabled" msgid="8017887509554714950">"Imezimwa"</string> - <!-- no translation found for enabled (3997122818554810678) --> - <skip /> + <string name="enabled" msgid="3997122818554810678">"Imewashwa"</string> <string name="external_source_trusted" msgid="1146522036773132905">"Imeruhusiwa"</string> <string name="external_source_untrusted" msgid="5037891688911672227">"Hairuhusiwi"</string> <string name="install_other_apps" msgid="3232595082023199454">"Kuweka programu zisizojulikana"</string> @@ -625,9 +624,11 @@ <string name="tv_media_transfer_earc_fallback_title" msgid="3098685494578519940">"HDMI eARC"</string> <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Imeunganishwa kupitia ARC"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Imeunganishwa kupitia eARC"</string> - <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Spika iliyojumuishwa ndani"</string> + <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Spika iliyojumuishwa"</string> <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"Sauti ya Televisheni"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Kuna tatizo la kuunganisha kwenye Intaneti. Zima kisha uwashe kifaa"</string> + <!-- no translation found for bluetooth_key_missing_subtext (1003639333895028298) --> + <skip /> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Kifaa cha sauti kinachotumia waya"</string> <string name="help_label" msgid="3528360748637781274">"Usaidizi na maoni"</string> <string name="storage_category" msgid="2287342585424631813">"Hifadhi"</string> diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml index 68a60cf85a90..cc8a36fac3ce 100644 --- a/packages/SettingsLib/res/values-ta/strings.xml +++ b/packages/SettingsLib/res/values-ta/strings.xml @@ -539,8 +539,7 @@ <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"வரையறுக்கப்பட்ட அமைப்பால் கட்டுப்படுத்தப்படுகிறது"</string> <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"அழைப்புகளின்போது பயன்படுத்த முடியாது"</string> <string name="disabled" msgid="8017887509554714950">"முடக்கப்பட்டது"</string> - <!-- no translation found for enabled (3997122818554810678) --> - <skip /> + <string name="enabled" msgid="3997122818554810678">"இயக்கப்பட்டது"</string> <string name="external_source_trusted" msgid="1146522036773132905">"அனுமதிக்கப்பட்டது"</string> <string name="external_source_untrusted" msgid="5037891688911672227">"அனுமதிக்கப்படவில்லை"</string> <string name="install_other_apps" msgid="3232595082023199454">"தெரியாத ஆப்ஸ்களை நிறுவுதல்"</string> @@ -628,6 +627,8 @@ <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"உள்ளமைந்த ஸ்பீக்கர்"</string> <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"டிவி ஆடியோ"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"இணைப்பதில் சிக்கல். சாதனத்தை ஆஃப் செய்து மீண்டும் ஆன் செய்யவும்"</string> + <!-- no translation found for bluetooth_key_missing_subtext (1003639333895028298) --> + <skip /> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"வயருடன்கூடிய ஆடியோ சாதனம்"</string> <string name="help_label" msgid="3528360748637781274">"உதவியும் கருத்தும்"</string> <string name="storage_category" msgid="2287342585424631813">"சேமிப்பகம்"</string> diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml index c2f284e0acb6..ccb5aa8cd140 100644 --- a/packages/SettingsLib/res/values-te/strings.xml +++ b/packages/SettingsLib/res/values-te/strings.xml @@ -627,6 +627,8 @@ <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"బిల్ట్-ఇన్ స్పీకర్"</string> <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"టీవీ ఆడియో"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"కనెక్ట్ చేయడంలో సమస్య ఉంది. పరికరాన్ని ఆఫ్ చేసి, ఆపై తిరిగి ఆన్ చేయండి"</string> + <!-- no translation found for bluetooth_key_missing_subtext (1003639333895028298) --> + <skip /> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"వైర్ గల ఆడియో పరికరం"</string> <string name="help_label" msgid="3528360748637781274">"సహాయం & ఫీడ్బ్యాక్"</string> <string name="storage_category" msgid="2287342585424631813">"స్టోరేజ్"</string> diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml index 035644f845f1..08d2017e8418 100644 --- a/packages/SettingsLib/res/values-th/strings.xml +++ b/packages/SettingsLib/res/values-th/strings.xml @@ -539,8 +539,7 @@ <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"ควบคุมโดยการตั้งค่าที่จำกัด"</string> <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"ใช้งานไม่ได้ขณะสนทนาโทรศัพท์"</string> <string name="disabled" msgid="8017887509554714950">"ปิดอยู่"</string> - <!-- no translation found for enabled (3997122818554810678) --> - <skip /> + <string name="enabled" msgid="3997122818554810678">"เปิดใช้อยู่"</string> <string name="external_source_trusted" msgid="1146522036773132905">"อนุญาต"</string> <string name="external_source_untrusted" msgid="5037891688911672227">"ไม่อนุญาต"</string> <string name="install_other_apps" msgid="3232595082023199454">"ติดตั้งแอปที่ไม่รู้จัก"</string> @@ -628,6 +627,8 @@ <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"ลำโพงในตัว"</string> <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"เสียงจากทีวี"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"เกิดปัญหาในการเชื่อมต่อ ปิดอุปกรณ์แล้วเปิดใหม่อีกครั้ง"</string> + <!-- no translation found for bluetooth_key_missing_subtext (1003639333895028298) --> + <skip /> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"อุปกรณ์เสียงแบบมีสาย"</string> <string name="help_label" msgid="3528360748637781274">"ความช่วยเหลือและความคิดเห็น"</string> <string name="storage_category" msgid="2287342585424631813">"พื้นที่เก็บข้อมูล"</string> diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml index 4ef2f436898a..14069b9db21d 100644 --- a/packages/SettingsLib/res/values-tl/strings.xml +++ b/packages/SettingsLib/res/values-tl/strings.xml @@ -539,8 +539,7 @@ <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Kinokontrol ng Pinaghihigpitang Setting"</string> <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Hindi available habang may tawag"</string> <string name="disabled" msgid="8017887509554714950">"Naka-disable"</string> - <!-- no translation found for enabled (3997122818554810678) --> - <skip /> + <string name="enabled" msgid="3997122818554810678">"Naka-enable"</string> <string name="external_source_trusted" msgid="1146522036773132905">"Pinapayagan"</string> <string name="external_source_untrusted" msgid="5037891688911672227">"Hindi pinapayagan"</string> <string name="install_other_apps" msgid="3232595082023199454">"Mag-install ng di-kilalang app"</string> @@ -628,6 +627,8 @@ <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Built-in na speaker"</string> <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"Audio ng TV"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Nagkaproblema sa pagkonekta. I-off at pagkatapos ay i-on ang device"</string> + <!-- no translation found for bluetooth_key_missing_subtext (1003639333895028298) --> + <skip /> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Wired na audio device"</string> <string name="help_label" msgid="3528360748637781274">"Tulong at feedback"</string> <string name="storage_category" msgid="2287342585424631813">"Storage"</string> diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml index 19295aa169ef..70210030d7a9 100644 --- a/packages/SettingsLib/res/values-tr/strings.xml +++ b/packages/SettingsLib/res/values-tr/strings.xml @@ -539,8 +539,7 @@ <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Kısıtlanmış ayar tarafından kontrol ediliyor"</string> <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Telefon aramaları sırasında kullanılamaz"</string> <string name="disabled" msgid="8017887509554714950">"Devre dışı"</string> - <!-- no translation found for enabled (3997122818554810678) --> - <skip /> + <string name="enabled" msgid="3997122818554810678">"Etkin"</string> <string name="external_source_trusted" msgid="1146522036773132905">"İzin verildi"</string> <string name="external_source_untrusted" msgid="5037891688911672227">"İzin verilmiyor"</string> <string name="install_other_apps" msgid="3232595082023199454">"Bilinmeyen uygulamaları yükleme"</string> @@ -628,6 +627,8 @@ <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Dahili hoparlör"</string> <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"TV Sesi"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Bağlanırken sorun oluştu. Cihazı kapatıp tekrar açın"</string> + <!-- no translation found for bluetooth_key_missing_subtext (1003639333895028298) --> + <skip /> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Kablolu ses cihazı"</string> <string name="help_label" msgid="3528360748637781274">"Yardım ve geri bildirim"</string> <string name="storage_category" msgid="2287342585424631813">"Depolama"</string> diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml index e46137fd237e..e8fb02e1ee82 100644 --- a/packages/SettingsLib/res/values-uk/strings.xml +++ b/packages/SettingsLib/res/values-uk/strings.xml @@ -539,8 +539,7 @@ <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Керується налаштуваннями з обмеженнями"</string> <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Недоступно під час викликів"</string> <string name="disabled" msgid="8017887509554714950">"Вимкнено"</string> - <!-- no translation found for enabled (3997122818554810678) --> - <skip /> + <string name="enabled" msgid="3997122818554810678">"Увімкнено"</string> <string name="external_source_trusted" msgid="1146522036773132905">"Дозволено"</string> <string name="external_source_untrusted" msgid="5037891688911672227">"Заборонено"</string> <string name="install_other_apps" msgid="3232595082023199454">"Встановлювати невідомі додатки"</string> @@ -628,6 +627,8 @@ <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Вбудований динамік"</string> <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"Аудіо з телевізора"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Не вдається підключитися. Перезавантажте пристрій."</string> + <!-- no translation found for bluetooth_key_missing_subtext (1003639333895028298) --> + <skip /> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Дротовий аудіопристрій"</string> <string name="help_label" msgid="3528360748637781274">"Довідка й відгуки"</string> <string name="storage_category" msgid="2287342585424631813">"Сховище"</string> diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml index e4b4001352cd..a0ff95a579b0 100644 --- a/packages/SettingsLib/res/values-ur/strings.xml +++ b/packages/SettingsLib/res/values-ur/strings.xml @@ -627,6 +627,8 @@ <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"پہلے سے شامل اسپیکر"</string> <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"TV آڈیو"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"منسلک کرنے میں مسئلہ پیش آ گیا۔ آلہ کو آف اور بیک آن کریں"</string> + <!-- no translation found for bluetooth_key_missing_subtext (1003639333895028298) --> + <skip /> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"وائرڈ آڈیو آلہ"</string> <string name="help_label" msgid="3528360748637781274">"مدد اور تاثرات"</string> <string name="storage_category" msgid="2287342585424631813">"اسٹوریج"</string> diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml index 2608a0440804..0a6588511647 100644 --- a/packages/SettingsLib/res/values-uz/strings.xml +++ b/packages/SettingsLib/res/values-uz/strings.xml @@ -627,6 +627,8 @@ <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Ichki karnay"</string> <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"TV Audio"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Ulanishda muammo yuz berdi. Qurilmani oʻchiring va yoqing"</string> + <!-- no translation found for bluetooth_key_missing_subtext (1003639333895028298) --> + <skip /> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Simli audio qurilma"</string> <string name="help_label" msgid="3528360748637781274">"Yordam/fikr-mulohaza"</string> <string name="storage_category" msgid="2287342585424631813">"Xotira"</string> diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml index 8f5c0c2675e4..5e1e3dad0034 100644 --- a/packages/SettingsLib/res/values-vi/strings.xml +++ b/packages/SettingsLib/res/values-vi/strings.xml @@ -539,8 +539,7 @@ <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Do chế độ Cài đặt hạn chế kiểm soát"</string> <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Không dùng được khi có cuộc gọi"</string> <string name="disabled" msgid="8017887509554714950">"Đã tắt"</string> - <!-- no translation found for enabled (3997122818554810678) --> - <skip /> + <string name="enabled" msgid="3997122818554810678">"Đã bật"</string> <string name="external_source_trusted" msgid="1146522036773132905">"Được phép"</string> <string name="external_source_untrusted" msgid="5037891688911672227">"Không được phép"</string> <string name="install_other_apps" msgid="3232595082023199454">"Cài ứng dụng không rõ nguồn"</string> @@ -628,6 +627,8 @@ <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Loa tích hợp"</string> <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"Âm thanh TV"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Sự cố kết nối. Hãy tắt thiết bị rồi bật lại"</string> + <!-- no translation found for bluetooth_key_missing_subtext (1003639333895028298) --> + <skip /> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Thiết bị âm thanh có dây"</string> <string name="help_label" msgid="3528360748637781274">"Trợ giúp và phản hồi"</string> <string name="storage_category" msgid="2287342585424631813">"Bộ nhớ"</string> diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml index 85d3c179a623..6bf27b35d4fe 100644 --- a/packages/SettingsLib/res/values-zh-rCN/strings.xml +++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml @@ -539,8 +539,7 @@ <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"由受限设置控制"</string> <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"通话期间无法使用"</string> <string name="disabled" msgid="8017887509554714950">"已停用"</string> - <!-- no translation found for enabled (3997122818554810678) --> - <skip /> + <string name="enabled" msgid="3997122818554810678">"已启用"</string> <string name="external_source_trusted" msgid="1146522036773132905">"允许"</string> <string name="external_source_untrusted" msgid="5037891688911672227">"不允许"</string> <string name="install_other_apps" msgid="3232595082023199454">"安装未知应用"</string> @@ -628,6 +627,8 @@ <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"内置扬声器"</string> <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"电视音频"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"连接时遇到问题。请关闭并重新开启设备"</string> + <!-- no translation found for bluetooth_key_missing_subtext (1003639333895028298) --> + <skip /> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"有线音频设备"</string> <string name="help_label" msgid="3528360748637781274">"帮助和反馈"</string> <string name="storage_category" msgid="2287342585424631813">"存储空间"</string> diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml index 3ff554000a6c..9f96b5bf44b1 100644 --- a/packages/SettingsLib/res/values-zh-rHK/strings.xml +++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml @@ -539,8 +539,7 @@ <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"由「受限設定」控制"</string> <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"通話時無法使用"</string> <string name="disabled" msgid="8017887509554714950">"已停用"</string> - <!-- no translation found for enabled (3997122818554810678) --> - <skip /> + <string name="enabled" msgid="3997122818554810678">"已啟用"</string> <string name="external_source_trusted" msgid="1146522036773132905">"允許"</string> <string name="external_source_untrusted" msgid="5037891688911672227">"不允許"</string> <string name="install_other_apps" msgid="3232595082023199454">"安裝不明的應用程式"</string> @@ -602,7 +601,7 @@ <string name="zen_mode_forever" msgid="3339224497605461291">"直至你關閉為止"</string> <string name="zen_mode_starred_contacts_empty_name" msgid="933552939706125937">"(沒有名稱)"</string> <string name="time_unit_just_now" msgid="3006134267292728099">"剛剛"</string> - <string name="media_transfer_this_device_name" msgid="2357329267148436433">"此手機"</string> + <string name="media_transfer_this_device_name" msgid="2357329267148436433">"這部手機"</string> <string name="media_transfer_this_device_name_tablet" msgid="2975593806278422086">"此平板電腦"</string> <string name="media_transfer_this_device_name_desktop" msgid="7912386128141470452">"此電腦 (內置)"</string> <string name="media_transfer_this_device_name_tv" msgid="8508713779441163887">"這部電視"</string> @@ -628,6 +627,8 @@ <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"內置喇叭"</string> <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"電視音訊"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"無法連接,請關閉裝置然後重新開機"</string> + <!-- no translation found for bluetooth_key_missing_subtext (1003639333895028298) --> + <skip /> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"有線音響裝置"</string> <string name="help_label" msgid="3528360748637781274">"說明與意見反映"</string> <string name="storage_category" msgid="2287342585424631813">"儲存空間"</string> diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml index 5362d380564f..e5f29e904e0a 100644 --- a/packages/SettingsLib/res/values-zh-rTW/strings.xml +++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml @@ -539,8 +539,7 @@ <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"由受限制的設定控管"</string> <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"通話時無法使用"</string> <string name="disabled" msgid="8017887509554714950">"已停用"</string> - <!-- no translation found for enabled (3997122818554810678) --> - <skip /> + <string name="enabled" msgid="3997122818554810678">"已啟用"</string> <string name="external_source_trusted" msgid="1146522036773132905">"允許"</string> <string name="external_source_untrusted" msgid="5037891688911672227">"不允許"</string> <string name="install_other_apps" msgid="3232595082023199454">"安裝不明應用程式"</string> @@ -628,6 +627,8 @@ <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"內建喇叭"</string> <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"電視音訊"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"無法連線,請關閉裝置後再重新開啟"</string> + <!-- no translation found for bluetooth_key_missing_subtext (1003639333895028298) --> + <skip /> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"有線音訊裝置"</string> <string name="help_label" msgid="3528360748637781274">"說明與意見回饋"</string> <string name="storage_category" msgid="2287342585424631813">"儲存空間"</string> diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml index 79ce6d7b25d0..c587678056e8 100644 --- a/packages/SettingsLib/res/values-zu/strings.xml +++ b/packages/SettingsLib/res/values-zu/strings.xml @@ -539,8 +539,7 @@ <string name="disabled_by_app_ops_text" msgid="8373595926549098012">"Kulawulwe Isethingi Elikhawulelwe"</string> <string name="disabled_in_phone_call_text" msgid="6568931334337318320">"Akutholakali ngesikhathi samakholi"</string> <string name="disabled" msgid="8017887509554714950">"Akusebenzi"</string> - <!-- no translation found for enabled (3997122818554810678) --> - <skip /> + <string name="enabled" msgid="3997122818554810678">"Ukwenza kusebenze"</string> <string name="external_source_trusted" msgid="1146522036773132905">"Kuvumelekile"</string> <string name="external_source_untrusted" msgid="5037891688911672227">"Akuvumelekile"</string> <string name="install_other_apps" msgid="3232595082023199454">"Faka ama-app angaziwa"</string> @@ -626,8 +625,10 @@ <string name="tv_media_transfer_arc_subtitle" msgid="1040017851325069082">"Ixhunywe nge-ARC"</string> <string name="tv_media_transfer_earc_subtitle" msgid="645191413103303077">"Ixhunywe nge-eARC"</string> <string name="tv_media_transfer_internal_speakers" msgid="4662765121700213785">"Isipikha esakhelwe ngaphakathi"</string> - <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"Umsondo weTV"</string> + <string name="tv_media_transfer_hdmi_title" msgid="6715658310934507444">"Umsindo we-TV"</string> <string name="profile_connect_timeout_subtext" msgid="4043408193005851761">"Inkinga yokuxhumeka. Vala idivayisi futhi uphinde uyivule"</string> + <!-- no translation found for bluetooth_key_missing_subtext (1003639333895028298) --> + <skip /> <string name="media_transfer_wired_device_name" msgid="4447880899964056007">"Idivayisi yomsindo enentambo"</string> <string name="help_label" msgid="3528360748637781274">"Usizo nempendulo"</string> <string name="storage_category" msgid="2287342585424631813">"Isitoreji"</string> diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java index ae9ad958b287..33dcb051d194 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothUtils.java @@ -1068,18 +1068,42 @@ public class BluetoothUtils { /** Get primary device Uri in broadcast. */ @NonNull public static String getPrimaryGroupIdUriForBroadcast() { + // TODO: once API is stable, deprecate SettingsProvider solution return "bluetooth_le_broadcast_fallback_active_group_id"; } - /** Get primary device group id in broadcast. */ + /** Get primary device group id in broadcast from SettingsProvider. */ @WorkerThread public static int getPrimaryGroupIdForBroadcast(@NonNull ContentResolver contentResolver) { + // TODO: once API is stable, deprecate SettingsProvider solution return Settings.Secure.getInt( contentResolver, getPrimaryGroupIdUriForBroadcast(), BluetoothCsipSetCoordinator.GROUP_ID_INVALID); } + /** + * Get primary device group id in broadcast. + * + * If Flags.adoptPrimaryGroupManagementApiV2 is enabled, get group id by API, + * Otherwise, still get value from SettingsProvider. + */ + @WorkerThread + public static int getPrimaryGroupIdForBroadcast(@NonNull ContentResolver contentResolver, + @Nullable LocalBluetoothManager manager) { + if (Flags.adoptPrimaryGroupManagementApiV2()) { + LeAudioProfile leaProfile = manager == null ? null : + manager.getProfileManager().getLeAudioProfile(); + if (leaProfile == null) { + Log.d(TAG, "getPrimaryGroupIdForBroadcast: profile is null"); + return BluetoothCsipSetCoordinator.GROUP_ID_INVALID; + } + return leaProfile.getBroadcastToUnicastFallbackGroup(); + } else { + return getPrimaryGroupIdForBroadcast(contentResolver); + } + } + /** Get develop option value for audio sharing preview. */ @WorkerThread public static boolean getAudioSharingPreviewValue(@Nullable ContentResolver contentResolver) { @@ -1101,7 +1125,7 @@ public class BluetoothUtils { LocalBluetoothLeBroadcast broadcast = localBtManager.getProfileManager().getLeAudioBroadcastProfile(); if (broadcast == null || !broadcast.isEnabled(null)) return null; - int primaryGroupId = getPrimaryGroupIdForBroadcast(contentResolver); + int primaryGroupId = getPrimaryGroupIdForBroadcast(contentResolver, localBtManager); if (primaryGroupId == BluetoothCsipSetCoordinator.GROUP_ID_INVALID) return null; LocalBluetoothLeBroadcastAssistant assistant = localBtManager.getProfileManager().getLeAudioBroadcastAssistantProfile(); diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java index 011b2fc15807..edec2e427315 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java @@ -75,6 +75,7 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.Executor; +import java.util.stream.IntStream; import java.util.stream.Stream; /** @@ -154,8 +155,8 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> private boolean mIsLeAudioProfileConnectedFail = false; private boolean mUnpairing; @Nullable - private final InputDevice mInputDevice; - private final boolean mIsDeviceStylus; + private InputDevice mInputDevice; + private boolean mIsDeviceStylus; // Group second device for Hearing Aid private CachedBluetoothDevice mSubDevice; @@ -313,8 +314,7 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> mLocalNapRoleConnected = true; } } - if (Flags.enableSetPreferredTransportForLeAudioDevice() - && profile instanceof HidProfile) { + if (profile instanceof HidProfile) { updatePreferredTransport(); } } else if (profile instanceof MapProfile @@ -329,8 +329,7 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> mLocalNapRoleConnected = false; } - if (Flags.enableSetPreferredTransportForLeAudioDevice() - && profile instanceof LeAudioProfile) { + if (profile instanceof LeAudioProfile) { updatePreferredTransport(); } @@ -762,11 +761,8 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> * {@link BluetoothDevice#BATTERY_LEVEL_UNKNOWN} */ public int getMinBatteryLevelWithMemberDevices() { - return Stream.concat(Stream.of(this), mMemberDevices.stream()) - .mapToInt(cachedDevice -> cachedDevice.getBatteryLevel()) - .filter(batteryLevel -> batteryLevel > BluetoothDevice.BATTERY_LEVEL_UNKNOWN) - .min() - .orElse(BluetoothDevice.BATTERY_LEVEL_UNKNOWN); + return getMinBatteryLevels(Stream.concat(Stream.of(this), mMemberDevices.stream()) + .mapToInt(CachedBluetoothDevice::getBatteryLevel)); } /** @@ -789,6 +785,13 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> : null; } + private int getMinBatteryLevels(IntStream batteryLevels) { + return batteryLevels + .filter(battery -> battery > BluetoothDevice.BATTERY_LEVEL_UNKNOWN) + .min() + .orElse(BluetoothDevice.BATTERY_LEVEL_UNKNOWN); + } + void refresh() { ListenableFuture<Void> future = ThreadUtils.getBackgroundExecutor().submit(() -> { if (BluetoothUtils.isAdvancedDetailsHeader(mDevice)) { @@ -1358,7 +1361,7 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> // Gets summary for the buds which are in the audio sharing. int groupId = BluetoothUtils.getGroupId(this); int primaryGroupId = BluetoothUtils.getPrimaryGroupIdForBroadcast( - mContext.getContentResolver()); + mContext.getContentResolver(), mBluetoothManager); if ((primaryGroupId != BluetoothCsipSetCoordinator.GROUP_ID_INVALID) ? (groupId == primaryGroupId) : isActiveDevice(BluetoothProfile.LE_AUDIO)) { // The buds are primary buds @@ -1674,10 +1677,8 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> return null; } else { int overallBattery = - Arrays.stream(new int[]{leftBattery, rightBattery, caseBattery}) - .filter(battery -> battery > BluetoothDevice.BATTERY_LEVEL_UNKNOWN) - .min() - .orElse(BluetoothDevice.BATTERY_LEVEL_UNKNOWN); + getMinBatteryLevels( + Arrays.stream(new int[]{leftBattery, rightBattery, caseBattery})); Log.d(TAG, "Acquired battery info from metadata for untethered device " + mDevice.getAnonymizedAddress() + " left earbud battery: " + leftBattery @@ -1711,10 +1712,75 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> @Nullable private BatteryLevelsInfo getBatteryFromBluetoothService() { - // TODO(b/397847825): Implement the logic to get battery from Bluetooth service. - return null; + BatteryLevelsInfo batteryLevelsInfo; + if (isConnectedHearingAidDevice()) { + // If the device is hearing aid device, sides can be distinguished by HearingAidInfo. + batteryLevelsInfo = getBatteryOfHearingAidDeviceComponents(); + if (batteryLevelsInfo != null) { + return batteryLevelsInfo; + } + } + if (isConnectedLeAudioDevice()) { + // If the device is LE Audio device, sides can be distinguished by LeAudioProfile. + batteryLevelsInfo = getBatteryOfLeAudioDeviceComponents(); + if (batteryLevelsInfo != null) { + return batteryLevelsInfo; + } + } + int overallBattery = getMinBatteryLevelWithMemberDevices(); + return overallBattery > BluetoothDevice.BATTERY_LEVEL_UNKNOWN + ? new BatteryLevelsInfo( + BluetoothDevice.BATTERY_LEVEL_UNKNOWN, + BluetoothDevice.BATTERY_LEVEL_UNKNOWN, + BluetoothDevice.BATTERY_LEVEL_UNKNOWN, + overallBattery) + : null; } + @Nullable + private BatteryLevelsInfo getBatteryOfHearingAidDeviceComponents() { + if (getDeviceSide() == HearingAidInfo.DeviceSide.SIDE_LEFT_AND_RIGHT) { + return new BatteryLevelsInfo( + BluetoothDevice.BATTERY_LEVEL_UNKNOWN, + BluetoothDevice.BATTERY_LEVEL_UNKNOWN, + BluetoothDevice.BATTERY_LEVEL_UNKNOWN, + mDevice.getBatteryLevel()); + } + + int leftBattery = getHearingAidSideBattery(HearingAidInfo.DeviceSide.SIDE_LEFT); + int rightBattery = getHearingAidSideBattery(HearingAidInfo.DeviceSide.SIDE_RIGHT); + int overallBattery = getMinBatteryLevels( + Arrays.stream(new int[]{leftBattery, rightBattery})); + + Log.d(TAG, "Acquired battery info from Bluetooth service for hearing aid device " + + mDevice.getAnonymizedAddress() + + " left battery: " + leftBattery + + " right battery: " + rightBattery + + " overall battery: " + overallBattery); + return overallBattery > BluetoothDevice.BATTERY_LEVEL_UNKNOWN + ? new BatteryLevelsInfo( + leftBattery, + rightBattery, + BluetoothDevice.BATTERY_LEVEL_UNKNOWN, + overallBattery) + : null; + } + + private int getHearingAidSideBattery(int side) { + Optional<CachedBluetoothDevice> connectedHearingAidSide = getConnectedHearingAidSide(side); + return connectedHearingAidSide.isPresent() + ? connectedHearingAidSide + .map(CachedBluetoothDevice::getBatteryLevel) + .filter(batteryLevel -> batteryLevel > BluetoothDevice.BATTERY_LEVEL_UNKNOWN) + .orElse(BluetoothDevice.BATTERY_LEVEL_UNKNOWN) + : BluetoothDevice.BATTERY_LEVEL_UNKNOWN; + } + + @Nullable + private BatteryLevelsInfo getBatteryOfLeAudioDeviceComponents() { + // TODO(b/397847825): Implement the logic to get battery of LE audio device components. + return null; + } private CharSequence getTvBatterySummary(int mainBattery, int leftBattery, int rightBattery, int lowBatteryColorRes) { // Since there doesn't seem to be a way to use format strings to add the @@ -1833,10 +1899,7 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> // Retrieve hearing aids (ASHA, HAP) individual side battery level if (leftBattery == BluetoothDevice.BATTERY_LEVEL_UNKNOWN) { - leftBattery = getConnectedHearingAidSide(HearingAidInfo.DeviceSide.SIDE_LEFT) - .map(CachedBluetoothDevice::getBatteryLevel) - .filter(batteryLevel -> batteryLevel > BluetoothDevice.BATTERY_LEVEL_UNKNOWN) - .orElse(BluetoothDevice.BATTERY_LEVEL_UNKNOWN); + leftBattery = getHearingAidSideBattery(HearingAidInfo.DeviceSide.SIDE_LEFT); } return leftBattery; @@ -1852,10 +1915,7 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> // Retrieve hearing aids (ASHA, HAP) individual side battery level if (rightBattery == BluetoothDevice.BATTERY_LEVEL_UNKNOWN) { - rightBattery = getConnectedHearingAidSide(HearingAidInfo.DeviceSide.SIDE_RIGHT) - .map(CachedBluetoothDevice::getBatteryLevel) - .filter(batteryLevel -> batteryLevel > BluetoothDevice.BATTERY_LEVEL_UNKNOWN) - .orElse(BluetoothDevice.BATTERY_LEVEL_UNKNOWN); + rightBattery = getHearingAidSideBattery(HearingAidInfo.DeviceSide.SIDE_RIGHT); } return rightBattery; @@ -2263,6 +2323,16 @@ public class CachedBluetoothDevice implements Comparable<CachedBluetoothDevice> mBluetoothManager = bluetoothManager; } + @VisibleForTesting + void setIsDeviceStylus(Boolean isDeviceStylus) { + mIsDeviceStylus = isDeviceStylus; + } + + @VisibleForTesting + void setInputDevice(@Nullable InputDevice inputDevice) { + mInputDevice = inputDevice; + } + private boolean isAndroidAuto() { try { ParcelUuid[] uuids = mDevice.getUuids(); diff --git a/packages/SettingsLib/src/com/android/settingslib/notification/data/repository/ZenModeRepository.kt b/packages/SettingsLib/src/com/android/settingslib/notification/data/repository/ZenModeRepository.kt index c4e724554c04..21d518a644a9 100644 --- a/packages/SettingsLib/src/com/android/settingslib/notification/data/repository/ZenModeRepository.kt +++ b/packages/SettingsLib/src/com/android/settingslib/notification/data/repository/ZenModeRepository.kt @@ -27,7 +27,6 @@ import android.content.IntentFilter import android.database.ContentObserver import android.os.Handler import android.provider.Settings -import com.android.settingslib.flags.Flags import com.android.settingslib.notification.modes.ZenMode import com.android.settingslib.notification.modes.ZenModesBackend import java.time.Duration @@ -35,6 +34,7 @@ import kotlin.coroutines.CoroutineContext import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.callbackFlow @@ -72,7 +72,7 @@ class ZenModeRepositoryImpl( private val notificationManager: NotificationManager, private val backend: ZenModesBackend, private val contentResolver: ContentResolver, - val scope: CoroutineScope, + val applicationScope: CoroutineScope, val backgroundCoroutineContext: CoroutineContext, // This is nullable just to simplify testing, since SettingsLib doesn't have a good way // to create a fake handler. @@ -104,7 +104,7 @@ class ZenModeRepositoryImpl( awaitClose { context.unregisterReceiver(receiver) } } .flowOn(backgroundCoroutineContext) - .shareIn(started = SharingStarted.WhileSubscribed(), scope = scope) + .shareIn(started = SharingStarted.WhileSubscribed(), scope = applicationScope) } override val consolidatedNotificationPolicy: StateFlow<NotificationManager.Policy?> by lazy { @@ -129,14 +129,11 @@ class ZenModeRepositoryImpl( .map { mapper(it) } .onStart { emit(mapper(null)) } .flowOn(backgroundCoroutineContext) - .stateIn(scope, SharingStarted.WhileSubscribed(), null) + .stateIn(applicationScope, SharingStarted.WhileSubscribed(), null) private val zenConfigChanged by lazy { if (android.app.Flags.modesUi()) { callbackFlow { - // emit an initial value - trySend(Unit) - val observer = object : ContentObserver(backgroundHandler) { override fun onChange(selfChange: Boolean) { @@ -163,16 +160,18 @@ class ZenModeRepositoryImpl( } } - override val modes: Flow<List<ZenMode>> by lazy { - if (android.app.Flags.modesUi()) { + override val modes: StateFlow<List<ZenMode>> = + if (android.app.Flags.modesUi()) zenConfigChanged .map { backend.modes } .distinctUntilChanged() .flowOn(backgroundCoroutineContext) - } else { - flowOf(emptyList()) - } - } + .stateIn( + scope = applicationScope, + started = SharingStarted.Eagerly, + initialValue = backend.modes, + ) + else MutableStateFlow<List<ZenMode>>(emptyList()) /** * Gets the current list of [ZenMode] instances according to the backend. diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java index b7814127b716..8fc4aa81b53f 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/BluetoothUtilsTest.java @@ -936,15 +936,60 @@ public class BluetoothUtilsTest { } @Test + @EnableFlags(Flags.FLAG_ADOPT_PRIMARY_GROUP_MANAGEMENT_API_V2) + public void getSecondaryDeviceForBroadcast_adoptAPI_noSecondary_returnNull() { + when(mBroadcast.isEnabled(any())).thenReturn(true); + when(mLeAudioProfile.getBroadcastToUnicastFallbackGroup()).thenReturn(1); + when(mDeviceManager.findDevice(mBluetoothDevice)).thenReturn(mCachedBluetoothDevice); + when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice); + when(mCachedBluetoothDevice.getGroupId()).thenReturn(1); + BluetoothLeBroadcastReceiveState state = mock(BluetoothLeBroadcastReceiveState.class); + when(mAssistant.getAllSources(mBluetoothDevice)).thenReturn(ImmutableList.of(state)); + when(mAssistant.getAllConnectedDevices()).thenReturn(ImmutableList.of(mBluetoothDevice)); + + assertThat( + BluetoothUtils.getSecondaryDeviceForBroadcast( + mContext.getContentResolver(), mLocalBluetoothManager)) + .isNull(); + } + + @Test + @EnableFlags(Flags.FLAG_ADOPT_PRIMARY_GROUP_MANAGEMENT_API_V2) + public void getSecondaryDeviceForBroadcast_adoptAPI_returnCorrectDevice() { + when(mBroadcast.isEnabled(any())).thenReturn(true); + when(mLeAudioProfile.getBroadcastToUnicastFallbackGroup()).thenReturn(1); + CachedBluetoothDevice cachedBluetoothDevice = mock(CachedBluetoothDevice.class); + BluetoothDevice bluetoothDevice = mock(BluetoothDevice.class); + when(cachedBluetoothDevice.getDevice()).thenReturn(bluetoothDevice); + when(cachedBluetoothDevice.getGroupId()).thenReturn(1); + when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice); + when(mCachedBluetoothDevice.getGroupId()).thenReturn(2); + when(mDeviceManager.findDevice(bluetoothDevice)).thenReturn(cachedBluetoothDevice); + when(mDeviceManager.findDevice(mBluetoothDevice)).thenReturn(mCachedBluetoothDevice); + BluetoothLeBroadcastReceiveState state = mock(BluetoothLeBroadcastReceiveState.class); + List<Long> bisSyncState = new ArrayList<>(); + bisSyncState.add(1L); + when(state.getBisSyncState()).thenReturn(bisSyncState); + when(mAssistant.getAllSources(any(BluetoothDevice.class))) + .thenReturn(ImmutableList.of(state)); + when(mAssistant.getAllConnectedDevices()) + .thenReturn(ImmutableList.of(mBluetoothDevice, bluetoothDevice)); + + assertThat( + BluetoothUtils.getSecondaryDeviceForBroadcast( + mContext.getContentResolver(), mLocalBluetoothManager)) + .isEqualTo(mCachedBluetoothDevice); + } + + @Test + @DisableFlags(Flags.FLAG_ADOPT_PRIMARY_GROUP_MANAGEMENT_API_V2) public void getSecondaryDeviceForBroadcast_noSecondary_returnNull() { Settings.Secure.putInt( mContext.getContentResolver(), BluetoothUtils.getPrimaryGroupIdUriForBroadcast(), 1); when(mBroadcast.isEnabled(any())).thenReturn(true); - CachedBluetoothDeviceManager deviceManager = mock(CachedBluetoothDeviceManager.class); - when(mLocalBluetoothManager.getCachedDeviceManager()).thenReturn(deviceManager); - when(deviceManager.findDevice(mBluetoothDevice)).thenReturn(mCachedBluetoothDevice); + when(mDeviceManager.findDevice(mBluetoothDevice)).thenReturn(mCachedBluetoothDevice); when(mCachedBluetoothDevice.getDevice()).thenReturn(mBluetoothDevice); when(mCachedBluetoothDevice.getGroupId()).thenReturn(1); BluetoothLeBroadcastReceiveState state = mock(BluetoothLeBroadcastReceiveState.class); @@ -958,6 +1003,7 @@ public class BluetoothUtilsTest { } @Test + @DisableFlags(Flags.FLAG_ADOPT_PRIMARY_GROUP_MANAGEMENT_API_V2) public void getSecondaryDeviceForBroadcast_returnCorrectDevice() { Settings.Secure.putInt( mContext.getContentResolver(), diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java index f57ee0c0930e..b4384b74ccbe 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java @@ -16,7 +16,6 @@ package com.android.settingslib.bluetooth; import static com.android.settingslib.flags.Flags.FLAG_ENABLE_LE_AUDIO_SHARING; -import static com.android.settingslib.flags.Flags.FLAG_ENABLE_SET_PREFERRED_TRANSPORT_FOR_LE_AUDIO_DEVICE; import static com.android.settingslib.flags.Flags.FLAG_ENABLE_TEMPORARY_BOND_DEVICES_UI; import static com.google.common.truth.Truth.assertThat; @@ -42,6 +41,8 @@ import android.content.Context; import android.graphics.drawable.BitmapDrawable; import android.hardware.input.InputManager; import android.media.AudioManager; +import android.platform.test.annotations.DisableFlags; +import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.SetFlagsRule; import android.provider.Settings; import android.text.Spannable; @@ -138,7 +139,6 @@ public class CachedBluetoothDeviceTest { public void setUp() { MockitoAnnotations.initMocks(this); mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_TV_MEDIA_OUTPUT_DIALOG); - mSetFlagsRule.enableFlags(FLAG_ENABLE_SET_PREFERRED_TRANSPORT_FOR_LE_AUDIO_DEVICE); mSetFlagsRule.enableFlags(FLAG_ENABLE_LE_AUDIO_SHARING); mSetFlagsRule.enableFlags(FLAG_ENABLE_TEMPORARY_BOND_DEVICES_UI); mContext = RuntimeEnvironment.application; @@ -163,6 +163,7 @@ public class CachedBluetoothDeviceTest { when(mHidProfile.getProfileId()).thenReturn(BluetoothProfile.HID_HOST); when(mLocalBluetoothManager.getProfileManager()).thenReturn(mProfileManager); when(mBroadcast.isEnabled(any())).thenReturn(false); + when(mProfileManager.getLeAudioProfile()).thenReturn(mLeAudioProfile); when(mProfileManager.getLeAudioBroadcastProfile()).thenReturn(mBroadcast); when(mProfileManager.getLeAudioBroadcastAssistantProfile()).thenReturn(mAssistant); mCachedDevice = spy(new CachedBluetoothDevice(mContext, mProfileManager, mDevice)); @@ -2004,6 +2005,70 @@ public class CachedBluetoothDeviceTest { } @Test + @EnableFlags(com.android.settingslib.flags.Flags.FLAG_ADOPT_PRIMARY_GROUP_MANAGEMENT_API_V2) + public void getConnectionSummary_adoptAPI_isBroadcastPrimary_fallbackDevice_returnActive() { + when(mBroadcast.isEnabled(any())).thenReturn(true); + when(mCachedDevice.getDevice()).thenReturn(mDevice); + when(mLeAudioProfile.getBroadcastToUnicastFallbackGroup()).thenReturn(1); + + List<Long> bisSyncState = new ArrayList<>(); + bisSyncState.add(1L); + when(mLeBroadcastReceiveState.getBisSyncState()).thenReturn(bisSyncState); + List<BluetoothLeBroadcastReceiveState> sourceList = new ArrayList<>(); + sourceList.add(mLeBroadcastReceiveState); + when(mAssistant.getAllSources(any())).thenReturn(sourceList); + + when(mCachedDevice.getGroupId()).thenReturn(1); + + assertThat(mCachedDevice.getConnectionSummary(false)) + .isEqualTo(mContext.getString(R.string.bluetooth_active_no_battery_level)); + } + + @Test + @EnableFlags(com.android.settingslib.flags.Flags.FLAG_ADOPT_PRIMARY_GROUP_MANAGEMENT_API_V2) + public void getConnectionSummary_adoptAPI_isBroadcastPrimary_activeDevice_returnActive() { + when(mBroadcast.isEnabled(any())).thenReturn(true); + when(mCachedDevice.getDevice()).thenReturn(mDevice); + when(mLeAudioProfile.getBroadcastToUnicastFallbackGroup()).thenReturn( + BluetoothCsipSetCoordinator.GROUP_ID_INVALID); + + List<Long> bisSyncState = new ArrayList<>(); + bisSyncState.add(1L); + when(mLeBroadcastReceiveState.getBisSyncState()).thenReturn(bisSyncState); + List<BluetoothLeBroadcastReceiveState> sourceList = new ArrayList<>(); + sourceList.add(mLeBroadcastReceiveState); + when(mAssistant.getAllSources(any())).thenReturn(sourceList); + + when(mCachedDevice.getGroupId()).thenReturn(1); + when(mCachedDevice.isActiveDevice(BluetoothProfile.LE_AUDIO)).thenReturn(true); + + assertThat(mCachedDevice.getConnectionSummary(false)) + .isEqualTo(mContext.getString(R.string.bluetooth_active_no_battery_level)); + } + + @Test + @EnableFlags(com.android.settingslib.flags.Flags.FLAG_ADOPT_PRIMARY_GROUP_MANAGEMENT_API_V2) + public void getConnectionSummary_adoptAPI_isBroadcastNotPrimary_returnActiveMedia() { + when(mBroadcast.isEnabled(any())).thenReturn(true); + when(mCachedDevice.getDevice()).thenReturn(mDevice); + when(mLeAudioProfile.getBroadcastToUnicastFallbackGroup()).thenReturn(1); + + List<Long> bisSyncState = new ArrayList<>(); + bisSyncState.add(1L); + when(mLeBroadcastReceiveState.getBisSyncState()).thenReturn(bisSyncState); + List<BluetoothLeBroadcastReceiveState> sourceList = new ArrayList<>(); + sourceList.add(mLeBroadcastReceiveState); + when(mAssistant.getAllSources(any())).thenReturn(sourceList); + + when(mCachedDevice.getGroupId()).thenReturn(BluetoothCsipSetCoordinator.GROUP_ID_INVALID); + + assertThat(mCachedDevice.getConnectionSummary(false)) + .isEqualTo( + mContext.getString(R.string.bluetooth_active_media_only_no_battery_level)); + } + + @Test + @DisableFlags(com.android.settingslib.flags.Flags.FLAG_ADOPT_PRIMARY_GROUP_MANAGEMENT_API_V2) public void getConnectionSummary_isBroadcastPrimary_fallbackDevice_returnActive() { when(mBroadcast.isEnabled(any())).thenReturn(true); when(mCachedDevice.getDevice()).thenReturn(mDevice); @@ -2026,6 +2091,7 @@ public class CachedBluetoothDeviceTest { } @Test + @DisableFlags(com.android.settingslib.flags.Flags.FLAG_ADOPT_PRIMARY_GROUP_MANAGEMENT_API_V2) public void getConnectionSummary_isBroadcastPrimary_activeDevice_returnActive() { when(mBroadcast.isEnabled(any())).thenReturn(true); when(mCachedDevice.getDevice()).thenReturn(mDevice); @@ -2049,6 +2115,7 @@ public class CachedBluetoothDeviceTest { } @Test + @DisableFlags(com.android.settingslib.flags.Flags.FLAG_ADOPT_PRIMARY_GROUP_MANAGEMENT_API_V2) public void getConnectionSummary_isBroadcastNotPrimary_returnActiveMedia() { when(mBroadcast.isEnabled(any())).thenReturn(true); when(mCachedDevice.getDevice()).thenReturn(mDevice); @@ -2231,11 +2298,7 @@ public class CachedBluetoothDeviceTest { "false".getBytes()); when(mDevice.getMetadata(BluetoothDevice.METADATA_MAIN_BATTERY)).thenReturn( MAIN_BATTERY.getBytes()); - when(mContext.getSystemService(InputManager.class)).thenReturn(mInputManager); - when(mInputManager.getInputDeviceIds()).thenReturn(new int[]{TEST_DEVICE_ID}); - when(mInputManager.getInputDeviceBluetoothAddress(TEST_DEVICE_ID)).thenReturn( - DEVICE_ADDRESS); - when(mInputManager.getInputDevice(TEST_DEVICE_ID)).thenReturn(mInputDevice); + mCachedDevice.setInputDevice(mInputDevice); BatteryLevelsInfo batteryLevelsInfo = mCachedDevice.getBatteryLevelsInfo(); @@ -2253,10 +2316,9 @@ public class CachedBluetoothDeviceTest { public void getBatteryLevelsInfo_stylusDeviceWithBattery_returnBatteryLevelsInfo() { when(mDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)).thenReturn( "false".getBytes()); - when(mDevice.getMetadata(BluetoothDevice.METADATA_DEVICE_TYPE)).thenReturn( - BluetoothDevice.DEVICE_TYPE_STYLUS.getBytes()); when(mDevice.getMetadata(BluetoothDevice.METADATA_MAIN_BATTERY)).thenReturn( MAIN_BATTERY.getBytes()); + mCachedDevice.setIsDeviceStylus(true); BatteryLevelsInfo batteryLevelsInfo = mCachedDevice.getBatteryLevelsInfo(); @@ -2270,6 +2332,31 @@ public class CachedBluetoothDeviceTest { Integer.parseInt(MAIN_BATTERY)); } + @Test + public void getBatteryLevelsInfo_hearingAidDeviceWithBattery_returnBatteryLevelsInfo() { + when(mDevice.getMetadata(BluetoothDevice.METADATA_IS_UNTETHERED_HEADSET)).thenReturn( + "false".getBytes()); + when(mProfileManager.getHearingAidProfile()).thenReturn(mHearingAidProfile); + updateProfileStatus(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED); + mSubCachedDevice.setHearingAidInfo(getLeftAshaHearingAidInfo()); + when(mSubCachedDevice.getBatteryLevel()).thenReturn(Integer.parseInt(TWS_BATTERY_LEFT)); + updateSubDeviceProfileStatus(mHearingAidProfile, BluetoothProfile.STATE_CONNECTED); + mCachedDevice.setSubDevice(mSubCachedDevice); + mCachedDevice.setHearingAidInfo(getRightAshaHearingAidInfo()); + when(mCachedDevice.getBatteryLevel()).thenReturn(Integer.parseInt(TWS_BATTERY_RIGHT)); + + BatteryLevelsInfo batteryLevelsInfo = mCachedDevice.getBatteryLevelsInfo(); + + assertThat(batteryLevelsInfo.getLeftBatteryLevel()).isEqualTo( + Integer.parseInt(TWS_BATTERY_LEFT)); + assertThat(batteryLevelsInfo.getRightBatteryLevel()).isEqualTo( + Integer.parseInt(TWS_BATTERY_RIGHT)); + assertThat(batteryLevelsInfo.getCaseBatteryLevel()).isEqualTo( + BluetoothDevice.BATTERY_LEVEL_UNKNOWN); + assertThat(batteryLevelsInfo.getOverallBatteryLevel()).isEqualTo( + Integer.parseInt(TWS_BATTERY_LEFT)); + } + private void updateProfileStatus(LocalBluetoothProfile profile, int status) { doReturn(status).when(profile).getConnectionStatus(mDevice); mCachedDevice.onProfileStateChanged(profile, status); diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/data/repository/ZenModeRepositoryTest.kt b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/data/repository/ZenModeRepositoryTest.kt index b364368df473..ec7baf6bf081 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/data/repository/ZenModeRepositoryTest.kt +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/notification/data/repository/ZenModeRepositoryTest.kt @@ -75,10 +75,14 @@ class ZenModeRepositoryTest { private val testScope: TestScope = TestScope() + private val initialModes = listOf(TestModeBuilder().setId("Built-in").build()) + @Before fun setup() { MockitoAnnotations.initMocks(this) + `when`(zenModesBackend.modes).thenReturn(initialModes) + underTest = ZenModeRepositoryImpl( context, @@ -151,8 +155,8 @@ class ZenModeRepositoryTest { fun modesListEmitsOnSettingsChange() { testScope.runTest { val values = mutableListOf<List<ZenMode>>() - val modes1 = listOf(TestModeBuilder().setId("One").build()) - `when`(zenModesBackend.modes).thenReturn(modes1) + + // an initial list of modes is read when the stateflow is created underTest.modes.onEach { values.add(it) }.launchIn(backgroundScope) runCurrent() @@ -172,7 +176,7 @@ class ZenModeRepositoryTest { triggerZenModeSettingUpdate() runCurrent() - assertThat(values).containsExactly(modes1, modes2, modes3).inOrder() + assertThat(values).containsExactly(initialModes, modes2, modes3).inOrder() } } diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java index ed11e12c32ff..2273b4f81eea 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsHelper.java @@ -54,6 +54,7 @@ import com.android.settingslib.devicestate.DeviceStateRotationLockSettingsManage import java.io.FileNotFoundException; import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Set; @@ -650,6 +651,10 @@ public class SettingsHelper { * e.g. current locale "en-US,zh-CN" and backup locale "ja-JP,zh-Hans-CN,en-US" are merged to * "en-US,zh-CN,ja-JP". * + * - Same language codes and scripts are dropped. + * e.g. current locale "en-US, zh-Hans-TW" and backup locale "en-UK, en-GB, zh-Hans-HK" are + * merged to "en-US, zh-Hans-TW". + * * - Unsupported locales are dropped. * e.g. current locale "en-US" and backup locale "ja-JP,zh-CN" but the supported locales * are "en-US,zh-CN", the merged locale list is "en-US,zh-CN". @@ -683,13 +688,23 @@ public class SettingsHelper { filtered.add(locale); } + final HashSet<String> existingLanguageAndScript = new HashSet<>(); for (int i = 0; i < restore.size(); i++) { final Locale restoredLocaleWithExtension = copyExtensionToTargetLocale(restoredLocale, getFilteredLocale(restore.get(i), allLocales)); + if (restoredLocaleWithExtension != null) { - filtered.add(restoredLocaleWithExtension); + String language = restoredLocaleWithExtension.getLanguage(); + String script = restoredLocaleWithExtension.getScript(); + + String restoredLanguageAndScript = + script == null ? language : language + "-" + script; + if (existingLanguageAndScript.add(restoredLanguageAndScript)) { + filtered.add(restoredLocaleWithExtension); + } } } + if (filtered.size() == current.size()) { return current; // Nothing added to current locale list. } diff --git a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperTest.java b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperTest.java index 40654b0e2f37..48c778542d66 100644 --- a/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperTest.java +++ b/packages/SettingsProvider/test/src/com/android/providers/settings/SettingsHelperTest.java @@ -388,7 +388,11 @@ public class SettingsHelperTest { LocaleList.forLanguageTags("zh-Hant-TW"), // current new String[] { "fa-Arab-AF-u-nu-latn", "zh-Hant-TW" })); // supported - + assertEquals(LocaleList.forLanguageTags("en-US,zh-Hans-TW"), + SettingsHelper.resolveLocales( + LocaleList.forLanguageTags("en-UK,en-GB,zh-Hans-HK"), // restore + LocaleList.forLanguageTags("en-US,zh-Hans-TW"), // current + new String[] { "en-US,zh-Hans-TW,en-UK,en-GB,zh-Hans-HK" })); // supported } @Test diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp index 129949fd38b2..7f4544379cd3 100644 --- a/packages/SystemUI/Android.bp +++ b/packages/SystemUI/Android.bp @@ -429,6 +429,7 @@ android_library { manifest: "AndroidManifest-res.xml", flags_packages: [ "android.app.flags-aconfig", + "com_android_systemui_flags", ], } diff --git a/packages/SystemUI/aconfig/desktop_users_and_accounts.aconfig b/packages/SystemUI/aconfig/desktop_users_and_accounts.aconfig new file mode 100644 index 000000000000..c7e9c9fbee2e --- /dev/null +++ b/packages/SystemUI/aconfig/desktop_users_and_accounts.aconfig @@ -0,0 +1,9 @@ +package: "com.android.systemui" +container: "system" + +flag { + name: "user_switcher_add_sign_out_option" + namespace: "desktop_users_and_accounts" + description: "Add a sign out option to the user switcher menu if sign out is possible" + bug: "381478261" +}
\ No newline at end of file diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig index 436e92bc0efa..685c689b6c28 100644 --- a/packages/SystemUI/aconfig/systemui.aconfig +++ b/packages/SystemUI/aconfig/systemui.aconfig @@ -289,6 +289,15 @@ flag { } } +flag { + name: "notification_skip_silent_updates" + namespace: "systemui" + description: "Do not notify HeadsUpManager for silent updates." + bug: "401068530" + metadata { + purpose: PURPOSE_BUGFIX + } +} flag { name: "scene_container" @@ -525,6 +534,14 @@ flag { } flag { + name: "status_bar_font_updates" + namespace: "systemui" + description: "Read only flag for using a new font in the status bar" + bug: "393609116" + is_fixed_read_only: true +} + +flag { name: "promote_notifications_automatically" namespace: "systemui" description: "Flag to automatically turn certain notifications into promoted notifications so " @@ -1436,16 +1453,6 @@ flag { } flag { - name: "dozeui_scheduling_alarms_background_execution" - namespace: "systemui" - description: "Decide whether to execute binder calls to schedule alarms in background thread" - bug: "330492575" - metadata { - purpose: PURPOSE_BUGFIX - } -} - -flag { name: "media_lockscreen_launch_animation" namespace : "systemui" description : "Enable the origin launch animation for UMO when opening on top of lockscreen." @@ -1858,6 +1865,14 @@ flag { } flag { + name: "shade_header_font_update" + namespace: "systemui" + description: "Updates the fonts of the shade header" + bug: "393609960" + is_fixed_read_only: true +} + +flag { name: "keyboard_shortcut_helper_shortcut_customizer" namespace: "systemui" description: "An implementation of shortcut customizations through shortcut helper." @@ -1902,13 +1917,6 @@ flag { } flag { - name: "spatial_model_launcher_pushback" - namespace: "systemui" - description: "Implement the depth push scaling effect on Launcher when users pull down shade." - bug: "370562309" -} - -flag { name: "spatial_model_app_pushback" namespace: "systemui" description: "Implement the depth push scaling effect on the current app when users pull down shade." @@ -2014,7 +2022,14 @@ flag { flag { name: "permission_helper_ui_rich_ongoing" namespace: "systemui" - description: "[RONs] Guards inline permission helper for demoting RONs" + description: "[RONs] Guards inline permission helper for demoting RONs [Guts/card version]" + bug: "379186372" +} + +flag { + name: "permission_helper_inline_ui_rich_ongoing" + namespace: "systemui" + description: "[RONs] Guards inline permission helper for demoting RONs [Inline version]" bug: "379186372" } @@ -2134,9 +2149,18 @@ flag { name: "media_projection_grey_error_text" namespace: "systemui" description: "Set the error text color to grey when app sharing is hidden by the requesting app" - bug: "390624334" + bug: "400877402" metadata { purpose: PURPOSE_BUGFIX } } +flag { + name: "keyguard_wm_reorder_atms_calls" + namespace: "systemui" + description: "Calls ATMS#setLockScreenShown before default display callbacks in case they're slow" + bug: "399693427" + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt index 7ee6a6e5ebf4..1fb7901dcb59 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityTransitionAnimator.kt @@ -751,7 +751,8 @@ constructor( OriginTransition(createLongLivedRunner(controllerFactory, scope, forLaunch = true)), "${cookie}_launchTransition", ) - transitionRegister.register(launchFilter, launchRemoteTransition, includeTakeover = true) + // TODO(b/403529740): re-enable takeovers once we solve the Compose jank issues. + transitionRegister.register(launchFilter, launchRemoteTransition, includeTakeover = false) // Cross-task close transitions should not use this animation, so we only register it for // when the opening window is Launcher. @@ -777,7 +778,8 @@ constructor( ), "${cookie}_returnTransition", ) - transitionRegister.register(returnFilter, returnRemoteTransition, includeTakeover = true) + // TODO(b/403529740): re-enable takeovers once we solve the Compose jank issues. + transitionRegister.register(returnFilter, returnRemoteTransition, includeTakeover = false) longLivedTransitions[cookie] = Pair(launchRemoteTransition, returnRemoteTransition) } @@ -1216,6 +1218,13 @@ constructor( private var animation: TransitionAnimator.Animation? = null /** + * Whether the opening/closing window needs to reparented to the view's window at the + * beginning of the animation. Since we don't always do this, we need to keep track of it in + * order to have the rest of the animation behave correctly. + */ + var reparent = false + + /** * A timeout to cancel the transition animation if the remote animation is not started or * cancelled within [TRANSITION_TIMEOUT] milliseconds after the intent was started. * @@ -1469,6 +1478,17 @@ constructor( transitionAnimator.isExpandingFullyAbove(controller.transitionContainer, endState) val windowState = startingWindowState ?: controller.windowAnimatorState + // We only reparent launch animations. In current integrations, returns are + // not affected by the issue solved by reparenting, and they present + // additional problems when the view lives in the Status Bar. + // TODO(b/397646693): remove this exception. + val isEligibleForReparenting = controller.isLaunching + val viewRoot = controller.transitionContainer.viewRootImpl + val skipReparenting = skipReparentTransaction || viewRoot == null + if (moveTransitionAnimationLayer() && isEligibleForReparenting && !skipReparenting) { + reparent = true + } + // We animate the opening window and delegate the view expansion to [this.controller]. val delegate = this.controller val controller = @@ -1536,16 +1556,13 @@ constructor( ) } - if (moveTransitionAnimationLayer() && !skipReparentTransaction) { + if (reparent) { // Ensure that the launching window is rendered above the view's window, // so it is not obstructed. // TODO(b/397180418): re-use the start transaction once the // RemoteAnimation wrapper is cleaned up. SurfaceControl.Transaction().use { - it.reparent( - window.leash, - controller.transitionContainer.viewRootImpl.surfaceControl, - ) + it.reparent(window.leash, viewRoot.surfaceControl) it.apply() } } @@ -1603,7 +1620,7 @@ constructor( null } val fadeWindowBackgroundLayer = - if (moveTransitionAnimationLayer()) { + if (reparent) { false } else { !controller.isBelowAnimatingWindow @@ -1727,7 +1744,7 @@ constructor( // fade in progressively. Otherwise, it should be fully opaque and will be progressively // revealed as the window background color layer above the window fades out. val alpha = - if (moveTransitionAnimationLayer() || controller.isBelowAnimatingWindow) { + if (reparent || controller.isBelowAnimatingWindow) { if (controller.isLaunching) { interpolators.contentAfterFadeInInterpolator.getInterpolation( windowProgress diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteAnimationRunnerCompat.java b/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteAnimationRunnerCompat.java index 21ec89646f0f..060f0c94732d 100644 --- a/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteAnimationRunnerCompat.java +++ b/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteAnimationRunnerCompat.java @@ -197,6 +197,10 @@ public abstract class RemoteAnimationRunnerCompat extends IRemoteAnimationRunner // Release surface references now. This is apparently to free GPU memory // before GC would. info.releaseAllSurfaces(); + // Make sure that the transition leashes created are not leaked. + for (SurfaceControl leash : leashMap.values()) { + finishTransaction.reparent(leash, null); + } // Don't release here since launcher might still be using them. Instead // let launcher release them (eg. via RemoteAnimationTargets) leashMap.clear(); diff --git a/packages/SystemUI/compose/core/src/com/android/compose/PlatformButtons.kt b/packages/SystemUI/compose/core/src/com/android/compose/PlatformButtons.kt index df50eb8fa3e8..da07fbd9b6a6 100644 --- a/packages/SystemUI/compose/core/src/com/android/compose/PlatformButtons.kt +++ b/packages/SystemUI/compose/core/src/com/android/compose/PlatformButtons.kt @@ -101,10 +101,17 @@ fun PlatformIconButton( modifier: Modifier = Modifier, enabled: Boolean = true, colors: IconButtonColors = iconButtonColors(), + shape: Shape = IconButtonDefaults.standardShape, @DrawableRes iconResource: Int, contentDescription: String?, ) { - IconButton(modifier = modifier, onClick = onClick, enabled = enabled, colors = colors) { + IconButton( + modifier = modifier, + onClick = onClick, + enabled = enabled, + colors = colors, + shape = shape, + ) { Icon( painter = painterResource(id = iconResource), contentDescription = contentDescription, diff --git a/packages/SystemUI/compose/core/src/com/android/compose/gesture/NestedScrollController.kt b/packages/SystemUI/compose/core/src/com/android/compose/gesture/NestedScrollController.kt index 2530a4f240e3..54232e76a568 100644 --- a/packages/SystemUI/compose/core/src/com/android/compose/gesture/NestedScrollController.kt +++ b/packages/SystemUI/compose/core/src/com/android/compose/gesture/NestedScrollController.kt @@ -134,6 +134,7 @@ private class NestedScrollControllerNode( private var bounds: NestedScrollableBound, ) : DelegatingNode(), NestedScrollConnection { private var childrenConsumedAnyScroll = false + private var availableOnPreScroll = Offset.Zero init { delegate(nestedScrollModifierNode(this, dispatcher = null)) @@ -153,12 +154,21 @@ private class NestedScrollControllerNode( this.bounds = bounds } + override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset { + availableOnPreScroll = available + return Offset.Zero + } + override fun onPostScroll( consumed: Offset, available: Offset, source: NestedScrollSource, ): Offset { - if (hasConsumedScrollInBounds(consumed.x) || hasConsumedScrollInBounds(consumed.y)) { + val consumedIncludingPreScroll = availableOnPreScroll - available + if ( + hasConsumedScrollInBounds(consumedIncludingPreScroll.x) || + hasConsumedScrollInBounds(consumedIncludingPreScroll.y) + ) { childrenConsumedAnyScroll = true } diff --git a/packages/SystemUI/compose/core/src/com/android/compose/gesture/effect/OffsetOverscrollEffect.kt b/packages/SystemUI/compose/core/src/com/android/compose/gesture/effect/OffsetOverscrollEffect.kt index 1bb8ae5019fb..07a571b94ce4 100644 --- a/packages/SystemUI/compose/core/src/com/android/compose/gesture/effect/OffsetOverscrollEffect.kt +++ b/packages/SystemUI/compose/core/src/com/android/compose/gesture/effect/OffsetOverscrollEffect.kt @@ -16,7 +16,6 @@ package com.android.compose.gesture.effect -import androidx.annotation.VisibleForTesting import androidx.compose.animation.core.AnimationSpec import androidx.compose.foundation.OverscrollEffect import androidx.compose.foundation.OverscrollFactory @@ -94,7 +93,6 @@ class OffsetOverscrollEffect(animationScope: CoroutineScope, animationSpec: Anim companion object { private val MaxDistance = 400.dp - @VisibleForTesting fun computeOffset(density: Density, overscrollDistance: Float): Int { val maxDistancePx = with(density) { MaxDistance.toPx() } val progress = ProgressConverter.Default.convert(overscrollDistance / maxDistancePx) diff --git a/packages/SystemUI/compose/core/src/com/android/compose/theme/PlatformTheme.kt b/packages/SystemUI/compose/core/src/com/android/compose/theme/PlatformTheme.kt index 84370ed4d2c7..6fb3679dfb7c 100644 --- a/packages/SystemUI/compose/core/src/com/android/compose/theme/PlatformTheme.kt +++ b/packages/SystemUI/compose/core/src/com/android/compose/theme/PlatformTheme.kt @@ -43,7 +43,7 @@ fun PlatformTheme(isDarkTheme: Boolean = isSystemInDarkTheme(), content: @Compos val context = LocalContext.current val colorScheme = remember(context, isDarkTheme) { platformColorScheme(isDarkTheme, context) } - val androidColorScheme = remember(context) { AndroidColorScheme(context) } + val androidColorScheme = remember(context, isDarkTheme) { AndroidColorScheme(context) } val typefaceNames = remember(context) { TypefaceNames.get(context) } val typefaceTokens = remember(typefaceNames) { TypefaceTokens(typefaceNames) } val typography = diff --git a/packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/NestedScrollControllerTest.kt b/packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/NestedScrollControllerTest.kt index 424af3395913..377cbe238f49 100644 --- a/packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/NestedScrollControllerTest.kt +++ b/packages/SystemUI/compose/core/tests/src/com/android/compose/gesture/NestedScrollControllerTest.kt @@ -23,9 +23,13 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.input.nestedscroll.NestedScrollConnection +import androidx.compose.ui.input.nestedscroll.NestedScrollSource +import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.test.junit4.createComposeRule import androidx.compose.ui.test.onRoot import androidx.compose.ui.test.performTouchInput @@ -103,4 +107,35 @@ class NestedScrollControllerTest { rule.waitForIdle() assertThat(state.isOuterScrollAllowed).isTrue() } + + @Test + fun supportsPreScrolls() { + val state = NestedScrollControlState() + rule.setContent { + Box( + Modifier.fillMaxSize() + .nestedScrollController(state) + .nestedScroll( + remember { + object : NestedScrollConnection { + override fun onPreScroll( + available: Offset, + source: NestedScrollSource, + ): Offset = available + } + } + ) + .scrollable(rememberScrollableState { 0f }, Orientation.Vertical) + ) + } + + rule.onRoot().performTouchInput { + down(topLeft) + moveBy(Offset(0f, bottom)) + } + assertThat(state.isOuterScrollAllowed).isFalse() + + rule.onRoot().performTouchInput { up() } + assertThat(state.isOuterScrollAllowed).isTrue() + } } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinInputDisplay.kt b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinInputDisplay.kt index 1f98cd8e07c0..90311ed93987 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinInputDisplay.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/bouncer/ui/composable/PinInputDisplay.kt @@ -35,7 +35,7 @@ import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.heightIn +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.foundation.layout.wrapContentSize @@ -83,10 +83,7 @@ import kotlinx.coroutines.joinAll import kotlinx.coroutines.launch @Composable -fun PinInputDisplay( - viewModel: PinBouncerViewModel, - modifier: Modifier = Modifier, -) { +fun PinInputDisplay(viewModel: PinBouncerViewModel, modifier: Modifier = Modifier) { val hintedPinLength: Int? by viewModel.hintedPinLength.collectAsStateWithLifecycle() val shapeAnimations = rememberShapeAnimations(viewModel.pinShapes) @@ -173,7 +170,10 @@ private fun HintingPinInputDisplay( LaunchedEffect(Unit) { playAnimation = true } val dotColor = MaterialTheme.colorScheme.onSurfaceVariant - Row(modifier = modifier.heightIn(min = shapeAnimations.shapeSize)) { + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = modifier.height(shapeAnimations.shapeSize), + ) { pinEntryDrawable.forEachIndexed { index, drawable -> // Key the loop by [index] and [drawable], so that updating a shape drawable at the same // index will play the new animation (by remembering a new [atEnd]). @@ -316,17 +316,15 @@ private fun SimArea(viewModel: PinBouncerViewModel) { Box(modifier = Modifier.padding(bottom = 20.dp)) { // If isLockedEsim is null, then we do not show anything. if (isLockedEsim == true) { - PlatformOutlinedButton( - onClick = { viewModel.onDisableEsimButtonClicked() }, - ) { + PlatformOutlinedButton(onClick = { viewModel.onDisableEsimButtonClicked() }) { Row( horizontalArrangement = Arrangement.spacedBy(10.dp), - verticalAlignment = Alignment.CenterVertically + verticalAlignment = Alignment.CenterVertically, ) { Image( painter = painterResource(id = R.drawable.ic_no_sim), contentDescription = null, - colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.onSurface) + colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.onSurface), ) Text( text = stringResource(R.string.disable_carrier_button_text), @@ -339,15 +337,13 @@ private fun SimArea(viewModel: PinBouncerViewModel) { Image( painter = painterResource(id = R.drawable.ic_lockscreen_sim), contentDescription = null, - colorFilter = ColorFilter.tint(colorResource(id = R.color.background_protected)) + colorFilter = ColorFilter.tint(colorResource(id = R.color.background_protected)), ) } } } -private class PinInputRow( - val shapeAnimations: ShapeAnimations, -) { +private class PinInputRow(val shapeAnimations: ShapeAnimations) { private val entries = mutableStateListOf<PinInputEntry>() @Composable @@ -359,10 +355,11 @@ private class PinInputRow( contentAlignment = Alignment.Center, ) { Row( - modifier - .heightIn(min = shapeAnimations.shapeSize) - // Pins overflowing horizontally should still be shown as scrolling. - .wrapContentSize(unbounded = true) + verticalAlignment = Alignment.CenterVertically, + modifier = + Modifier.height(shapeAnimations.shapeSize) + // Pins overflowing horizontally should still be shown as scrolling. + .wrapContentSize(unbounded = true), ) { entries.forEach { entry -> key(entry.digit) { entry.Content() } } } @@ -439,10 +436,7 @@ private class PinInputRow( } } -private class PinInputEntry( - val digit: Digit, - val shapeAnimations: ShapeAnimations, -) { +private class PinInputEntry(val digit: Digit, val shapeAnimations: ShapeAnimations) { private val shape = shapeAnimations.getShapeToDot(digit.sequenceNumber) // horizontal space occupied, used to shift contents as individual digits are animated in/out private val entryWidth = @@ -474,7 +468,7 @@ private class PinInputEntry( suspend fun animateRemoval() = coroutineScope { awaitAll( async { entryWidth.animateTo(0.dp, shapeAnimations.inputShiftAnimationSpec) }, - async { shapeSize.animateTo(0.dp, shapeAnimations.deleteShapeSizeAnimationSpec) } + async { shapeSize.animateTo(0.dp, shapeAnimations.deleteShapeSizeAnimationSpec) }, ) } @@ -505,7 +499,7 @@ private class PinInputEntry( layout(animatedEntryWidth.roundToPx(), shapeHeight.roundToPx()) { placeable.place( ((animatedEntryWidth - animatedShapeSize) / 2f).roundToPx(), - ((shapeHeight - animatedShapeSize) / 2f).roundToPx() + ((shapeHeight - animatedShapeSize) / 2f).roundToPx(), ) } }, diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt index 0db2bb51c971..5fac6863e931 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/keyguard/ui/composable/section/LockSection.kt @@ -33,6 +33,7 @@ import com.android.compose.animation.scene.ElementKey import com.android.systemui.biometrics.AuthController import com.android.systemui.customization.R as customR import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.flags.FeatureFlagsClassic import com.android.systemui.flags.Flags import com.android.systemui.keyguard.ui.binder.DeviceEntryIconViewBinder @@ -49,12 +50,14 @@ import com.android.systemui.res.R import com.android.systemui.statusbar.VibratorHelper import dagger.Lazy import javax.inject.Inject +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope class LockSection @Inject constructor( @Application private val applicationScope: CoroutineScope, + @Main private val mainDispatcher: CoroutineDispatcher, private val windowManager: WindowManager, private val authController: AuthController, private val featureFlags: FeatureFlagsClassic, @@ -80,6 +83,7 @@ constructor( id = R.id.device_entry_icon_view DeviceEntryIconViewBinder.bind( applicationScope, + mainDispatcher, this, deviceEntryIconViewModel.get(), deviceEntryForegroundViewModel.get(), diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt index 09b8d178cc8e..3e907e87c13b 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/notifications/ui/composable/Notifications.kt @@ -574,11 +574,11 @@ fun ContentScope.NotificationScrollingStack( ) { Column( modifier = - Modifier.thenIf(supportNestedScrolling) { + Modifier.disableSwipesWhenScrolling(NestedScrollableBound.BottomRight) + .thenIf(supportNestedScrolling) { Modifier.nestedScroll(scrimNestedScrollConnection) } .stackVerticalOverscroll(coroutineScope) { scrollState.canScrollForward } - .disableSwipesWhenScrolling(NestedScrollableBound.BottomRight) .verticalScroll(scrollState) .padding(top = stackTopPadding, bottom = stackBottomPadding) .fillMaxWidth() 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 86c8fc34a63c..addb08fd2319 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 @@ -43,6 +43,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color @@ -67,12 +68,15 @@ import com.android.compose.animation.scene.content.state.TransitionState import com.android.compose.modifiers.thenIf import com.android.compose.theme.colorAttr import com.android.settingslib.Utils +import com.android.systemui.Flags import com.android.systemui.battery.BatteryMeterView import com.android.systemui.battery.BatteryMeterViewController import com.android.systemui.common.ui.compose.windowinsets.CutoutLocation import com.android.systemui.common.ui.compose.windowinsets.LocalDisplayCutout import com.android.systemui.common.ui.compose.windowinsets.LocalScreenCornerRadius import com.android.systemui.compose.modifiers.sysuiResTag +import com.android.systemui.kairos.ExperimentalKairosApi +import com.android.systemui.kairos.buildSpec import com.android.systemui.privacy.OngoingPrivacyChip import com.android.systemui.res.R import com.android.systemui.scene.shared.model.Scenes @@ -86,8 +90,12 @@ import com.android.systemui.shade.ui.viewmodel.ShadeHeaderViewModel.HeaderChipHi import com.android.systemui.statusbar.phone.StatusBarLocation import com.android.systemui.statusbar.phone.StatusIconContainer import com.android.systemui.statusbar.pipeline.mobile.ui.view.ModernShadeCarrierGroupMobileView +import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsViewModelKairosComposeWrapper import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.ShadeCarrierGroupMobileIconViewModel +import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.ShadeCarrierGroupMobileIconViewModelKairos +import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.composeWrapper import com.android.systemui.statusbar.policy.Clock +import com.android.systemui.util.composable.kairos.ActivatedKairosSpec object ShadeHeader { object Elements { @@ -520,8 +528,14 @@ private fun BatteryIcon( ) } +@OptIn(ExperimentalKairosApi::class) @Composable private fun ShadeCarrierGroup(viewModel: ShadeHeaderViewModel, modifier: Modifier = Modifier) { + if (Flags.statusBarMobileIconKairos()) { + ShadeCarrierGroupKairos(viewModel, modifier) + return + } + Row(modifier = modifier, horizontalArrangement = Arrangement.spacedBy(5.dp)) { for (subId in viewModel.mobileSubIds) { AndroidView( @@ -543,6 +557,49 @@ private fun ShadeCarrierGroup(viewModel: ShadeHeaderViewModel, modifier: Modifie } } +@ExperimentalKairosApi +@Composable +private fun ShadeCarrierGroupKairos( + viewModel: ShadeHeaderViewModel, + modifier: Modifier = Modifier, +) { + Row(modifier = modifier) { + ActivatedKairosSpec( + buildSpec = viewModel.mobileIconsViewModelKairos.get().composeWrapper(), + kairosNetwork = viewModel.kairosNetwork, + ) { iconsViewModel: MobileIconsViewModelKairosComposeWrapper -> + for ((subId, icon) in iconsViewModel.icons) { + Spacer(modifier = Modifier.width(5.dp)) + val scope = rememberCoroutineScope() + AndroidView( + factory = { context -> + ModernShadeCarrierGroupMobileView.constructAndBind( + context = context, + logger = iconsViewModel.logger, + slot = "mobile_carrier_shade_group", + viewModel = + buildSpec { + ShadeCarrierGroupMobileIconViewModelKairos( + icon, + icon.iconInteractor, + ) + }, + scope = scope, + subscriptionId = subId, + location = StatusBarLocation.SHADE_CARRIER_GROUP, + kairosNetwork = viewModel.kairosNetwork, + ) + .first + .also { + it.setOnClickListener { viewModel.onShadeCarrierGroupClicked() } + } + } + ) + } + } + } +} + @Composable private fun ContentScope.StatusIcons( viewModel: ShadeHeaderViewModel, diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/ColumnVolumeSliders.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/ColumnVolumeSliders.kt index d9e8f02f005b..52b1e3aeb00c 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/ColumnVolumeSliders.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/ColumnVolumeSliders.kt @@ -30,9 +30,11 @@ import androidx.compose.animation.scaleOut import androidx.compose.animation.shrinkVertically import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.IconButtonDefaults @@ -42,7 +44,6 @@ import androidx.compose.runtime.State import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.semantics.Role @@ -154,9 +155,7 @@ fun ColumnVolumeSliders( totalCount = viewModels.size, ), ) - .thenIf(!Flags.volumeRedesign()) { - Modifier.padding(top = 16.dp) - }, + .padding(top = if (Flags.volumeRedesign()) 4.dp else 16.dp), state = sliderState, onValueChange = { newValue: Float -> sliderViewModel.onValueChanged(sliderState, newValue) @@ -223,7 +222,7 @@ private fun ExpandButtonLegacy( } @Composable -private fun ExpandButton( +private fun RowScope.ExpandButton( isExpanded: Boolean, isExpandable: Boolean, onExpandedChanged: (Boolean) -> Unit, @@ -243,16 +242,17 @@ private fun ExpandButton( ) { PlatformIconButton( modifier = - Modifier.size(width = 48.dp, height = 40.dp).semantics { + Modifier.size(40.dp).semantics { role = Role.Switch stateDescription = expandButtonStateDescription }, onClick = { onExpandedChanged(!isExpanded) }, colors = IconButtonDefaults.iconButtonColors( - containerColor = Color.Transparent, - contentColor = MaterialTheme.colorScheme.onSurfaceVariant, + containerColor = MaterialTheme.colorScheme.surfaceContainerHighest, + contentColor = MaterialTheme.colorScheme.onSurface, ), + shape = RoundedCornerShape(12.dp), iconResource = if (isExpanded) { R.drawable.ic_arrow_down_24dp diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt index da54cb8e4679..f9492dad85df 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/volume/ui/composable/VolumeSlider.kt @@ -30,14 +30,16 @@ import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi import androidx.compose.material3.Icon as MaterialIcon import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.SliderDefaults import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect @@ -58,31 +60,33 @@ import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.semantics.customActions import androidx.compose.ui.semantics.disabled import androidx.compose.ui.semantics.progressBarRangeInfo -import androidx.compose.ui.semantics.semantics import androidx.compose.ui.semantics.setProgress import androidx.compose.ui.semantics.stateDescription +import androidx.compose.ui.unit.DpSize import androidx.compose.ui.unit.dp import com.android.compose.PlatformSlider import com.android.compose.PlatformSliderColors import com.android.systemui.Flags -import com.android.systemui.common.shared.model.Icon +import com.android.systemui.common.shared.model.Icon as IconModel import com.android.systemui.common.ui.compose.Icon import com.android.systemui.compose.modifiers.sysuiResTag import com.android.systemui.haptics.slider.SliderHapticFeedbackFilter import com.android.systemui.haptics.slider.compose.ui.SliderHapticsViewModel import com.android.systemui.lifecycle.rememberViewModel import com.android.systemui.res.R +import com.android.systemui.volume.dialog.sliders.ui.compose.SliderTrack import com.android.systemui.volume.haptics.ui.VolumeHapticsConfigsProvider import com.android.systemui.volume.panel.component.volume.slider.ui.viewmodel.SliderState -import com.android.systemui.volume.ui.slider.AccessibilityParams -import com.android.systemui.volume.ui.slider.Haptics -import com.android.systemui.volume.ui.slider.Slider +import com.android.systemui.volume.ui.compose.slider.AccessibilityParams +import com.android.systemui.volume.ui.compose.slider.Haptics +import com.android.systemui.volume.ui.compose.slider.Slider +import com.android.systemui.volume.ui.compose.slider.SliderIcon import kotlin.math.round import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.map -@OptIn(ExperimentalMaterial3Api::class) +@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class) @Composable fun VolumeSlider( state: SliderState, @@ -92,7 +96,7 @@ fun VolumeSlider( modifier: Modifier = Modifier, hapticsViewModelFactory: SliderHapticsViewModel.Factory?, onValueChangeFinished: (() -> Unit)? = null, - button: (@Composable () -> Unit)? = null, + button: (@Composable RowScope.() -> Unit)? = null, ) { if (!Flags.volumeRedesign()) { LegacyVolumeSlider( @@ -107,54 +111,86 @@ fun VolumeSlider( return } - Column( - modifier = modifier.animateContentSize().semantics(true) {}, - verticalArrangement = Arrangement.Top, - ) { + Column(modifier = modifier.animateContentSize()) { + Text( + text = state.label, + style = MaterialTheme.typography.titleMedium, + color = MaterialTheme.colorScheme.onSurface, + modifier = Modifier.fillMaxWidth().clearAndSetSemantics {}, + ) Row( - horizontalArrangement = Arrangement.spacedBy(12.dp), - modifier = Modifier.fillMaxWidth().height(40.dp), + horizontalArrangement = Arrangement.spacedBy(8.dp), + modifier = Modifier.fillMaxWidth().padding(vertical = 4.dp), verticalAlignment = Alignment.CenterVertically, ) { - state.icon?.let { - Icon( - icon = it, - tint = MaterialTheme.colorScheme.onSurface, - modifier = Modifier.size(24.dp), + val materialSliderColors = + SliderDefaults.colors( + activeTickColor = MaterialTheme.colorScheme.surfaceContainerHigh, + inactiveTrackColor = MaterialTheme.colorScheme.surfaceContainerHigh, ) - } - Text( - text = state.label, - style = MaterialTheme.typography.titleMedium, - color = MaterialTheme.colorScheme.onSurface, - modifier = Modifier.weight(1f).clearAndSetSemantics {}, + Slider( + value = state.value, + valueRange = state.valueRange, + onValueChanged = onValueChange, + onValueChangeFinished = { onValueChangeFinished?.invoke() }, + colors = materialSliderColors, + isEnabled = state.isEnabled, + stepDistance = state.step, + accessibilityParams = + AccessibilityParams( + contentDescription = state.a11yContentDescription, + stateDescription = state.a11yStateDescription, + ), + track = { sliderState -> + SliderTrack( + sliderState = sliderState, + colors = materialSliderColors, + isEnabled = state.isEnabled, + activeTrackStartIcon = + state.icon?.let { icon -> + { iconsState -> + SliderIcon( + icon = { + Icon(icon = icon, modifier = Modifier.size(24.dp)) + }, + isVisible = iconsState.isActiveTrackStartIconVisible, + ) + } + }, + inactiveTrackStartIcon = + state.icon?.let { icon -> + { iconsState -> + SliderIcon( + icon = { + Icon(icon = icon, modifier = Modifier.size(24.dp)) + }, + isVisible = !iconsState.isActiveTrackStartIconVisible, + ) + } + }, + ) + }, + thumb = { sliderState, interactionSource -> + SliderDefaults.Thumb( + sliderState = sliderState, + interactionSource = interactionSource, + enabled = state.isEnabled, + colors = materialSliderColors, + thumbSize = DpSize(4.dp, 52.dp), + ) + }, + haptics = + hapticsViewModelFactory?.let { + Haptics.Enabled( + hapticsViewModelFactory = it, + hapticFilter = state.hapticFilter, + orientation = Orientation.Horizontal, + ) + } ?: Haptics.Disabled, + modifier = Modifier.weight(1f).sysuiResTag(state.label), ) - button?.invoke() + button?.invoke(this) } - - Slider( - value = state.value, - valueRange = state.valueRange, - onValueChanged = onValueChange, - onValueChangeFinished = { onValueChangeFinished?.invoke() }, - isEnabled = state.isEnabled, - stepDistance = state.step, - accessibilityParams = - AccessibilityParams( - contentDescription = state.a11yContentDescription, - stateDescription = state.a11yStateDescription, - ), - haptics = - hapticsViewModelFactory?.let { - Haptics.Enabled( - hapticsViewModelFactory = it, - hapticFilter = state.hapticFilter, - orientation = Orientation.Horizontal, - ) - } ?: Haptics.Disabled, - modifier = - Modifier.height(40.dp).padding(top = 4.dp, bottom = 12.dp).sysuiResTag(state.label), - ) state.disabledMessage?.let { disabledMessage -> AnimatedVisibility(visible = !state.isEnabled) { Row( @@ -253,7 +289,11 @@ private fun LegacyVolumeSlider( enabled = state.isEnabled, icon = { state.icon?.let { - SliderIcon(icon = it, onIconTapped = onIconTapped, isTappable = state.isMutable) + LegacySliderIcon( + icon = it, + onIconTapped = onIconTapped, + isTappable = state.isMutable, + ) } }, colors = sliderColors, @@ -289,8 +329,8 @@ private fun valueState(state: SliderState): State<Float> { } @Composable -private fun SliderIcon( - icon: Icon, +private fun LegacySliderIcon( + icon: IconModel, onIconTapped: () -> Unit, isTappable: Boolean, modifier: Modifier = Modifier, diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt index 96c3ac75587e..8744357a74c9 100644 --- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt +++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/FlexClockController.kt @@ -144,23 +144,25 @@ class FlexClockController(private val clockCtx: ClockContext) : ClockController listOf( GSFAxes.WEIGHT.toClockAxis( type = AxisType.Float, - currentValue = 475f, + currentValue = 400f, name = "Weight", description = "Glyph Weight", ), GSFAxes.WIDTH.toClockAxis( type = AxisType.Float, - currentValue = 85f, + currentValue = 80f, name = "Width", description = "Glyph Width", ), GSFAxes.ROUND.toClockAxis( type = AxisType.Boolean, + currentValue = 100f, name = "Round", description = "Glyph Roundness", ), GSFAxes.SLANT.toClockAxis( type = AxisType.Boolean, + currentValue = 0f, name = "Slant", description = "Glyph Slant", ), diff --git a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerTest.java b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerTest.java index 2845f6a2983a..e75f60736435 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/keyguard/KeyguardSecurityContainerTest.java @@ -507,8 +507,8 @@ public class KeyguardSecurityContainerTest extends SysuiTestCase { 0 /* flags */); users.add(new UserRecord(info, null, false /* isGuest */, false /* isCurrent */, false /* isAddUser */, false /* isRestricted */, true /* isSwitchToEnabled */, - false /* isAddSupervisedUser */, null /* enforcedAdmin */, - false /* isManageUsers */)); + false /* isAddSupervisedUser */, false /* isSignOut */, + null /* enforcedAdmin */, false /* isManageUsers */)); } return users; } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorTest.kt index c1feca29906a..91ec1cbce8a4 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractorTest.kt @@ -77,6 +77,7 @@ class PrimaryBouncerInteractorTest : SysuiTestCase() { private lateinit var resources: TestableResources private lateinit var trustRepository: FakeTrustRepository private lateinit var testScope: TestScope + private val TEST_REASON = "reason" @Before fun setUp() { @@ -118,7 +119,7 @@ class PrimaryBouncerInteractorTest : SysuiTestCase() { mainHandler.setMode(FakeHandler.Mode.QUEUEING) // WHEN bouncer show is requested - underTest.show(true) + underTest.show(true, TEST_REASON) // WHEN all queued messages are dispatched mainHandler.dispatchQueuedMessages() @@ -134,7 +135,7 @@ class PrimaryBouncerInteractorTest : SysuiTestCase() { @Test fun testShow_isScrimmed() { - underTest.show(true) + underTest.show(true, TEST_REASON) verify(repository).setKeyguardAuthenticatedBiometrics(null) verify(repository).setPrimaryStartingToHide(false) verify(repository).setPrimaryScrimmed(true) @@ -162,7 +163,7 @@ class PrimaryBouncerInteractorTest : SysuiTestCase() { @Test fun testShowReturnsFalseWhenDelegateIsNotSet() { whenever(bouncerView.delegate).thenReturn(null) - assertThat(underTest.show(true)).isEqualTo(false) + assertThat(underTest.show(true, TEST_REASON)).isEqualTo(false) } @Test @@ -171,7 +172,7 @@ class PrimaryBouncerInteractorTest : SysuiTestCase() { whenever(keyguardSecurityModel.getSecurityMode(anyInt())) .thenReturn(KeyguardSecurityModel.SecurityMode.SimPuk) - underTest.show(true) + underTest.show(true, TEST_REASON) verify(repository).setPrimaryShow(false) verify(repository).setPrimaryShow(true) } @@ -352,7 +353,7 @@ class PrimaryBouncerInteractorTest : SysuiTestCase() { whenever(faceAuthInteractor.canFaceAuthRun()).thenReturn(true) // WHEN bouncer show is requested - underTest.show(true) + underTest.show(true, TEST_REASON) // THEN primary show & primary showing soon aren't updated immediately verify(repository, never()).setPrimaryShow(true) @@ -375,7 +376,7 @@ class PrimaryBouncerInteractorTest : SysuiTestCase() { whenever(faceAuthInteractor.canFaceAuthRun()).thenReturn(false) // WHEN bouncer show is requested - underTest.show(true) + underTest.show(true, TEST_REASON) // THEN primary show & primary showing soon are updated immediately verify(repository).setPrimaryShow(true) @@ -394,7 +395,7 @@ class PrimaryBouncerInteractorTest : SysuiTestCase() { runCurrent() // WHEN bouncer show is requested - underTest.show(true) + underTest.show(true, TEST_REASON) // THEN primary show & primary showing soon were scheduled to update verify(repository, never()).setPrimaryShow(true) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt index 859137507bbf..358635e2400c 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayRepositoryTest.kt @@ -80,17 +80,18 @@ class DisplayRepositoryTest : SysuiTestCase() { testScope.backgroundScope, UnconfinedTestDispatcher(), ) - DisplayRepositoryImpl( + val displaysWithDecorRepository = + DisplaysWithDecorationsRepositoryImpl( commandQueue, windowManager, testScope.backgroundScope, displayRepositoryFromLib, ) - .also { - verify(displayManager, never()).registerDisplayListener(any(), any()) - // It needs to be called, just once, for the initial value. - verify(displayManager).getDisplays() - } + DisplayRepositoryImpl(displayRepositoryFromLib, displaysWithDecorRepository).also { + verify(displayManager, never()).registerDisplayListener(any(), any()) + // It needs to be called, just once, for the initial value. + verify(displayManager).getDisplays() + } } @Before diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayScopeRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayScopeRepositoryInstanceProviderTest.kt index da7a723f220e..8afaea32273d 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayScopeRepositoryImplTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/DisplayScopeRepositoryInstanceProviderTest.kt @@ -16,7 +16,6 @@ package com.android.systemui.display.data.repository -import android.platform.test.annotations.EnableFlags import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase @@ -24,77 +23,41 @@ import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.kosmos.testDispatcher import com.android.systemui.kosmos.testScope import com.android.systemui.kosmos.useUnconfinedTestDispatcher -import com.android.systemui.statusbar.core.StatusBarConnectedDisplays import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.isActive import kotlinx.coroutines.test.runTest -import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -@EnableFlags(StatusBarConnectedDisplays.FLAG_NAME) @RunWith(AndroidJUnit4::class) @SmallTest -class DisplayScopeRepositoryImplTest : SysuiTestCase() { +class DisplayScopeRepositoryInstanceProviderTest : SysuiTestCase() { private val kosmos = testKosmos().useUnconfinedTestDispatcher() private val testScope = kosmos.testScope - private val fakeDisplayRepository = kosmos.displayRepository - private val repo = - DisplayScopeRepositoryImpl( + private val underTest = + DisplayScopeRepositoryInstanceProvider( kosmos.applicationCoroutineScope, kosmos.testDispatcher, - fakeDisplayRepository, ) - @Before - fun setUp() { - repo.start() - } - - @Test - fun scopeForDisplay_multipleCallsForSameDisplayId_returnsSameInstance() { - val scopeForDisplay = repo.scopeForDisplay(displayId = 1) - - assertThat(repo.scopeForDisplay(displayId = 1)).isSameInstanceAs(scopeForDisplay) - } - - @Test - fun scopeForDisplay_differentDisplayId_returnsNewInstance() { - val scopeForDisplay1 = repo.scopeForDisplay(displayId = 1) - val scopeForDisplay2 = repo.scopeForDisplay(displayId = 2) - - assertThat(scopeForDisplay1).isNotSameInstanceAs(scopeForDisplay2) - } - @Test - fun scopeForDisplay_activeByDefault() = + fun createInstance_activeByDefault() = testScope.runTest { - val scopeForDisplay = repo.scopeForDisplay(displayId = 1) + val scopeForDisplay = underTest.createInstance(displayId = 1) assertThat(scopeForDisplay.isActive).isTrue() } @Test - fun scopeForDisplay_afterDisplayRemoved_scopeIsCancelled() = + fun destroyInstance_afterDisplayRemoved_scopeIsCancelled() = testScope.runTest { - val scopeForDisplay = repo.scopeForDisplay(displayId = 1) + val scopeForDisplay = underTest.createInstance(displayId = 1) - fakeDisplayRepository.removeDisplay(displayId = 1) + underTest.destroyInstance(scopeForDisplay) assertThat(scopeForDisplay.isActive).isFalse() } - - @Test - fun scopeForDisplay_afterDisplayRemoved_returnsNewInstance() = - testScope.runTest { - val initialScope = repo.scopeForDisplay(displayId = 1) - - fakeDisplayRepository.removeDisplay(displayId = 1) - - val newScope = repo.scopeForDisplay(displayId = 1) - assertThat(newScope).isNotSameInstanceAs(initialScope) - } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/PerDisplayInstanceRepositoryImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/PerDisplayInstanceRepositoryImplTest.kt index 28b9e733be94..bf49d92dd2bd 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/PerDisplayInstanceRepositoryImplTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/display/data/repository/PerDisplayInstanceRepositoryImplTest.kt @@ -44,9 +44,10 @@ class PerDisplayInstanceRepositoryImplTest : SysuiTestCase() { private val fakeDisplayRepository = kosmos.displayRepository private val fakePerDisplayInstanceProviderWithTeardown = kosmos.fakePerDisplayInstanceProviderWithTeardown + private val lifecycleManager = kosmos.fakeDisplayInstanceLifecycleManager private val underTest: PerDisplayInstanceRepositoryImpl<TestPerDisplayInstance> = - kosmos.fakePerDisplayInstanceRepository + kosmos.createPerDisplayInstanceRepository(overrideLifecycleManager = null) @Before fun addDisplays() = runBlocking { @@ -109,6 +110,43 @@ class PerDisplayInstanceRepositoryImplTest : SysuiTestCase() { verify(kosmos.dumpManager).registerNormalDumpable(anyString(), any()) } + @Test + fun perDisplay_afterCustomLifecycleManagerRemovesDisplay_destroyInstanceInvoked() = + testScope.runTest { + val underTest = + kosmos.createPerDisplayInstanceRepository( + overrideLifecycleManager = lifecycleManager + ) + // Let's start with both + lifecycleManager.displayIds.value = setOf(DEFAULT_DISPLAY_ID, NON_DEFAULT_DISPLAY_ID) + + val instance = underTest[NON_DEFAULT_DISPLAY_ID] + + lifecycleManager.displayIds.value = setOf(DEFAULT_DISPLAY_ID) + + // Now that the lifecycle manager says so, let's make sure it was destroyed + assertThat(fakePerDisplayInstanceProviderWithTeardown.destroyed) + .containsExactly(instance) + } + + @Test + fun perDisplay_lifecycleManagerDoesNotContainIt_displayRepositoryDoes_returnsNull() = + testScope.runTest { + val underTest = + kosmos.createPerDisplayInstanceRepository( + overrideLifecycleManager = lifecycleManager + ) + // only default display, so getting for the non-default one should fail, despite the + // repository having both displays already + lifecycleManager.displayIds.value = setOf(DEFAULT_DISPLAY_ID) + + assertThat(underTest[NON_DEFAULT_DISPLAY_ID]).isNull() + + lifecycleManager.displayIds.value = setOf(DEFAULT_DISPLAY_ID, NON_DEFAULT_DISPLAY_ID) + + assertThat(underTest[NON_DEFAULT_DISPLAY_ID]).isNotNull() + } + private fun createDisplay(displayId: Int): Display = display(type = Display.TYPE_INTERNAL, id = displayId) 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 0718d0d32812..83fd4c258082 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 @@ -264,6 +264,29 @@ class KeyguardInteractorTest : SysuiTestCase() { } @Test + fun dismissAlpha_doesNotEmitWhenNotDismissible() = + testScope.runTest { + val dismissAlpha by collectValues(underTest.dismissAlpha) + assertThat(dismissAlpha[0]).isEqualTo(1f) + assertThat(dismissAlpha.size).isEqualTo(1) + + keyguardTransitionRepository.sendTransitionSteps(from = AOD, to = LOCKSCREEN, testScope) + + // User begins to swipe up when not dimissible, which would show bouncer + repository.setStatusBarState(StatusBarState.KEYGUARD) + repository.setKeyguardDismissible(false) + shadeRepository.setLegacyShadeExpansion(0.98f) + + assertThat(dismissAlpha[0]).isEqualTo(1f) + assertThat(dismissAlpha.size).isEqualTo(1) + + // Shade reset should not affect dismiss alpha when not dismissible + shadeRepository.setLegacyShadeExpansion(0f) + assertThat(dismissAlpha[0]).isEqualTo(1f) + assertThat(dismissAlpha.size).isEqualTo(1) + } + + @Test fun dismissAlpha_onGlanceableHub_doesNotEmitWhenShadeResets() = testScope.runTest { val dismissAlpha by collectValues(underTest.dismissAlpha) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractorTest.kt index 6704d63395ad..582666561be2 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractorTest.kt @@ -267,12 +267,20 @@ class KeyguardKeyEventInteractorTest : SysuiTestCase() { // action down: does NOT collapse the shade val actionDownMenuKeyEvent = KeyEvent(KeyEvent.ACTION_DOWN, keycode) assertThat(underTest.dispatchKeyEvent(actionDownMenuKeyEvent)).isFalse() - verify(statusBarKeyguardViewManager, never()).showPrimaryBouncer(any()) + verify(statusBarKeyguardViewManager, never()) + .showPrimaryBouncer( + any(), + eq("KeyguardKeyEventInteractor#collapseShadeLockedOrShowPrimaryBouncer"), + ) // action up: collapses the shade val actionUpMenuKeyEvent = KeyEvent(KeyEvent.ACTION_UP, keycode) assertThat(underTest.dispatchKeyEvent(actionUpMenuKeyEvent)).isTrue() - verify(statusBarKeyguardViewManager).showPrimaryBouncer(eq(true)) + verify(statusBarKeyguardViewManager) + .showPrimaryBouncer( + eq(true), + eq("KeyguardKeyEventInteractor#collapseShadeLockedOrShowPrimaryBouncer"), + ) } private fun verifyActionsDoNothing(keycode: Int) { @@ -280,12 +288,20 @@ class KeyguardKeyEventInteractorTest : SysuiTestCase() { val actionDownMenuKeyEvent = KeyEvent(KeyEvent.ACTION_DOWN, keycode) assertThat(underTest.dispatchKeyEvent(actionDownMenuKeyEvent)).isFalse() verify(shadeController, never()).animateCollapseShadeForced() - verify(statusBarKeyguardViewManager, never()).showPrimaryBouncer(any()) + verify(statusBarKeyguardViewManager, never()) + .showPrimaryBouncer( + any(), + eq("KeyguardKeyEventInteractor#collapseShadeLockedOrShowPrimaryBouncer"), + ) // action up: doesNothing val actionUpMenuKeyEvent = KeyEvent(KeyEvent.ACTION_UP, keycode) assertThat(underTest.dispatchKeyEvent(actionUpMenuKeyEvent)).isFalse() verify(shadeController, never()).animateCollapseShadeForced() - verify(statusBarKeyguardViewManager, never()).showPrimaryBouncer(any()) + verify(statusBarKeyguardViewManager, never()) + .showPrimaryBouncer( + any(), + eq("KeyguardKeyEventInteractor#collapseShadeLockedOrShowPrimaryBouncer"), + ) } } 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 f0eedee48e57..4f351143c793 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 @@ -340,6 +340,22 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() { @Test @EnableSceneContainer + fun surfaceBehindVisibility_whileSceneContainerNotVisible_alwaysTrue() = + testScope.runTest { + val isSurfaceBehindVisible by collectLastValue(underTest.value.surfaceBehindVisibility) + val currentScene by collectLastValue(kosmos.sceneInteractor.currentScene) + assertThat(currentScene).isEqualTo(Scenes.Lockscreen) + assertThat(isSurfaceBehindVisible).isFalse() + + kosmos.sceneInteractor.setVisible(false, "test") + runCurrent() + + assertThat(currentScene).isEqualTo(Scenes.Lockscreen) + assertThat(isSurfaceBehindVisible).isTrue() + } + + @Test + @EnableSceneContainer fun surfaceBehindVisibility_idleWhileLocked_alwaysFalse() = testScope.runTest { val isSurfaceBehindVisible by collectLastValue(underTest.value.surfaceBehindVisibility) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelTest.kt index e1323c166f6b..9aee4c97f214 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelTest.kt @@ -35,6 +35,7 @@ import com.android.systemui.plugins.ActivityStarter import com.android.systemui.statusbar.phone.statusBarKeyguardViewManager import com.android.systemui.testKosmos import com.android.systemui.util.mockito.any +import com.android.systemui.util.mockito.eq import com.google.common.collect.Range import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.test.runTest @@ -56,7 +57,8 @@ class AlternateBouncerViewModelTest : SysuiTestCase() { fun onTapped() = testScope.runTest { underTest.onTapped() - verify(statusBarKeyguardViewManager).showPrimaryBouncer(any()) + verify(statusBarKeyguardViewManager) + .showPrimaryBouncer(any(), eq("AlternateBouncerViewModel#onTapped")) } @Test @@ -154,7 +156,7 @@ class AlternateBouncerViewModelTest : SysuiTestCase() { private fun stepToAlternateBouncer( value: Float, - state: TransitionState = TransitionState.RUNNING + state: TransitionState = TransitionState.RUNNING, ): TransitionStep { return step( from = KeyguardState.LOCKSCREEN, @@ -166,7 +168,7 @@ class AlternateBouncerViewModelTest : SysuiTestCase() { private fun stepFromAlternateBouncer( value: Float, - state: TransitionState = TransitionState.RUNNING + state: TransitionState = TransitionState.RUNNING, ): TransitionStep { return step( from = KeyguardState.ALTERNATE_BOUNCER, @@ -180,14 +182,14 @@ class AlternateBouncerViewModelTest : SysuiTestCase() { from: KeyguardState, to: KeyguardState, value: Float, - transitionState: TransitionState + transitionState: TransitionState, ): TransitionStep { return TransitionStep( from = from, to = to, value = value, transitionState = transitionState, - ownerName = "AlternateBouncerViewModelTest" + ownerName = "AlternateBouncerViewModelTest", ) } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/model/SysUiStateExtTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/model/SysUiStateExtTest.kt index d1b552906fbb..5d4de02f9aaa 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/model/SysUiStateExtTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/model/SysUiStateExtTest.kt @@ -16,7 +16,6 @@ package com.android.systemui.model -import android.view.Display import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase @@ -35,7 +34,7 @@ class SysUiStateExtTest : SysuiTestCase() { @Test fun updateFlags() { - underTest.updateFlags(Display.DEFAULT_DISPLAY, 1L to true, 2L to false, 4L to true) + underTest.updateFlags(1L to true, 2L to false, 4L to true) assertThat(underTest.flags and 1L).isNotEqualTo(0L) assertThat(underTest.flags and 2L).isEqualTo(0L) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/EditTileListStateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/EditTileListStateTest.kt index 8b9ae9a0606d..2dd2f7c0f562 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/EditTileListStateTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/EditTileListStateTest.kt @@ -23,6 +23,7 @@ import com.android.systemui.SysuiTestCase import com.android.systemui.common.shared.model.Icon import com.android.systemui.qs.panels.shared.model.SizedTile import com.android.systemui.qs.panels.shared.model.SizedTileImpl +import com.android.systemui.qs.panels.ui.compose.selection.PlacementEvent import com.android.systemui.qs.panels.ui.model.GridCell import com.android.systemui.qs.panels.ui.model.TileGridCell import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModel @@ -108,6 +109,76 @@ class EditTileListStateTest : SysuiTestCase() { assertThat(underTest.tiles.toStrings()).doesNotContain(TestEditTiles[0].tile.tileSpec.spec) } + @Test + fun targetIndexForPlacementToTileSpec_returnsCorrectIndex() { + val placementEvent = + PlacementEvent.PlaceToTileSpec( + movingSpec = TestEditTiles[0].tile.tileSpec, + targetSpec = TestEditTiles[3].tile.tileSpec, + ) + val index = underTest.targetIndexForPlacement(placementEvent) + + assertThat(index).isEqualTo(3) + } + + @Test + fun targetIndexForPlacementToIndex_indexOutOfBounds_returnsCorrectIndex() { + val placementEventTooLow = + PlacementEvent.PlaceToIndex( + movingSpec = TestEditTiles[0].tile.tileSpec, + targetIndex = -1, + ) + val index1 = underTest.targetIndexForPlacement(placementEventTooLow) + + assertThat(index1).isEqualTo(0) + + val placementEventTooHigh = + PlacementEvent.PlaceToIndex( + movingSpec = TestEditTiles[0].tile.tileSpec, + targetIndex = 10, + ) + val index2 = underTest.targetIndexForPlacement(placementEventTooHigh) + assertThat(index2).isEqualTo(TestEditTiles.size) + } + + @Test + fun targetIndexForPlacementToIndex_movingBack_returnsCorrectIndex() { + /** + * With the grid: [ a ] [ b ] [ c ] [ Large D ] [ e ] [ f ] + * + * Moving 'e' to the spacer at index 3 will result in the tilespec order: a, b, c, e, d, f + * + * 'e' is now at index 3 + */ + val placementEvent = + PlacementEvent.PlaceToIndex( + movingSpec = TestEditTiles[4].tile.tileSpec, + targetIndex = 3, + ) + val index = underTest.targetIndexForPlacement(placementEvent) + + assertThat(index).isEqualTo(3) + } + + @Test + fun targetIndexForPlacementToIndex_movingForward_returnsCorrectIndex() { + /** + * With the grid: [ a ] [ b ] [ c ] [ Large D ] [ e ] [ f ] + * + * Moving '1' to the spacer at index 3 will result in the tilespec order: b, c, a, d, e, f + * + * 'a' is now at index 2 + */ + val placementEvent = + PlacementEvent.PlaceToIndex( + movingSpec = TestEditTiles[0].tile.tileSpec, + targetIndex = 3, + ) + val index = underTest.targetIndexForPlacement(placementEvent) + + assertThat(index).isEqualTo(2) + } + private fun List<GridCell>.toStrings(): List<String> { return map { if (it is TileGridCell) { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/selection/MutableSelectionStateTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/selection/MutableSelectionStateTest.kt index ab217a3f50ef..33ee3379c0d6 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/selection/MutableSelectionStateTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/panels/ui/compose/selection/MutableSelectionStateTest.kt @@ -19,8 +19,14 @@ package com.android.systemui.qs.panels.ui.compose.selection import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase +import com.android.systemui.qs.panels.ui.compose.selection.TileState.GreyedOut +import com.android.systemui.qs.panels.ui.compose.selection.TileState.None +import com.android.systemui.qs.panels.ui.compose.selection.TileState.Placeable +import com.android.systemui.qs.panels.ui.compose.selection.TileState.Removable +import com.android.systemui.qs.panels.ui.compose.selection.TileState.Selected import com.android.systemui.qs.pipeline.shared.TileSpec import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith @@ -45,7 +51,104 @@ class MutableSelectionStateTest : SysuiTestCase() { assertThat(underTest.selection).isEqualTo(newSpec) } + @Test + fun placementModeEnabled_tapOnIndex_sendsCorrectPlacementEvent() { + // Tap while in placement mode + underTest.enterPlacementMode(TEST_SPEC) + underTest.onTap(2) + + assertThat(underTest.placementEnabled).isFalse() + val event = underTest.placementEvent as PlacementEvent.PlaceToIndex + assertThat(event.movingSpec).isEqualTo(TEST_SPEC) + assertThat(event.targetIndex).isEqualTo(2) + } + + @Test + fun placementModeDisabled_tapOnIndex_doesNotSendPlacementEvent() { + // Tap while placement mode is disabled + underTest.onTap(2) + + assertThat(underTest.placementEnabled).isFalse() + assertThat(underTest.placementEvent).isNull() + } + + @Test + fun placementModeEnabled_tapOnSelection_exitPlacementMode() { + // Tap while in placement mode + underTest.enterPlacementMode(TEST_SPEC) + underTest.onTap(TEST_SPEC) + + assertThat(underTest.placementEnabled).isFalse() + assertThat(underTest.placementEvent).isNull() + } + + @Test + fun placementModeEnabled_tapOnTileSpec_sendsCorrectPlacementEvent() { + // Tap while in placement mode + underTest.enterPlacementMode(TEST_SPEC) + underTest.onTap(TEST_SPEC_2) + + assertThat(underTest.placementEnabled).isFalse() + val event = underTest.placementEvent as PlacementEvent.PlaceToTileSpec + assertThat(event.movingSpec).isEqualTo(TEST_SPEC) + assertThat(event.targetSpec).isEqualTo(TEST_SPEC_2) + } + + @Test + fun placementModeDisabled_tapOnSelection_unselect() { + // Select the tile and tap on it + underTest.select(TEST_SPEC) + underTest.onTap(TEST_SPEC) + + assertThat(underTest.placementEnabled).isFalse() + assertThat(underTest.selected).isFalse() + } + + @Test + fun placementModeDisabled_tapOnTile_selects() { + // Select a tile but tap a second one + underTest.select(TEST_SPEC) + underTest.onTap(TEST_SPEC_2) + + assertThat(underTest.placementEnabled).isFalse() + assertThat(underTest.selection).isEqualTo(TEST_SPEC_2) + } + + @Test + fun tileStateFor_selectedTile_returnsSingleSelection() = runTest { + underTest.select(TEST_SPEC) + + assertThat(underTest.tileStateFor(TEST_SPEC, None, canShowRemovalBadge = true)) + .isEqualTo(Selected) + assertThat(underTest.tileStateFor(TEST_SPEC_2, None, canShowRemovalBadge = true)) + .isEqualTo(Removable) + assertThat(underTest.tileStateFor(TEST_SPEC_3, None, canShowRemovalBadge = true)) + .isEqualTo(Removable) + } + + @Test + fun tileStateFor_placementMode_returnsSinglePlaceable() = runTest { + underTest.enterPlacementMode(TEST_SPEC) + + assertThat(underTest.tileStateFor(TEST_SPEC, None, canShowRemovalBadge = true)) + .isEqualTo(Placeable) + assertThat(underTest.tileStateFor(TEST_SPEC_2, None, canShowRemovalBadge = true)) + .isEqualTo(GreyedOut) + assertThat(underTest.tileStateFor(TEST_SPEC_3, None, canShowRemovalBadge = true)) + .isEqualTo(GreyedOut) + } + + @Test + fun tileStateFor_nonRemovableTile_returnsNoneState() = runTest { + assertThat(underTest.tileStateFor(TEST_SPEC, None, canShowRemovalBadge = true)) + .isEqualTo(Removable) + assertThat(underTest.tileStateFor(TEST_SPEC_2, None, canShowRemovalBadge = false)) + .isEqualTo(None) + } + companion object { private val TEST_SPEC = TileSpec.create("testSpec") + private val TEST_SPEC_2 = TileSpec.create("testSpec2") + private val TEST_SPEC_3 = TileSpec.create("testSpec3") } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/CastTileTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/CastTileTest.java index 765c5749cd4b..d880aa604849 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/CastTileTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/CastTileTest.java @@ -50,6 +50,7 @@ import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.qs.QSHost; import com.android.systemui.qs.QsEventLogger; import com.android.systemui.qs.logging.QSLogger; +import com.android.systemui.qs.tiles.dialog.CastDetailsViewModel; import com.android.systemui.shade.domain.interactor.FakeShadeDialogContextInteractor; import com.android.systemui.shade.domain.interactor.ShadeDialogContextInteractor; import com.android.systemui.statusbar.connectivity.IconState; @@ -63,6 +64,7 @@ import com.android.systemui.statusbar.policy.HotspotController; import com.android.systemui.statusbar.policy.KeyguardStateController; import org.junit.After; +import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -104,6 +106,8 @@ public class CastTileTest extends SysuiTestCase { private DialogTransitionAnimator mDialogTransitionAnimator; @Mock private QsEventLogger mUiEventLogger; + @Mock + private CastDetailsViewModel.Factory mCastDetailsViewModelFactory; private final TileJavaAdapter mJavaAdapter = new TileJavaAdapter(); private final FakeConnectivityRepository mConnectivityRepository = @@ -517,6 +521,29 @@ public class CastTileTest extends SysuiTestCase { assertTrue(mCastTile.getState().forceExpandIcon); } + @Test + public void testDetailsViewUnavailableState_returnsNull() { + createAndStartTileNewImpl(); + mTestableLooper.processAllMessages(); + + assertEquals(Tile.STATE_UNAVAILABLE, mCastTile.getState().state); + mCastTile.getDetailsViewModel(Assert::assertNull); + } + + @Test + public void testDetailsViewAvailableState_returnsNotNull() { + createAndStartTileNewImpl(); + CastDevice device = createConnectedCastDevice(); + List<CastDevice> devices = new ArrayList<>(); + devices.add(device); + when(mController.getCastDevices()).thenReturn(devices); + mConnectivityRepository.setWifiConnected(true); + mTestableLooper.processAllMessages(); + + assertEquals(Tile.STATE_ACTIVE, mCastTile.getState().state); + mCastTile.getDetailsViewModel(Assert::assertNotNull); + } + /** * For simplicity, let this method still set the field even though that's kind of gross */ @@ -540,7 +567,8 @@ public class CastTileTest extends SysuiTestCase { mConnectivityRepository, mJavaAdapter, mFeatureFlags, - mShadeDialogContextInteractor + mShadeDialogContextInteractor, + mCastDetailsViewModelFactory ); mCastTile.initialize(); @@ -584,7 +612,8 @@ public class CastTileTest extends SysuiTestCase { mConnectivityRepository, mJavaAdapter, mFeatureFlags, - mShadeDialogContextInteractor + mShadeDialogContextInteractor, + mCastDetailsViewModelFactory ); mCastTile.initialize(); diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/dialog/CastDetailsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/dialog/CastDetailsViewModelTest.kt new file mode 100644 index 000000000000..468c3dc3be93 --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/dialog/CastDetailsViewModelTest.kt @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles.dialog + +import android.content.Context +import android.media.MediaRouter +import android.provider.Settings +import android.testing.TestableLooper.RunWithLooper +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.internal.app.MediaRouteDialogPresenter +import com.android.systemui.SysuiTestCase +import com.android.systemui.qs.tiles.base.domain.actions.FakeQSTileIntentUserInputHandler +import com.android.systemui.qs.tiles.base.domain.actions.intentInputs +import com.google.common.truth.Truth.assertThat +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.mock +import org.mockito.kotlin.stub + +@SmallTest +@RunWithLooper(setAsMainLooper = true) +@RunWith(AndroidJUnit4::class) +class CastDetailsViewModelTest : SysuiTestCase() { + var inputHandler: FakeQSTileIntentUserInputHandler = FakeQSTileIntentUserInputHandler() + private var context: Context = mock() + private var mediaRouter: MediaRouter = mock() + private var selectedRoute: MediaRouter.RouteInfo = mock() + + @Test + fun testClickOnSettingsButton() { + var viewModel = CastDetailsViewModel(inputHandler, context, MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY) + + viewModel.clickOnSettingsButton() + + assertThat(inputHandler.handledInputs).hasSize(1) + val intentInput = inputHandler.intentInputs.last() + assertThat(intentInput.expandable).isNull() + assertThat(intentInput.intent.action).isEqualTo(Settings.ACTION_CAST_SETTINGS) + } + + @Test + fun testShouldShowChooserDialog() { + context.stub { + on { getSystemService(MediaRouter::class.java) } doReturn mediaRouter + } + mediaRouter.stub { + on { selectedRoute } doReturn selectedRoute + } + + var viewModel = + CastDetailsViewModel(inputHandler, context, MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY) + + assertThat(viewModel.shouldShowChooserDialog()) + .isEqualTo( + MediaRouteDialogPresenter.shouldShowChooserDialog( + context, + MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY, + ) + ) + } +} 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 1743e056b65c..6d4fffdefb1b 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 @@ -22,7 +22,6 @@ import android.os.PowerManager import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags import android.provider.Settings -import android.view.Display import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.compose.animation.scene.ObservableTransitionState @@ -1213,15 +1212,15 @@ class SceneContainerStartableTest : SysuiTestCase() { fakeSceneDataSource.pause() sceneInteractor.changeScene(sceneKey, "reason") runCurrent() - verify(sysUiState, times(index)).commitUpdate(Display.DEFAULT_DISPLAY) + verify(sysUiState, times(index)).commitUpdate() fakeSceneDataSource.unpause(expectedScene = sceneKey) runCurrent() - verify(sysUiState, times(index)).commitUpdate(Display.DEFAULT_DISPLAY) + verify(sysUiState, times(index)).commitUpdate() transitionStateFlow.value = ObservableTransitionState.Idle(sceneKey) runCurrent() - verify(sysUiState, times(index + 1)).commitUpdate(Display.DEFAULT_DISPLAY) + verify(sysUiState, times(index + 1)).commitUpdate() } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/carrier/ShadeCarrierGroupControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/carrier/ShadeCarrierGroupControllerTest.java index cd55bb23c1fc..6f1150e5fd96 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/carrier/ShadeCarrierGroupControllerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/carrier/ShadeCarrierGroupControllerTest.java @@ -46,6 +46,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.keyguard.CarrierTextManager; +import com.android.systemui.kairos.KairosNetwork; import com.android.systemui.log.core.FakeLogBuffer; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.statusbar.connectivity.IconState; @@ -55,6 +56,7 @@ import com.android.systemui.statusbar.connectivity.SignalCallback; import com.android.systemui.statusbar.connectivity.ui.MobileContextProvider; import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags; import com.android.systemui.statusbar.pipeline.mobile.ui.MobileUiAdapter; +import com.android.systemui.statusbar.pipeline.mobile.ui.MobileUiAdapterKairos; import com.android.systemui.statusbar.pipeline.mobile.ui.MobileViewLogger; import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsViewModel; import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.ShadeCarrierGroupMobileIconViewModel; @@ -63,6 +65,7 @@ import com.android.systemui.util.kotlin.FlowProviderKt; import com.android.systemui.utils.leaks.LeakCheckedTest; import com.android.systemui.utils.os.FakeHandler; +import kotlinx.coroutines.CoroutineScope; import kotlinx.coroutines.flow.MutableStateFlow; import org.junit.Before; @@ -178,8 +181,10 @@ public class ShadeCarrierGroupControllerTest extends LeakCheckedTest { mSlotIndexResolver, mMobileUiAdapter, mMobileContextProvider, - mStatusBarPipelineFlags - ) + mStatusBarPipelineFlags, + mock(CoroutineScope.class), + mock(KairosNetwork.class), + () -> mock(MobileUiAdapterKairos.class)) .setShadeCarrierGroup(mShadeCarrierGroup) .build(); diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/CommandQueueTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/CommandQueueTest.java index 70df82d95008..c26f18f5ab6d 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/CommandQueueTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/CommandQueueTest.java @@ -36,7 +36,9 @@ import android.hardware.biometrics.IBiometricSysuiReceiver; import android.hardware.biometrics.PromptInfo; import android.hardware.fingerprint.IUdfpsRefreshRateRequestCallback; import android.os.Bundle; +import android.os.RemoteException; import android.platform.test.annotations.EnableFlags; +import android.util.Pair; import android.view.KeyEvent; import android.view.WindowInsets; import android.view.WindowInsets.Type.InsetsType; @@ -46,6 +48,7 @@ import android.view.WindowInsetsController.Behavior; import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; +import com.android.internal.statusbar.DisableStates; import com.android.internal.statusbar.LetterboxDetails; import com.android.internal.statusbar.StatusBarIcon; import com.android.internal.view.AppearanceRegion; @@ -58,11 +61,14 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import java.util.HashMap; +import java.util.Map; + @SmallTest @RunWith(AndroidJUnit4.class) public class CommandQueueTest extends SysuiTestCase { - private static final LetterboxDetails[] TEST_LETTERBOX_DETAILS = new LetterboxDetails[] { + private static final LetterboxDetails[] TEST_LETTERBOX_DETAILS = new LetterboxDetails[]{ new LetterboxDetails( /* letterboxInnerBounds= */ new Rect(100, 0, 200, 500), /* letterboxFullBounds= */ new Rect(0, 0, 500, 100), @@ -119,6 +125,27 @@ public class CommandQueueTest extends SysuiTestCase { } @Test + public void testDisableForAllDisplays() throws RemoteException { + int state1 = 14; + int state2 = 42; + int secondaryDisplayState1 = 16; + int secondaryDisplayState2 = 44; + Map<Integer, Pair<Integer, Integer>> displaysWithStates = new HashMap<>(); + displaysWithStates.put(DEFAULT_DISPLAY, new Pair<>(state1, state2)); // Example values + displaysWithStates.put(SECONDARY_DISPLAY, + new Pair<>(secondaryDisplayState1, secondaryDisplayState2)); // Example values + DisableStates expectedDisableStates = new DisableStates(displaysWithStates, true); + + mCommandQueue.disableForAllDisplays(expectedDisableStates); + waitForIdleSync(); + + verify(mCallbacks).disable(eq(DEFAULT_DISPLAY), eq(state1), eq(state2), eq(true)); + verify(mCallbacks).disable(eq(SECONDARY_DISPLAY), eq(secondaryDisplayState1), + eq(secondaryDisplayState2), eq(true)); + } + + + @Test public void testExpandNotifications() { mCommandQueue.animateExpandNotificationsPanel(); waitForIdleSync(); @@ -475,7 +502,8 @@ public class CommandQueueTest extends SysuiTestCase { final long requestId = 10; mCommandQueue.showAuthenticationDialog(promptInfo, receiver, sensorIds, - credentialAllowed, requireConfirmation, userId, operationId, packageName, requestId); + credentialAllowed, requireConfirmation, userId, operationId, packageName, + requestId); waitForIdleSync(); verify(mCallbacks).showAuthenticationDialog(eq(promptInfo), eq(receiver), eq(sensorIds), eq(credentialAllowed), eq(requireConfirmation), eq(userId), eq(operationId), diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationGroupingUtilTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationGroupingUtilTest.kt index e04162bf990a..18f25cd4838b 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationGroupingUtilTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationGroupingUtilTest.kt @@ -16,24 +16,35 @@ package com.android.systemui.statusbar +import android.app.Notification +import android.platform.test.annotations.EnableFlags import android.platform.test.flag.junit.FlagsParameterization import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase +import com.android.systemui.res.R +import com.android.systemui.statusbar.notification.collection.BundleEntry +import com.android.systemui.statusbar.notification.collection.EntryAdapterFactory +import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.row.NotificationTestHelper +import com.android.systemui.statusbar.notification.row.entryAdapterFactory import com.android.systemui.statusbar.notification.shared.NotificationBundleUi +import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import org.junit.Before import org.junit.Test import org.junit.runner.RunWith +import org.mockito.Mockito.mock +import org.mockito.Mockito.`when` import platform.test.runner.parameterized.ParameterizedAndroidJunit4 import platform.test.runner.parameterized.Parameters @SmallTest @RunWith(ParameterizedAndroidJunit4::class) class NotificationGroupingUtilTest(flags: FlagsParameterization) : SysuiTestCase() { - + private val kosmos = testKosmos() private lateinit var underTest: NotificationGroupingUtil + private val factory: EntryAdapterFactory = kosmos.entryAdapterFactory private lateinit var testHelper: NotificationTestHelper companion object { @@ -60,4 +71,55 @@ class NotificationGroupingUtilTest(flags: FlagsParameterization) : SysuiTestCase underTest = NotificationGroupingUtil(row) assertThat(underTest.showsTime(row)).isTrue() } + + @Test + fun iconExtractor_extractsSbn_notification() { + val row = testHelper.createRow() + + underTest = NotificationGroupingUtil(row) + + assertThat(NotificationGroupingUtil.ICON_EXTRACTOR.extractData(row)).isInstanceOf( + Notification::class.java) + } + + @Test + @EnableFlags(NotificationBundleUi.FLAG_NAME) + fun iconExtractor_noException_bundle() { + val row = mock(ExpandableNotificationRow::class.java) + val be = BundleEntry("promotions") + `when`(row.entryAdapter).thenReturn(factory.create(be)) + + underTest = NotificationGroupingUtil(row) + + assertThat(NotificationGroupingUtil.ICON_EXTRACTOR.extractData(row)).isNull() + } + + @Test + fun iconComparator_sameNotificationIcon() { + val n1 = NotificationGroupingUtil.ICON_EXTRACTOR.extractData(testHelper.createRow()) + val n2 = NotificationGroupingUtil.ICON_EXTRACTOR.extractData(testHelper.createRow()) + + assertThat(NotificationGroupingUtil.IconComparator().hasSameIcon(n1, n2)).isTrue() + } + + @Test + fun iconComparator_differentNotificationIcon() { + val notif = Notification.Builder(mContext, "").setSmallIcon(R.drawable.ic_menu).build() + val n1 = NotificationGroupingUtil.ICON_EXTRACTOR.extractData(testHelper.createRow(notif)) + val n2 = NotificationGroupingUtil.ICON_EXTRACTOR.extractData(testHelper.createRow()) + + assertThat(NotificationGroupingUtil.IconComparator().hasSameIcon(n1, n2)).isFalse() + } + + @Test + @EnableFlags(NotificationBundleUi.FLAG_NAME) + fun iconComparator_bundleNotification() { + assertThat(NotificationGroupingUtil.IconComparator().hasSameIcon(null, + NotificationGroupingUtil.ICON_EXTRACTOR.extractData(testHelper.createRow()))).isFalse() + } + + @Test + fun iconComparator_twoBundles() { + assertThat(NotificationGroupingUtil.IconComparator().hasSameIcon(null, null)).isFalse() + } }
\ No newline at end of file diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt index 3ecf302204bc..67af7a54988e 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/NotificationShadeDepthControllerTest.kt @@ -33,6 +33,7 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.kosmos.testScope import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.shade.ShadeExpansionChangeEvent +import com.android.systemui.shade.data.repository.fakeShadeDisplaysRepository import com.android.systemui.shade.domain.interactor.ShadeModeInteractor import com.android.systemui.statusbar.phone.BiometricUnlockController import com.android.systemui.statusbar.phone.DozeParameters @@ -45,6 +46,8 @@ import com.android.systemui.wallpapers.domain.interactor.WallpaperInteractor import com.android.systemui.window.domain.interactor.WindowRootViewBlurInteractor import com.android.wm.shell.appzoomout.AppZoomOut import com.google.common.truth.Truth.assertThat +import java.util.Optional +import java.util.function.Consumer import org.junit.Before import org.junit.Rule import org.junit.Test @@ -65,8 +68,6 @@ import org.mockito.Mockito.reset import org.mockito.Mockito.verify import org.mockito.Mockito.`when` import org.mockito.junit.MockitoJUnit -import java.util.Optional -import java.util.function.Consumer @RunWith(AndroidJUnit4::class) @RunWithLooper @@ -75,6 +76,7 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() { private val kosmos = testKosmos() private val applicationScope = kosmos.testScope.backgroundScope + private val shadeDisplayRepository = kosmos.fakeShadeDisplaysRepository @Mock private lateinit var statusBarStateController: StatusBarStateController @Mock private lateinit var blurUtils: BlurUtils @Mock private lateinit var biometricUnlockController: BiometricUnlockController @@ -135,7 +137,8 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() { windowRootViewBlurInteractor, appZoomOutOptional, applicationScope, - dumpManager + dumpManager, + { shadeDisplayRepository }, ) notificationShadeDepthController.shadeAnimation = shadeAnimation notificationShadeDepthController.brightnessMirrorSpring = brightnessSpring @@ -355,6 +358,36 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() { } @Test + @EnableFlags(Flags.FLAG_SHADE_WINDOW_GOES_AROUND) + fun updateBlurCallback_shadeInExternalDisplay_doesSetZeroZoom() { + notificationShadeDepthController.onPanelExpansionChanged( + ShadeExpansionChangeEvent(fraction = 1f, expanded = true, tracking = false) + ) + notificationShadeDepthController.addListener(listener) + shadeDisplayRepository.setDisplayId(1) // not default display. + + notificationShadeDepthController.updateBlurCallback.doFrame(0) + + verify(wallpaperController).setNotificationShadeZoom(eq(0f)) + verify(listener).onWallpaperZoomOutChanged(eq(0f)) + } + + @Test + @EnableFlags(Flags.FLAG_SHADE_WINDOW_GOES_AROUND) + fun updateBlurCallback_shadeInDefaultDisplay_doesNotSetZeroZoom() { + notificationShadeDepthController.onPanelExpansionChanged( + ShadeExpansionChangeEvent(fraction = 1f, expanded = true, tracking = false) + ) + notificationShadeDepthController.addListener(listener) + shadeDisplayRepository.setDisplayId(0) // shade is in default display + + notificationShadeDepthController.updateBlurCallback.doFrame(0) + + verify(wallpaperController).setNotificationShadeZoom(floatThat { it != 0f }) + verify(listener).onWallpaperZoomOutChanged(floatThat { it != 0f }) + } + + @Test @DisableFlags(Flags.FLAG_NOTIFICATION_SHADE_BLUR) fun updateBlurCallback_setsOpaque_whenScrim() { scrimVisibilityCaptor.value.accept(ScrimController.OPAQUE) @@ -488,10 +521,10 @@ class NotificationShadeDepthControllerTest : SysuiTestCase() { } private fun enableSplitShade() { - `when` (shadeModeInteractor.isSplitShade).thenReturn(true) + `when`(shadeModeInteractor.isSplitShade).thenReturn(true) } private fun disableSplitShade() { - `when` (shadeModeInteractor.isSplitShade).thenReturn(false) + `when`(shadeModeInteractor.isSplitShade).thenReturn(false) } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt index 8120c2d9f816..5ec9b601bcca 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModelTest.kt @@ -44,6 +44,7 @@ import com.android.systemui.statusbar.chips.ui.model.OngoingActivityChipModel import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer import com.android.systemui.statusbar.core.StatusBarConnectedDisplays import com.android.systemui.statusbar.core.StatusBarRootModernization +import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentBuilder import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel import com.android.systemui.statusbar.phone.ongoingcall.DisableChipsModernization import com.android.systemui.statusbar.phone.ongoingcall.EnableChipsModernization @@ -106,6 +107,7 @@ class CallChipViewModelTest(flags: FlagsParameterization) : SysuiTestCase() { assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active.IconOnly::class.java) assertThat((latest as OngoingActivityChipModel.Active).isHidden).isFalse() + assertThat((latest as OngoingActivityChipModel.Active).isImportantForPrivacy).isFalse() } @Test @@ -117,6 +119,7 @@ class CallChipViewModelTest(flags: FlagsParameterization) : SysuiTestCase() { assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active.IconOnly::class.java) assertThat((latest as OngoingActivityChipModel.Active).isHidden).isFalse() + assertThat((latest as OngoingActivityChipModel.Active).isImportantForPrivacy).isFalse() } @Test @@ -128,6 +131,7 @@ class CallChipViewModelTest(flags: FlagsParameterization) : SysuiTestCase() { assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active.Timer::class.java) assertThat((latest as OngoingActivityChipModel.Active).isHidden).isFalse() + assertThat((latest as OngoingActivityChipModel.Active).isImportantForPrivacy).isFalse() } @Test @@ -152,6 +156,7 @@ class CallChipViewModelTest(flags: FlagsParameterization) : SysuiTestCase() { assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active.IconOnly::class.java) assertThat((latest as OngoingActivityChipModel.Active).isHidden).isTrue() + assertThat((latest as OngoingActivityChipModel.Active).isImportantForPrivacy).isFalse() } @Test @@ -176,6 +181,7 @@ class CallChipViewModelTest(flags: FlagsParameterization) : SysuiTestCase() { assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active.IconOnly::class.java) assertThat((latest as OngoingActivityChipModel.Active).isHidden).isTrue() + assertThat((latest as OngoingActivityChipModel.Active).isImportantForPrivacy).isFalse() } @Test @@ -200,6 +206,7 @@ class CallChipViewModelTest(flags: FlagsParameterization) : SysuiTestCase() { assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active.Timer::class.java) assertThat((latest as OngoingActivityChipModel.Active).isHidden).isTrue() + assertThat((latest as OngoingActivityChipModel.Active).isImportantForPrivacy).isFalse() } @Test @@ -793,8 +800,8 @@ class CallChipViewModelTest(flags: FlagsParameterization) : SysuiTestCase() { } private val PROMOTED_CONTENT_WITH_COLOR = - PromotedNotificationContentModel.Builder("notif") - .apply { + PromotedNotificationContentBuilder("notif") + .applyToShared { this.colors = PromotedNotificationContentModel.Colors( backgroundColor = PROMOTED_BACKGROUND_COLOR, diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModelTest.kt index d921ab3b284d..a60e7c19d8bd 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModelTest.kt @@ -135,6 +135,7 @@ class CastToOtherDeviceChipViewModelTest : SysuiTestCase() { ) assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active.Timer::class.java) + assertThat((latest as OngoingActivityChipModel.Active).isImportantForPrivacy).isTrue() val icon = (((latest as OngoingActivityChipModel.Active).icon) as OngoingActivityChipModel.ChipIcon.SingleColorIcon) @@ -157,6 +158,7 @@ class CastToOtherDeviceChipViewModelTest : SysuiTestCase() { ) assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active.IconOnly::class.java) + assertThat((latest as OngoingActivityChipModel.Active).isImportantForPrivacy).isTrue() val icon = (((latest as OngoingActivityChipModel.Active).icon) as OngoingActivityChipModel.ChipIcon.SingleColorIcon) @@ -177,6 +179,7 @@ class CastToOtherDeviceChipViewModelTest : SysuiTestCase() { MediaProjectionState.Projecting.EntireScreen(CAST_TO_OTHER_DEVICES_PACKAGE) assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active.Timer::class.java) + assertThat((latest as OngoingActivityChipModel.Active).isImportantForPrivacy).isTrue() val icon = (((latest as OngoingActivityChipModel.Active).icon) as OngoingActivityChipModel.ChipIcon.SingleColorIcon) @@ -215,6 +218,7 @@ class CastToOtherDeviceChipViewModelTest : SysuiTestCase() { ) assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active.IconOnly::class.java) + assertThat((latest as OngoingActivityChipModel.Active).isImportantForPrivacy).isTrue() val icon = (((latest as OngoingActivityChipModel.Active).icon) as OngoingActivityChipModel.ChipIcon.SingleColorIcon) @@ -245,6 +249,7 @@ class CastToOtherDeviceChipViewModelTest : SysuiTestCase() { // Only the projection info will show a timer assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active.Timer::class.java) + assertThat((latest as OngoingActivityChipModel.Active).isImportantForPrivacy).isTrue() val icon = (((latest as OngoingActivityChipModel.Active).icon) as OngoingActivityChipModel.ChipIcon.SingleColorIcon) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractorTest.kt index 7f8f5f43e775..9ce43a0792c2 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/SingleNotificationChipInteractorTest.kt @@ -30,7 +30,7 @@ import com.android.systemui.statusbar.StatusBarIconView import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips import com.android.systemui.statusbar.core.StatusBarConnectedDisplays import com.android.systemui.statusbar.notification.data.model.activeNotificationModel -import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel +import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentBuilder import com.android.systemui.testKosmos import com.android.systemui.util.time.fakeSystemClock import com.google.common.truth.Truth.assertThat @@ -420,7 +420,7 @@ class SingleNotificationChipInteractorTest : SysuiTestCase() { // WHEN the notif gets a new UID that starts as visible activityManagerRepository.fake.startingIsAppVisibleValue = true val newPromotedContentBuilder = - PromotedNotificationContentModel.Builder("notif").apply { + PromotedNotificationContentBuilder("notif").applyToShared { this.shortCriticalText = "Arrived" } val newPromotedContent = newPromotedContentBuilder.build() @@ -452,6 +452,6 @@ class SingleNotificationChipInteractorTest : SysuiTestCase() { companion object { private const val UID = 885 - private val PROMOTED_CONTENT = PromotedNotificationContentModel.Builder("notif1").build() + private val PROMOTED_CONTENT = PromotedNotificationContentBuilder("notif1").build() } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractorTest.kt index 0b9b297130a2..202d5cff95d7 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/domain/interactor/StatusBarNotificationChipsInteractorTest.kt @@ -36,7 +36,7 @@ import com.android.systemui.statusbar.notification.data.repository.ActiveNotific import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository import com.android.systemui.statusbar.notification.data.repository.addNotif import com.android.systemui.statusbar.notification.data.repository.removeNotif -import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel +import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentBuilder import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel import com.android.systemui.statusbar.notification.shared.CallType import com.android.systemui.testKosmos @@ -65,7 +65,7 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() { activeNotificationModel( key = "notif", statusBarChipIcon = mock<StatusBarIconView>(), - promotedContent = PromotedNotificationContentModel.Builder("notif").build(), + promotedContent = PromotedNotificationContentBuilder("notif").build(), ) ) ) @@ -96,7 +96,7 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() { activeNotificationModel( key = "notif", statusBarChipIcon = null, - promotedContent = PromotedNotificationContentModel.Builder("notif").build(), + promotedContent = PromotedNotificationContentBuilder("notif").build(), ) ) ) @@ -115,7 +115,7 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() { activeNotificationModel( key = "notif", statusBarChipIcon = null, - promotedContent = PromotedNotificationContentModel.Builder("notif").build(), + promotedContent = PromotedNotificationContentBuilder("notif").build(), ) ) ) @@ -135,7 +135,7 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() { activeNotificationModel( key = "notif", statusBarChipIcon = icon, - promotedContent = PromotedNotificationContentModel.Builder("notif").build(), + promotedContent = PromotedNotificationContentBuilder("notif").build(), ) ) ) @@ -158,12 +158,12 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() { activeNotificationModel( key = "notif1", statusBarChipIcon = firstIcon, - promotedContent = PromotedNotificationContentModel.Builder("notif1").build(), + promotedContent = PromotedNotificationContentBuilder("notif1").build(), ), activeNotificationModel( key = "notif2", statusBarChipIcon = secondIcon, - promotedContent = PromotedNotificationContentModel.Builder("notif2").build(), + promotedContent = PromotedNotificationContentBuilder("notif2").build(), ), activeNotificationModel( key = "notif3", @@ -195,7 +195,7 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() { key = "notif", uid = uid, statusBarChipIcon = mock<StatusBarIconView>(), - promotedContent = PromotedNotificationContentModel.Builder("notif1").build(), + promotedContent = PromotedNotificationContentBuilder("notif1").build(), ) ) ) @@ -223,14 +223,14 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() { key = "promotedNormal", statusBarChipIcon = mock(), promotedContent = - PromotedNotificationContentModel.Builder("promotedNormal").build(), + PromotedNotificationContentBuilder("promotedNormal").build(), callType = CallType.None, ), activeNotificationModel( key = "promotedCall", statusBarChipIcon = mock(), promotedContent = - PromotedNotificationContentModel.Builder("promotedCall").build(), + PromotedNotificationContentBuilder("promotedCall").build(), callType = CallType.Ongoing, ), ) @@ -256,7 +256,7 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() { activeNotificationModel( key = "notif", statusBarChipIcon = firstIcon, - promotedContent = PromotedNotificationContentModel.Builder("notif").build(), + promotedContent = PromotedNotificationContentBuilder("notif").build(), ) ) ) @@ -269,7 +269,7 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() { activeNotificationModel( key = "notif", statusBarChipIcon = secondIcon, - promotedContent = PromotedNotificationContentModel.Builder("notif").build(), + promotedContent = PromotedNotificationContentBuilder("notif").build(), ) ) ) @@ -282,7 +282,7 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() { activeNotificationModel( key = "notif", statusBarChipIcon = thirdIcon, - promotedContent = PromotedNotificationContentModel.Builder("notif").build(), + promotedContent = PromotedNotificationContentBuilder("notif").build(), ) ) ) @@ -302,7 +302,7 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() { activeNotificationModel( key = "notif", statusBarChipIcon = mock(), - promotedContent = PromotedNotificationContentModel.Builder("notif").build(), + promotedContent = PromotedNotificationContentBuilder("notif").build(), ) ) ) @@ -325,7 +325,7 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() { activeNotificationModel( key = "notif", statusBarChipIcon = mock(), - promotedContent = PromotedNotificationContentModel.Builder("notif").build(), + promotedContent = PromotedNotificationContentBuilder("notif").build(), ) ) ) @@ -348,7 +348,7 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() { activeNotificationModel( key = "notif1", statusBarChipIcon = firstIcon, - promotedContent = PromotedNotificationContentModel.Builder("notif1").build(), + promotedContent = PromotedNotificationContentBuilder("notif1").build(), ) setNotifs(listOf(notif1)) assertThat(latest!!.map { it.key }).containsExactly("notif1").inOrder() @@ -359,7 +359,7 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() { activeNotificationModel( key = "notif2", statusBarChipIcon = secondIcon, - promotedContent = PromotedNotificationContentModel.Builder("notif2").build(), + promotedContent = PromotedNotificationContentBuilder("notif2").build(), ) setNotifs(listOf(notif1, notif2)) @@ -380,7 +380,7 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() { // WHEN notif1 gets an update val notif1NewPromotedContent = - PromotedNotificationContentModel.Builder("notif1").apply { + PromotedNotificationContentBuilder("notif1").applyToShared { this.shortCriticalText = "Arrived" } setNotifs( @@ -426,8 +426,7 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() { key = notif1Info.key, uid = notif1Info.uid, statusBarChipIcon = notif1Info.icon, - promotedContent = - PromotedNotificationContentModel.Builder(notif1Info.key).build(), + promotedContent = PromotedNotificationContentBuilder(notif1Info.key).build(), ) ) activityManagerRepository.fake.setIsAppVisible(notif1Info.uid, isAppVisible = false) @@ -443,8 +442,7 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() { key = notif2Info.key, uid = notif2Info.uid, statusBarChipIcon = notif2Info.icon, - promotedContent = - PromotedNotificationContentModel.Builder(notif2Info.key).build(), + promotedContent = PromotedNotificationContentBuilder(notif2Info.key).build(), ) ) activityManagerRepository.fake.setIsAppVisible(notif2Info.uid, isAppVisible = false) @@ -482,16 +480,14 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() { key = notif1Info.key, uid = notif1Info.uid, statusBarChipIcon = notif1Info.icon, - promotedContent = - PromotedNotificationContentModel.Builder(notif1Info.key).build(), + promotedContent = PromotedNotificationContentBuilder(notif1Info.key).build(), ) val notif2 = activeNotificationModel( key = notif2Info.key, uid = notif2Info.uid, statusBarChipIcon = notif2Info.icon, - promotedContent = - PromotedNotificationContentModel.Builder(notif2Info.key).build(), + promotedContent = PromotedNotificationContentBuilder(notif2Info.key).build(), ) setNotifs(listOf(notif1, notif2)) assertThat(latest!!.map { it.key }).containsExactly("notif1", "notif2").inOrder() @@ -537,16 +533,14 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() { key = notif1Info.key, uid = notif1Info.uid, statusBarChipIcon = notif1Info.icon, - promotedContent = - PromotedNotificationContentModel.Builder(notif1Info.key).build(), + promotedContent = PromotedNotificationContentBuilder(notif1Info.key).build(), ) val notif2 = activeNotificationModel( key = notif2Info.key, uid = notif2Info.uid, statusBarChipIcon = notif2Info.icon, - promotedContent = - PromotedNotificationContentModel.Builder(notif2Info.key).build(), + promotedContent = PromotedNotificationContentBuilder(notif2Info.key).build(), ) setNotifs(listOf(notif1, notif2)) assertThat(latest!!.map { it.key }).containsExactly("notif1", "notif2").inOrder() @@ -567,8 +561,7 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() { key = notif3Info.key, uid = notif3Info.uid, statusBarChipIcon = notif3Info.icon, - promotedContent = - PromotedNotificationContentModel.Builder(notif3Info.key).build(), + promotedContent = PromotedNotificationContentBuilder(notif3Info.key).build(), ) setNotifs(listOf(notif1, notif2, notif3)) @@ -597,8 +590,7 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() { key = notif1Info.key, uid = notif1Info.uid, statusBarChipIcon = notif1Info.icon, - promotedContent = - PromotedNotificationContentModel.Builder(notif1Info.key).build(), + promotedContent = PromotedNotificationContentBuilder(notif1Info.key).build(), ) setNotifs(listOf(notif1)) @@ -609,8 +601,7 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() { key = notif2Info.key, uid = notif2Info.uid, statusBarChipIcon = notif2Info.icon, - promotedContent = - PromotedNotificationContentModel.Builder(notif2Info.key).build(), + promotedContent = PromotedNotificationContentBuilder(notif2Info.key).build(), ) setNotifs(listOf(notif1, notif2)) @@ -637,7 +628,7 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() { // WHEN notif2 gets an update val notif2NewPromotedContent = - PromotedNotificationContentModel.Builder("notif2").apply { + PromotedNotificationContentBuilder("notif2").applyToShared { this.shortCriticalText = "Arrived" } setNotifs( @@ -662,8 +653,7 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() { key = notif3Info.key, uid = notif3Info.uid, statusBarChipIcon = notif3Info.icon, - promotedContent = - PromotedNotificationContentModel.Builder(notif3Info.key).build(), + promotedContent = PromotedNotificationContentBuilder(notif3Info.key).build(), ) setNotifs(listOf(notif1, notif2, notif3)) @@ -710,8 +700,7 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() { activeNotificationModel( key = "notif|uid1", statusBarChipIcon = firstIcon, - promotedContent = - PromotedNotificationContentModel.Builder("notif|uid1").build(), + promotedContent = PromotedNotificationContentBuilder("notif|uid1").build(), ) ) ) @@ -725,8 +714,7 @@ class StatusBarNotificationChipsInteractorTest : SysuiTestCase() { activeNotificationModel( key = "notif|uid2", statusBarChipIcon = secondIcon, - promotedContent = - PromotedNotificationContentModel.Builder("notif|uid2").build(), + promotedContent = PromotedNotificationContentBuilder("notif|uid2").build(), ) ) ) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt index b5cfc7e9080d..eecdbbfd408b 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModelTest.kt @@ -43,6 +43,7 @@ import com.android.systemui.statusbar.notification.data.repository.ActiveNotific import com.android.systemui.statusbar.notification.data.repository.UnconfinedFakeHeadsUpRowRepository import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository import com.android.systemui.statusbar.notification.headsup.PinnedStatus +import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentBuilder import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel.When import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel @@ -101,7 +102,7 @@ class NotifChipsViewModelTest : SysuiTestCase() { activeNotificationModel( key = "notif", statusBarChipIcon = null, - promotedContent = PromotedNotificationContentModel.Builder("notif").build(), + promotedContent = PromotedNotificationContentBuilder("notif").build(), ) ) ) @@ -121,7 +122,7 @@ class NotifChipsViewModelTest : SysuiTestCase() { activeNotificationModel( key = "notif", statusBarChipIcon = null, - promotedContent = PromotedNotificationContentModel.Builder("notif").build(), + promotedContent = PromotedNotificationContentBuilder("notif").build(), ) ) ) @@ -142,7 +143,7 @@ class NotifChipsViewModelTest : SysuiTestCase() { key = "notif", appName = "Fake App Name", statusBarChipIcon = icon, - promotedContent = PromotedNotificationContentModel.Builder("notif").build(), + promotedContent = PromotedNotificationContentBuilder("notif").build(), ) ) ) @@ -172,7 +173,7 @@ class NotifChipsViewModelTest : SysuiTestCase() { key = notifKey, appName = "Fake App Name", statusBarChipIcon = null, - promotedContent = PromotedNotificationContentModel.Builder(notifKey).build(), + promotedContent = PromotedNotificationContentBuilder(notifKey).build(), ) ) ) @@ -195,7 +196,7 @@ class NotifChipsViewModelTest : SysuiTestCase() { val latest by collectLastValue(underTest.chips) val promotedContentBuilder = - PromotedNotificationContentModel.Builder("notif").apply { + PromotedNotificationContentBuilder("notif").applyToShared { this.colors = PromotedNotificationContentModel.Colors( backgroundColor = 56, @@ -229,12 +230,12 @@ class NotifChipsViewModelTest : SysuiTestCase() { activeNotificationModel( key = "notif1", statusBarChipIcon = firstIcon, - promotedContent = PromotedNotificationContentModel.Builder("notif1").build(), + promotedContent = PromotedNotificationContentBuilder("notif1").build(), ), activeNotificationModel( key = "notif2", statusBarChipIcon = secondIcon, - promotedContent = PromotedNotificationContentModel.Builder("notif2").build(), + promotedContent = PromotedNotificationContentBuilder("notif2").build(), ), activeNotificationModel( key = "notif3", @@ -264,13 +265,12 @@ class NotifChipsViewModelTest : SysuiTestCase() { activeNotificationModel( key = firstKey, statusBarChipIcon = null, - promotedContent = PromotedNotificationContentModel.Builder(firstKey).build(), + promotedContent = PromotedNotificationContentBuilder(firstKey).build(), ), activeNotificationModel( key = secondKey, statusBarChipIcon = null, - promotedContent = - PromotedNotificationContentModel.Builder(secondKey).build(), + promotedContent = PromotedNotificationContentBuilder(secondKey).build(), ), activeNotificationModel( key = thirdKey, @@ -294,7 +294,7 @@ class NotifChipsViewModelTest : SysuiTestCase() { fakeSystemClock.setCurrentTimeMillis(currentTime) val promotedContentBuilder = - PromotedNotificationContentModel.Builder("notif").apply { + PromotedNotificationContentBuilder("notif").applyToShared { this.shortCriticalText = "Arrived" this.time = When.Time(currentTime + 30.minutes.inWholeMilliseconds) } @@ -321,7 +321,7 @@ class NotifChipsViewModelTest : SysuiTestCase() { val latest by collectLastValue(underTest.chips) val promotedContentBuilder = - PromotedNotificationContentModel.Builder("notif").apply { this.time = null } + PromotedNotificationContentBuilder("notif").applyToShared { this.time = null } setNotifs( listOf( activeNotificationModel( @@ -346,7 +346,7 @@ class NotifChipsViewModelTest : SysuiTestCase() { fakeSystemClock.setCurrentTimeMillis(currentTime) val promotedContentBuilder = - PromotedNotificationContentModel.Builder("notif").apply { + PromotedNotificationContentBuilder("notif").applyToShared { this.wasPromotedAutomatically = true this.time = When.Time(currentTime + 30.minutes.inWholeMilliseconds) } @@ -374,7 +374,7 @@ class NotifChipsViewModelTest : SysuiTestCase() { fakeSystemClock.setCurrentTimeMillis(currentTime) val promotedContentBuilder = - PromotedNotificationContentModel.Builder("notif").apply { + PromotedNotificationContentBuilder("notif").applyToShared { this.wasPromotedAutomatically = false this.time = When.Time(currentTime + 30.minutes.inWholeMilliseconds) } @@ -402,7 +402,7 @@ class NotifChipsViewModelTest : SysuiTestCase() { fakeSystemClock.setCurrentTimeMillis(currentTime) val promotedContentBuilder = - PromotedNotificationContentModel.Builder("notif").apply { + PromotedNotificationContentBuilder("notif").applyToShared { this.time = When.Time(currentTime + 13.minutes.inWholeMilliseconds) } @@ -430,7 +430,7 @@ class NotifChipsViewModelTest : SysuiTestCase() { fakeSystemClock.setCurrentTimeMillis(currentTime) val promotedContentBuilder = - PromotedNotificationContentModel.Builder("notif").apply { + PromotedNotificationContentBuilder("notif").applyToShared { this.time = When.Time(currentTime + 500) } @@ -458,7 +458,7 @@ class NotifChipsViewModelTest : SysuiTestCase() { fakeSystemClock.setCurrentTimeMillis(currentTime) val promotedContentBuilder = - PromotedNotificationContentModel.Builder("notif").apply { + PromotedNotificationContentBuilder("notif").applyToShared { this.time = When.Time(currentTime) } @@ -486,7 +486,7 @@ class NotifChipsViewModelTest : SysuiTestCase() { fakeSystemClock.setCurrentTimeMillis(currentTime) val promotedContentBuilder = - PromotedNotificationContentModel.Builder("notif").apply { + PromotedNotificationContentBuilder("notif").applyToShared { this.time = When.Time(currentTime - 2.minutes.inWholeMilliseconds) } @@ -515,7 +515,7 @@ class NotifChipsViewModelTest : SysuiTestCase() { fakeSystemClock.setCurrentTimeMillis(currentTime) val promotedContentBuilder = - PromotedNotificationContentModel.Builder("notif").apply { + PromotedNotificationContentBuilder("notif").applyToShared { this.time = When.Time(currentTime + 3.minutes.inWholeMilliseconds) } @@ -555,7 +555,7 @@ class NotifChipsViewModelTest : SysuiTestCase() { val whenElapsed = currentElapsed - 1.minutes.inWholeMilliseconds val promotedContentBuilder = - PromotedNotificationContentModel.Builder("notif").apply { + PromotedNotificationContentBuilder("notif").applyToShared { this.time = When.Chronometer(elapsedRealtimeMillis = whenElapsed, isCountDown = false) } @@ -592,7 +592,7 @@ class NotifChipsViewModelTest : SysuiTestCase() { val whenElapsed = currentElapsed + 10.minutes.inWholeMilliseconds val promotedContentBuilder = - PromotedNotificationContentModel.Builder("notif").apply { + PromotedNotificationContentBuilder("notif").applyToShared { this.time = When.Chronometer(elapsedRealtimeMillis = whenElapsed, isCountDown = true) } @@ -623,7 +623,7 @@ class NotifChipsViewModelTest : SysuiTestCase() { fakeSystemClock.setCurrentTimeMillis(currentTime) val promotedContentBuilder = - PromotedNotificationContentModel.Builder("notif").apply { + PromotedNotificationContentBuilder("notif").applyToShared { this.time = When.Time(currentTime + 10.minutes.inWholeMilliseconds) } setNotifs( @@ -653,7 +653,7 @@ class NotifChipsViewModelTest : SysuiTestCase() { fakeSystemClock.setCurrentTimeMillis(currentTime) val promotedContentBuilder = - PromotedNotificationContentModel.Builder("notif").apply { + PromotedNotificationContentBuilder("notif").applyToShared { this.time = When.Time(currentTime + 10.minutes.inWholeMilliseconds) } setNotifs( @@ -690,11 +690,11 @@ class NotifChipsViewModelTest : SysuiTestCase() { fakeSystemClock.setCurrentTimeMillis(currentTime) val promotedContentBuilder = - PromotedNotificationContentModel.Builder("notif").apply { + PromotedNotificationContentBuilder("notif").applyToShared { this.time = When.Time(currentTime + 10.minutes.inWholeMilliseconds) } val otherPromotedContentBuilder = - PromotedNotificationContentModel.Builder("other notif").apply { + PromotedNotificationContentBuilder("other notif").applyToShared { this.time = When.Time(currentTime + 10.minutes.inWholeMilliseconds) } val icon = createStatusBarIconViewOrNull() @@ -738,7 +738,7 @@ class NotifChipsViewModelTest : SysuiTestCase() { fakeSystemClock.setCurrentTimeMillis(currentTime) val promotedContentBuilder = - PromotedNotificationContentModel.Builder("notif").apply { + PromotedNotificationContentBuilder("notif").applyToShared { this.time = When.Time(currentTime + 10.minutes.inWholeMilliseconds) } setNotifs( @@ -781,7 +781,7 @@ class NotifChipsViewModelTest : SysuiTestCase() { activeNotificationModel( key, statusBarChipIcon = createStatusBarIconViewOrNull(), - promotedContent = PromotedNotificationContentModel.Builder(key).build(), + promotedContent = PromotedNotificationContentBuilder(key).build(), ) ) ) @@ -809,7 +809,7 @@ class NotifChipsViewModelTest : SysuiTestCase() { activeNotificationModel( key, statusBarChipIcon = createStatusBarIconViewOrNull(), - promotedContent = PromotedNotificationContentModel.Builder(key).build(), + promotedContent = PromotedNotificationContentBuilder(key).build(), ) ) ) @@ -842,6 +842,7 @@ class NotifChipsViewModelTest : SysuiTestCase() { expectedContentDescriptionSubstrings: List<String> = emptyList(), ) { val active = latest as OngoingActivityChipModel.Active + assertThat(active.isImportantForPrivacy).isFalse() if (StatusBarConnectedDisplays.isEnabled) { assertThat(active.icon) .isInstanceOf( diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractorTest.kt index 538247e26a6b..f8c57655005e 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractorTest.kt @@ -87,7 +87,7 @@ class ScreenRecordChipInteractorTest : SysuiTestCase() { screenRecordRepo.screenRecordState.value = ScreenRecordModel.Recording mediaProjectionRepo.mediaProjectionState.value = MediaProjectionState.NotProjecting - assertThat(latest).isEqualTo(ScreenRecordChipModel.Recording(recordedTask = null)) + assertThat((latest as ScreenRecordChipModel.Recording).recordedTask).isNull() } @Test @@ -99,7 +99,7 @@ class ScreenRecordChipInteractorTest : SysuiTestCase() { mediaProjectionRepo.mediaProjectionState.value = MediaProjectionState.Projecting.EntireScreen("host.package") - assertThat(latest).isEqualTo(ScreenRecordChipModel.Recording(recordedTask = null)) + assertThat((latest as ScreenRecordChipModel.Recording).recordedTask).isNull() } @Test @@ -116,7 +116,48 @@ class ScreenRecordChipInteractorTest : SysuiTestCase() { task, ) - assertThat(latest).isEqualTo(ScreenRecordChipModel.Recording(recordedTask = task)) + assertThat((latest as ScreenRecordChipModel.Recording).recordedTask).isEqualTo(task) + } + + @Test + fun screenRecordState_projectionIsNotProjecting_hostPackageNull() = + testScope.runTest { + val latest by collectLastValue(underTest.screenRecordState) + + screenRecordRepo.screenRecordState.value = ScreenRecordModel.Recording + mediaProjectionRepo.mediaProjectionState.value = MediaProjectionState.NotProjecting + + assertThat((latest as ScreenRecordChipModel.Recording).hostPackage).isNull() + } + + @Test + fun screenRecordState_projectionIsEntireScreen_hostPackageMatches() = + testScope.runTest { + val latest by collectLastValue(underTest.screenRecordState) + + screenRecordRepo.screenRecordState.value = ScreenRecordModel.Recording + mediaProjectionRepo.mediaProjectionState.value = + MediaProjectionState.Projecting.EntireScreen(hostPackage = "host.package") + + assertThat((latest as ScreenRecordChipModel.Recording).hostPackage) + .isEqualTo("host.package") + } + + @Test + fun screenRecordState_projectionIsSingleTask_hostPackageMatches() = + testScope.runTest { + val latest by collectLastValue(underTest.screenRecordState) + + screenRecordRepo.screenRecordState.value = ScreenRecordModel.Recording + mediaProjectionRepo.mediaProjectionState.value = + MediaProjectionState.Projecting.SingleTask( + hostPackage = "host.package", + hostDeviceName = null, + task = createTask(taskId = 1), + ) + + assertThat((latest as ScreenRecordChipModel.Recording).hostPackage) + .isEqualTo("host.package") } @Test @@ -150,7 +191,7 @@ class ScreenRecordChipInteractorTest : SysuiTestCase() { advanceTimeBy(901) // THEN we automatically update to the recording state - assertThat(latest).isEqualTo(ScreenRecordChipModel.Recording(recordedTask = null)) + assertThat(latest).isInstanceOf(ScreenRecordChipModel.Recording::class.java) } @Test @@ -175,13 +216,14 @@ class ScreenRecordChipInteractorTest : SysuiTestCase() { ) // THEN we immediately switch to Recording, and we have the task - assertThat(latest).isEqualTo(ScreenRecordChipModel.Recording(recordedTask = task)) + assertThat(latest).isInstanceOf(ScreenRecordChipModel.Recording::class.java) + assertThat((latest as ScreenRecordChipModel.Recording).recordedTask).isEqualTo(task) // WHEN more than 900ms has elapsed advanceTimeBy(200) // THEN we still stay in the Recording state and we have the task - assertThat(latest).isEqualTo(ScreenRecordChipModel.Recording(recordedTask = task)) + assertThat((latest as ScreenRecordChipModel.Recording).recordedTask).isEqualTo(task) } @Test @@ -247,7 +289,7 @@ class ScreenRecordChipInteractorTest : SysuiTestCase() { // THEN we *do* auto-start 400ms later advanceTimeBy(401) - assertThat(latest).isEqualTo(ScreenRecordChipModel.Recording(recordedTask = null)) + assertThat(latest).isInstanceOf(ScreenRecordChipModel.Recording::class.java) } @Test diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModelTest.kt index 005af366a6c0..91942213ce75 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModelTest.kt @@ -110,6 +110,7 @@ class ScreenRecordChipViewModelTest : SysuiTestCase() { screenRecordRepo.screenRecordState.value = ScreenRecordModel.Starting(400) assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active.Countdown::class.java) + assertThat((latest as OngoingActivityChipModel.Active).isImportantForPrivacy).isTrue() assertThat((latest as OngoingActivityChipModel.Active).icon).isNull() assertThat((latest as OngoingActivityChipModel.Active).onClickListenerLegacy).isNull() } @@ -156,6 +157,7 @@ class ScreenRecordChipViewModelTest : SysuiTestCase() { screenRecordRepo.screenRecordState.value = ScreenRecordModel.Recording assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active.Timer::class.java) + assertThat((latest as OngoingActivityChipModel.Active).isImportantForPrivacy).isTrue() val icon = (((latest as OngoingActivityChipModel.Active).icon) as OngoingActivityChipModel.ChipIcon.SingleColorIcon) @@ -261,6 +263,7 @@ class ScreenRecordChipViewModelTest : SysuiTestCase() { mediaProjectionRepo.mediaProjectionState.value = MediaProjectionState.NotProjecting assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active::class.java) + assertThat((latest as OngoingActivityChipModel.Active).isImportantForPrivacy).isTrue() assertThat((latest as OngoingActivityChipModel.Active.Timer).startTimeMs) .isEqualTo(1234) @@ -275,6 +278,7 @@ class ScreenRecordChipViewModelTest : SysuiTestCase() { // THEN the start time is still the old start time assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active::class.java) + assertThat((latest as OngoingActivityChipModel.Active).isImportantForPrivacy).isTrue() assertThat((latest as OngoingActivityChipModel.Active.Timer).startTimeMs) .isEqualTo(1234) } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModelTest.kt index d6b10a89726e..99e378c78ee2 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModelTest.kt @@ -384,6 +384,7 @@ class ShareToAppChipViewModelTest : SysuiTestCase() { MediaProjectionState.Projecting.NoScreen(NORMAL_PACKAGE, hostDeviceName = null) assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active.IconOnly::class.java) + assertThat((latest as OngoingActivityChipModel.Active).isImportantForPrivacy).isTrue() val icon = (((latest as OngoingActivityChipModel.Active).icon) as OngoingActivityChipModel.ChipIcon.SingleColorIcon) @@ -407,6 +408,7 @@ class ShareToAppChipViewModelTest : SysuiTestCase() { ) assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active.Timer::class.java) + assertThat((latest as OngoingActivityChipModel.Active).isImportantForPrivacy).isTrue() val icon = (((latest as OngoingActivityChipModel.Active).icon) as OngoingActivityChipModel.ChipIcon.SingleColorIcon) @@ -424,6 +426,7 @@ class ShareToAppChipViewModelTest : SysuiTestCase() { MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE) assertThat(latest).isInstanceOf(OngoingActivityChipModel.Active.Timer::class.java) + assertThat((latest as OngoingActivityChipModel.Active).isImportantForPrivacy).isTrue() val icon = (((latest as OngoingActivityChipModel.Active).icon) as OngoingActivityChipModel.ChipIcon.SingleColorIcon) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt index 7135cf01394a..83b3c9cae3c1 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsWithNotifsViewModelTest.kt @@ -63,7 +63,7 @@ import com.android.systemui.statusbar.notification.data.repository.activeNotific import com.android.systemui.statusbar.notification.data.repository.addNotif import com.android.systemui.statusbar.notification.data.repository.addNotifs import com.android.systemui.statusbar.notification.data.repository.removeNotif -import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel +import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentBuilder import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel import com.android.systemui.statusbar.phone.SystemUIDialog import com.android.systemui.statusbar.phone.ongoingcall.DisableChipsModernization @@ -358,7 +358,7 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() { addOngoingCallState(key = "call") val promotedContentBuilder = - PromotedNotificationContentModel.Builder("notif").apply { + PromotedNotificationContentBuilder("notif").applyToShared { this.shortCriticalText = "Some text here" } activeNotificationListRepository.addNotif( @@ -741,7 +741,7 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() { activeNotificationModel( key = "notif", statusBarChipIcon = icon, - promotedContent = PromotedNotificationContentModel.Builder("notif").build(), + promotedContent = PromotedNotificationContentBuilder("notif").build(), ) ) ) @@ -765,7 +765,7 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() { activeNotificationModel( key = "notif", statusBarChipIcon = icon, - promotedContent = PromotedNotificationContentModel.Builder("notif").build(), + promotedContent = PromotedNotificationContentBuilder("notif").build(), ) ) ) @@ -791,14 +791,12 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() { activeNotificationModel( key = "firstNotif", statusBarChipIcon = firstIcon, - promotedContent = - PromotedNotificationContentModel.Builder("firstNotif").build(), + promotedContent = PromotedNotificationContentBuilder("firstNotif").build(), ), activeNotificationModel( key = "secondNotif", statusBarChipIcon = secondIcon, - promotedContent = - PromotedNotificationContentModel.Builder("secondNotif").build(), + promotedContent = PromotedNotificationContentBuilder("secondNotif").build(), ), ) ) @@ -822,14 +820,12 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() { activeNotificationModel( key = "firstNotif", statusBarChipIcon = firstIcon, - promotedContent = - PromotedNotificationContentModel.Builder("firstNotif").build(), + promotedContent = PromotedNotificationContentBuilder("firstNotif").build(), ), activeNotificationModel( key = "secondNotif", statusBarChipIcon = secondIcon, - promotedContent = - PromotedNotificationContentModel.Builder("secondNotif").build(), + promotedContent = PromotedNotificationContentBuilder("secondNotif").build(), ), ) ) @@ -857,20 +853,17 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() { activeNotificationModel( key = "firstNotif", statusBarChipIcon = firstIcon, - promotedContent = - PromotedNotificationContentModel.Builder("firstNotif").build(), + promotedContent = PromotedNotificationContentBuilder("firstNotif").build(), ), activeNotificationModel( key = "secondNotif", statusBarChipIcon = secondIcon, - promotedContent = - PromotedNotificationContentModel.Builder("secondNotif").build(), + promotedContent = PromotedNotificationContentBuilder("secondNotif").build(), ), activeNotificationModel( key = "thirdNotif", statusBarChipIcon = thirdIcon, - promotedContent = - PromotedNotificationContentModel.Builder("thirdNotif").build(), + promotedContent = PromotedNotificationContentBuilder("thirdNotif").build(), ), ) ) @@ -896,26 +889,22 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() { activeNotificationModel( key = "firstNotif", statusBarChipIcon = firstIcon, - promotedContent = - PromotedNotificationContentModel.Builder("firstNotif").build(), + promotedContent = PromotedNotificationContentBuilder("firstNotif").build(), ), activeNotificationModel( key = "secondNotif", statusBarChipIcon = secondIcon, - promotedContent = - PromotedNotificationContentModel.Builder("secondNotif").build(), + promotedContent = PromotedNotificationContentBuilder("secondNotif").build(), ), activeNotificationModel( key = "thirdNotif", statusBarChipIcon = thirdIcon, - promotedContent = - PromotedNotificationContentModel.Builder("thirdNotif").build(), + promotedContent = PromotedNotificationContentBuilder("thirdNotif").build(), ), activeNotificationModel( key = "fourthNotif", statusBarChipIcon = fourthIcon, - promotedContent = - PromotedNotificationContentModel.Builder("fourthNotif").build(), + promotedContent = PromotedNotificationContentBuilder("fourthNotif").build(), ), ) ) @@ -941,20 +930,17 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() { activeNotificationModel( key = "firstNotif", statusBarChipIcon = createStatusBarIconViewOrNull(), - promotedContent = - PromotedNotificationContentModel.Builder("firstNotif").build(), + promotedContent = PromotedNotificationContentBuilder("firstNotif").build(), ), activeNotificationModel( key = "secondNotif", statusBarChipIcon = createStatusBarIconViewOrNull(), - promotedContent = - PromotedNotificationContentModel.Builder("secondNotif").build(), + promotedContent = PromotedNotificationContentBuilder("secondNotif").build(), ), activeNotificationModel( key = "thirdNotif", statusBarChipIcon = createStatusBarIconViewOrNull(), - promotedContent = - PromotedNotificationContentModel.Builder("thirdNotif").build(), + promotedContent = PromotedNotificationContentBuilder("thirdNotif").build(), ), ) ) @@ -973,26 +959,22 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() { activeNotificationModel( key = "firstNotif", statusBarChipIcon = createStatusBarIconViewOrNull(), - promotedContent = - PromotedNotificationContentModel.Builder("firstNotif").build(), + promotedContent = PromotedNotificationContentBuilder("firstNotif").build(), ), activeNotificationModel( key = "secondNotif", statusBarChipIcon = createStatusBarIconViewOrNull(), - promotedContent = - PromotedNotificationContentModel.Builder("secondNotif").build(), + promotedContent = PromotedNotificationContentBuilder("secondNotif").build(), ), activeNotificationModel( key = "thirdNotif", statusBarChipIcon = createStatusBarIconViewOrNull(), - promotedContent = - PromotedNotificationContentModel.Builder("thirdNotif").build(), + promotedContent = PromotedNotificationContentBuilder("thirdNotif").build(), ), activeNotificationModel( key = "fourthNotif", statusBarChipIcon = createStatusBarIconViewOrNull(), - promotedContent = - PromotedNotificationContentModel.Builder("fourthNotif").build(), + promotedContent = PromotedNotificationContentBuilder("fourthNotif").build(), ), ) ) @@ -1016,14 +998,12 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() { activeNotificationModel( key = "firstNotif", statusBarChipIcon = firstIcon, - promotedContent = - PromotedNotificationContentModel.Builder("firstNotif").build(), + promotedContent = PromotedNotificationContentBuilder("firstNotif").build(), ), activeNotificationModel( key = "secondNotif", statusBarChipIcon = createStatusBarIconViewOrNull(), - promotedContent = - PromotedNotificationContentModel.Builder("secondNotif").build(), + promotedContent = PromotedNotificationContentBuilder("secondNotif").build(), ), ) ) @@ -1050,20 +1030,17 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() { activeNotificationModel( key = "firstNotif", statusBarChipIcon = firstIcon, - promotedContent = - PromotedNotificationContentModel.Builder("firstNotif").build(), + promotedContent = PromotedNotificationContentBuilder("firstNotif").build(), ), activeNotificationModel( key = "secondNotif", statusBarChipIcon = secondIcon, - promotedContent = - PromotedNotificationContentModel.Builder("secondNotif").build(), + promotedContent = PromotedNotificationContentBuilder("secondNotif").build(), ), activeNotificationModel( key = "thirdNotif", statusBarChipIcon = thirdIcon, - promotedContent = - PromotedNotificationContentModel.Builder("thirdNotif").build(), + promotedContent = PromotedNotificationContentBuilder("thirdNotif").build(), ), ) ) @@ -1092,7 +1069,7 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() { activeNotificationModel( key = "notif", statusBarChipIcon = createStatusBarIconViewOrNull(), - promotedContent = PromotedNotificationContentModel.Builder("notif").build(), + promotedContent = PromotedNotificationContentBuilder("notif").build(), ) ) @@ -1114,14 +1091,14 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() { activeNotificationModel( key = "notif1", statusBarChipIcon = createStatusBarIconViewOrNull(), - promotedContent = PromotedNotificationContentModel.Builder("notif1").build(), + promotedContent = PromotedNotificationContentBuilder("notif1").build(), ) ) activeNotificationListRepository.addNotif( activeNotificationModel( key = "notif2", statusBarChipIcon = createStatusBarIconViewOrNull(), - promotedContent = PromotedNotificationContentModel.Builder("notif2").build(), + promotedContent = PromotedNotificationContentBuilder("notif2").build(), ) ) @@ -1143,14 +1120,14 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() { activeNotificationModel( key = "notif1", statusBarChipIcon = createStatusBarIconViewOrNull(), - promotedContent = PromotedNotificationContentModel.Builder("notif1").build(), + promotedContent = PromotedNotificationContentBuilder("notif1").build(), ) ) activeNotificationListRepository.addNotif( activeNotificationModel( key = "notif2", statusBarChipIcon = createStatusBarIconViewOrNull(), - promotedContent = PromotedNotificationContentModel.Builder("notif2").build(), + promotedContent = PromotedNotificationContentBuilder("notif2").build(), ) ) @@ -1159,6 +1136,11 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() { .inOrder() } + // The ranking between different chips should stay consistent between + // OngoingActivityChipsViewModel and PromotedNotificationsInteractor. + // Make sure to also change + // PromotedNotificationsInteractorTest#orderedChipNotificationKeys_rankingIsCorrect + // if you change this test. @EnableChipsModernization @Test fun chips_screenRecordAndCallAndPromotedNotifs_secondNotifInOverflow() = @@ -1173,7 +1155,7 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() { activeNotificationModel( key = "notif", statusBarChipIcon = notifIcon, - promotedContent = PromotedNotificationContentModel.Builder("notif").build(), + promotedContent = PromotedNotificationContentBuilder("notif").build(), ) ) addOngoingCallState(key = callNotificationKey) @@ -1184,7 +1166,7 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() { activeNotificationModel( key = "notif2", statusBarChipIcon = notifIcon2, - promotedContent = PromotedNotificationContentModel.Builder("notif2").build(), + promotedContent = PromotedNotificationContentBuilder("notif2").build(), ) ) @@ -1209,7 +1191,7 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() { activeNotificationModel( key = "notif", statusBarChipIcon = notifIcon, - promotedContent = PromotedNotificationContentModel.Builder("notif").build(), + promotedContent = PromotedNotificationContentBuilder("notif").build(), ) ) ) @@ -1260,7 +1242,7 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() { activeNotificationModel( key = "notif", statusBarChipIcon = notifIcon, - promotedContent = PromotedNotificationContentModel.Builder("notif").build(), + promotedContent = PromotedNotificationContentBuilder("notif").build(), ) ) @@ -1299,7 +1281,7 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() { activeNotificationModel( key = "notif", statusBarChipIcon = notifIcon, - promotedContent = PromotedNotificationContentModel.Builder("notif").build(), + promotedContent = PromotedNotificationContentBuilder("notif").build(), ) ) // And everything else hidden @@ -1377,7 +1359,7 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() { activeNotificationModel( key = "notif1", statusBarChipIcon = notif1Icon, - promotedContent = PromotedNotificationContentModel.Builder("notif1").build(), + promotedContent = PromotedNotificationContentBuilder("notif1").build(), ) ) ) @@ -1431,7 +1413,7 @@ class OngoingActivityChipsWithNotifsViewModelTest : SysuiTestCase() { activeNotificationModel( key = "notif2", statusBarChipIcon = notif2Icon, - promotedContent = PromotedNotificationContentModel.Builder("notif2").build(), + promotedContent = PromotedNotificationContentBuilder("notif2").build(), ) ) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/ColorizedFgsCoordinatorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/ColorizedFgsCoordinatorTest.kt index ba2d40ba6a0d..3d7ced747450 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/ColorizedFgsCoordinatorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/collection/coordinator/ColorizedFgsCoordinatorTest.kt @@ -16,6 +16,7 @@ package com.android.systemui.statusbar.notification.collection.coordinator import android.app.Notification +import android.app.Notification.FLAG_FOREGROUND_SERVICE import android.app.NotificationManager import android.app.PendingIntent import android.app.Person @@ -31,6 +32,10 @@ import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.kosmos.collectLastValue import com.android.systemui.kosmos.runTest import com.android.systemui.kosmos.useUnconfinedTestDispatcher +import com.android.systemui.mediaprojection.data.model.MediaProjectionState +import com.android.systemui.mediaprojection.data.repository.fakeMediaProjectionRepository +import com.android.systemui.screenrecord.data.model.ScreenRecordModel +import com.android.systemui.screenrecord.data.repository.screenRecordRepository import com.android.systemui.statusbar.chips.notification.domain.interactor.statusBarNotificationChipsInteractor import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips import com.android.systemui.statusbar.core.StatusBarRootModernization @@ -169,6 +174,87 @@ class ColorizedFgsCoordinatorTest : SysuiTestCase() { @Test @EnableFlags(PromotedNotificationUi.FLAG_NAME) + fun testIncludeScreenRecordNotifInSection_importanceDefault() = + kosmos.runTest { + // GIVEN a screen record event + screen record notif that has a status bar chip + screenRecordRepository.screenRecordState.value = ScreenRecordModel.Recording + fakeMediaProjectionRepository.mediaProjectionState.value = + MediaProjectionState.Projecting.EntireScreen(hostPackage = "test_pkg") + val screenRecordEntry = + buildNotificationEntry(tag = "screenRecord", promoted = false) { + setImportance(NotificationManager.IMPORTANCE_DEFAULT) + setFlag(context, FLAG_FOREGROUND_SERVICE, true) + } + + renderNotificationListInteractor.setRenderedList(listOf(screenRecordEntry)) + + val orderedChipNotificationKeys by + collectLastValue(promotedNotificationsInteractor.orderedChipNotificationKeys) + + assertThat(orderedChipNotificationKeys) + .containsExactly("0|test_pkg|0|screenRecord|0") + .inOrder() + + // THEN the entry is in the fgs section + assertTrue(sectioner.isInSection(screenRecordEntry)) + } + + @Test + @EnableFlags(PromotedNotificationUi.FLAG_NAME) + fun testDiscludeScreenRecordNotifInSection_importanceMin() = + kosmos.runTest { + // GIVEN a screen record event + screen record notif that has a status bar chip + screenRecordRepository.screenRecordState.value = ScreenRecordModel.Recording + fakeMediaProjectionRepository.mediaProjectionState.value = + MediaProjectionState.Projecting.EntireScreen(hostPackage = "test_pkg") + val screenRecordEntry = + buildNotificationEntry(tag = "screenRecord", promoted = false) { + setImportance(NotificationManager.IMPORTANCE_MIN) + setFlag(context, FLAG_FOREGROUND_SERVICE, true) + } + + renderNotificationListInteractor.setRenderedList(listOf(screenRecordEntry)) + + val orderedChipNotificationKeys by + collectLastValue(promotedNotificationsInteractor.orderedChipNotificationKeys) + + assertThat(orderedChipNotificationKeys) + .containsExactly("0|test_pkg|0|screenRecord|0") + .inOrder() + + // THEN the entry is NOT in the fgs section + assertFalse(sectioner.isInSection(screenRecordEntry)) + } + + @Test + @DisableFlags(PromotedNotificationUi.FLAG_NAME) + fun testDiscludeScreenRecordNotifInSection_flagDisabled() = + kosmos.runTest { + // GIVEN a screen record event + screen record notif that has a status bar chip + screenRecordRepository.screenRecordState.value = ScreenRecordModel.Recording + fakeMediaProjectionRepository.mediaProjectionState.value = + MediaProjectionState.Projecting.EntireScreen(hostPackage = "test_pkg") + val screenRecordEntry = + buildNotificationEntry(tag = "screenRecord", promoted = false) { + setImportance(NotificationManager.IMPORTANCE_DEFAULT) + setFlag(context, FLAG_FOREGROUND_SERVICE, true) + } + + renderNotificationListInteractor.setRenderedList(listOf(screenRecordEntry)) + + val orderedChipNotificationKeys by + collectLastValue(promotedNotificationsInteractor.orderedChipNotificationKeys) + + assertThat(orderedChipNotificationKeys) + .containsExactly("0|test_pkg|0|screenRecord|0") + .inOrder() + + // THEN the entry is NOT in the fgs section + assertFalse(sectioner.isInSection(screenRecordEntry)) + } + + @Test + @EnableFlags(PromotedNotificationUi.FLAG_NAME) fun promoterSelectsPromotedOngoing_flagEnabled() { val promoter: NotifPromoter = withArgCaptor { verify(notifPipeline).addPromoter(capture()) } @@ -234,8 +320,4 @@ class ColorizedFgsCoordinatorTest : SysuiTestCase() { val person = Person.Builder().setName("person").build() return Notification.CallStyle.forOngoingCall(person, pendingIntent) } - - companion object { - private const val NOTIF_USER_ID = 0 - } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractorTest.kt index d3befa921e9e..29bb29f25797 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/ActiveNotificationsInteractorTest.kt @@ -29,7 +29,7 @@ import com.android.systemui.statusbar.notification.data.model.activeNotification import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository import com.android.systemui.statusbar.notification.data.repository.setActiveNotifs -import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel +import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentBuilder import com.android.systemui.statusbar.notification.shared.CallType import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat @@ -170,7 +170,7 @@ class ActiveNotificationsInteractorTest : SysuiTestCase() { val promoted1 = activeNotificationModel( key = "notif1", - promotedContent = PromotedNotificationContentModel.Builder("notif1").build(), + promotedContent = PromotedNotificationContentBuilder("notif1").build(), ) val notPromoted2 = activeNotificationModel(key = "notif2", promotedContent = null) @@ -208,14 +208,14 @@ class ActiveNotificationsInteractorTest : SysuiTestCase() { val promoted1 = activeNotificationModel( key = "notif1", - promotedContent = PromotedNotificationContentModel.Builder("notif1").build(), + promotedContent = PromotedNotificationContentBuilder("notif1").build(), ) val notPromoted2 = activeNotificationModel(key = "notif2", promotedContent = null) val notPromoted3 = activeNotificationModel(key = "notif3", promotedContent = null) val promoted4 = activeNotificationModel( key = "notif4", - promotedContent = PromotedNotificationContentModel.Builder("notif4").build(), + promotedContent = PromotedNotificationContentBuilder("notif4").build(), ) activeNotificationListRepository.activeNotifications.value = diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationsListInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationsListInteractorTest.kt index 35b19c19d5ce..5c749e6e35d6 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationsListInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationsListInteractorTest.kt @@ -28,7 +28,8 @@ import com.android.systemui.statusbar.notification.collection.GroupEntry import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUi -import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel +import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentBuilder +import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModels import com.android.systemui.statusbar.notification.shared.byKey import com.android.systemui.testKosmos import com.android.systemui.util.mockito.mock @@ -130,7 +131,7 @@ class RenderNotificationsListInteractorTest : SysuiTestCase() { val promoted2 = mockNotificationEntry( "key2", - promotedContent = PromotedNotificationContentModel.Builder("key2").build(), + promotedContent = PromotedNotificationContentBuilder("key2").build(), ) underTest.setRenderedList(listOf(notPromoted1, promoted2)) @@ -149,7 +150,7 @@ class RenderNotificationsListInteractorTest : SysuiTestCase() { private fun mockNotificationEntry( key: String, rank: Int = 0, - promotedContent: PromotedNotificationContentModel? = null, + promotedContent: PromotedNotificationContentModels? = null, ): NotificationEntry { val nBuilder = Notification.Builder(context, "a") val notification = nBuilder.build() @@ -165,7 +166,7 @@ class RenderNotificationsListInteractorTest : SysuiTestCase() { whenever(this.representativeEntry).thenReturn(this) whenever(this.ranking).thenReturn(RankingBuilder().setRank(rank).build()) whenever(this.sbn).thenReturn(mockSbn) - whenever(this.promotedNotificationContentModel).thenReturn(promotedContent) + whenever(this.promotedNotificationContentModels).thenReturn(promotedContent) } } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorTest.kt index ee698ae20adb..41120a16e4ea 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/icon/domain/interactor/NotificationIconsInteractorTest.kt @@ -33,8 +33,10 @@ import com.android.systemui.statusbar.notification.data.repository.notifications import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor import com.android.systemui.statusbar.notification.domain.interactor.headsUpNotificationIconInteractor import com.android.systemui.statusbar.notification.promoted.domain.interactor.aodPromotedNotificationInteractor +import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentBuilder import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel.Style.Base +import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModels import com.android.systemui.statusbar.notification.shared.byIsAmbient import com.android.systemui.statusbar.notification.shared.byIsLastMessageFromReply import com.android.systemui.statusbar.notification.shared.byIsPromoted @@ -354,6 +356,6 @@ private val testIcons = private fun promotedContent( key: String, style: PromotedNotificationContentModel.Style, -): PromotedNotificationContentModel { - return PromotedNotificationContentModel.Builder(key).apply { this.style = style }.build() +): PromotedNotificationContentModels { + return PromotedNotificationContentBuilder(key).applyToShared { this.style = style }.build() } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorImplTest.kt index 4c099b305332..cc016b9768b7 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorImplTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorImplTest.kt @@ -33,18 +33,21 @@ import android.platform.test.annotations.EnableFlags import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase +import com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_NONE +import com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_PUBLIC import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder import com.android.systemui.statusbar.notification.promoted.AutomaticPromotionCoordinator.Companion.EXTRA_WAS_AUTOMATICALLY_PROMOTED -import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel.Style import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel.When +import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModels import com.android.systemui.statusbar.notification.row.RowImageInflater import com.android.systemui.testKosmos import com.android.systemui.util.time.fakeSystemClock import com.android.systemui.util.time.systemClock import com.google.common.truth.Truth.assertThat +import kotlin.test.assertNotNull import kotlin.time.Duration import kotlin.time.Duration.Companion.minutes import org.junit.Test @@ -112,12 +115,43 @@ class PromotedNotificationContentExtractorImplTest : SysuiTestCase() { setContentText(TEST_CONTENT_TEXT) } - val content = extractContent(entry) + val content = requireContent(entry) - assertThat(content).isNotNull() - assertThat(content?.subText).isEqualTo(TEST_SUB_TEXT) - assertThat(content?.title).isEqualTo(TEST_CONTENT_TITLE) - assertThat(content?.text).isEqualTo(TEST_CONTENT_TEXT) + content.privateVersion.apply { + assertThat(subText).isEqualTo(TEST_SUB_TEXT) + assertThat(title).isEqualTo(TEST_CONTENT_TITLE) + assertThat(text).isEqualTo(TEST_CONTENT_TEXT) + } + + content.publicVersion.apply { + assertThat(subText).isNull() + assertThat(title).isNull() + assertThat(text).isNull() + } + } + + @Test + @EnableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME) + fun extractsContent_commonFields_noRedaction() { + val entry = createEntry { + setSubText(TEST_SUB_TEXT) + setContentTitle(TEST_CONTENT_TITLE) + setContentText(TEST_CONTENT_TEXT) + } + + val content = requireContent(entry, redactionType = REDACTION_TYPE_NONE) + + content.privateVersion.apply { + assertThat(subText).isEqualTo(TEST_SUB_TEXT) + assertThat(title).isEqualTo(TEST_CONTENT_TITLE) + assertThat(text).isEqualTo(TEST_CONTENT_TEXT) + } + + content.publicVersion.apply { + assertThat(subText).isEqualTo(TEST_SUB_TEXT) + assertThat(title).isEqualTo(TEST_CONTENT_TITLE) + assertThat(text).isEqualTo(TEST_CONTENT_TEXT) + } } @Test @@ -125,9 +159,9 @@ class PromotedNotificationContentExtractorImplTest : SysuiTestCase() { fun extractContent_wasPromotedAutomatically_false() { val entry = createEntry { extras.putBoolean(EXTRA_WAS_AUTOMATICALLY_PROMOTED, false) } - val content = extractContent(entry) + val content = requireContent(entry).privateVersion - assertThat(content!!.wasPromotedAutomatically).isFalse() + assertThat(content.wasPromotedAutomatically).isFalse() } @Test @@ -135,9 +169,9 @@ class PromotedNotificationContentExtractorImplTest : SysuiTestCase() { fun extractContent_wasPromotedAutomatically_true() { val entry = createEntry { extras.putBoolean(EXTRA_WAS_AUTOMATICALLY_PROMOTED, true) } - val content = extractContent(entry) + val content = requireContent(entry).privateVersion - assertThat(content!!.wasPromotedAutomatically).isTrue() + assertThat(content.wasPromotedAutomatically).isTrue() } @Test @@ -146,10 +180,9 @@ class PromotedNotificationContentExtractorImplTest : SysuiTestCase() { fun extractContent_apiFlagOff_shortCriticalTextNotExtracted() { val entry = createEntry { setShortCriticalText(TEST_SHORT_CRITICAL_TEXT) } - val content = extractContent(entry) + val content = requireContent(entry).privateVersion - assertThat(content).isNotNull() - assertThat(content?.text).isNull() + assertThat(content.text).isNull() } @Test @@ -161,10 +194,9 @@ class PromotedNotificationContentExtractorImplTest : SysuiTestCase() { fun extractContent_apiFlagOn_shortCriticalTextExtracted() { val entry = createEntry { setShortCriticalText(TEST_SHORT_CRITICAL_TEXT) } - val content = extractContent(entry) + val content = requireContent(entry).privateVersion - assertThat(content).isNotNull() - assertThat(content?.shortCriticalText).isEqualTo(TEST_SHORT_CRITICAL_TEXT) + assertThat(content.shortCriticalText).isEqualTo(TEST_SHORT_CRITICAL_TEXT) } @Test @@ -176,10 +208,9 @@ class PromotedNotificationContentExtractorImplTest : SysuiTestCase() { fun extractContent_noShortCriticalTextSet_textIsNull() { val entry = createEntry { setShortCriticalText(null) } - val content = extractContent(entry) + val content = requireContent(entry).privateVersion - assertThat(content).isNotNull() - assertThat(content?.shortCriticalText).isNull() + assertThat(content.shortCriticalText).isNull() } @Test @@ -190,11 +221,22 @@ class PromotedNotificationContentExtractorImplTest : SysuiTestCase() { @Test @EnableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME) + fun extractTime_basicTimeZero() { + assertExtractedTime( + hasTime = true, + hasChronometer = false, + provided = ProvidedTime.Value(0L), + expected = ExpectedTime.Time, + ) + } + + @Test + @EnableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME) fun extractTime_basicTimeNow() { assertExtractedTime( hasTime = true, hasChronometer = false, - whenOffset = Duration.ZERO, + provided = ProvidedTime.Offset(Duration.ZERO), expected = ExpectedTime.Time, ) } @@ -205,7 +247,7 @@ class PromotedNotificationContentExtractorImplTest : SysuiTestCase() { assertExtractedTime( hasTime = true, hasChronometer = false, - whenOffset = (-5).minutes, + provided = ProvidedTime.Offset((-5).minutes), expected = ExpectedTime.Time, ) } @@ -216,19 +258,31 @@ class PromotedNotificationContentExtractorImplTest : SysuiTestCase() { assertExtractedTime( hasTime = true, hasChronometer = false, - whenOffset = 5.minutes, + provided = ProvidedTime.Offset(5.minutes), expected = ExpectedTime.Time, ) } @Test @EnableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME) + fun extractTime_countUpZero() { + assertExtractedTime( + hasTime = false, + hasChronometer = true, + isCountDown = false, + provided = ProvidedTime.Value(0L), + expected = ExpectedTime.CountUp, + ) + } + + @Test + @EnableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME) fun extractTime_countUpNow() { assertExtractedTime( hasTime = false, hasChronometer = true, isCountDown = false, - whenOffset = Duration.ZERO, + provided = ProvidedTime.Offset(Duration.ZERO), expected = ExpectedTime.CountUp, ) } @@ -240,7 +294,7 @@ class PromotedNotificationContentExtractorImplTest : SysuiTestCase() { hasTime = false, hasChronometer = true, isCountDown = false, - whenOffset = (-5).minutes, + provided = ProvidedTime.Offset((-5).minutes), expected = ExpectedTime.CountUp, ) } @@ -252,19 +306,31 @@ class PromotedNotificationContentExtractorImplTest : SysuiTestCase() { hasTime = false, hasChronometer = true, isCountDown = false, - whenOffset = 5.minutes, + provided = ProvidedTime.Offset(5.minutes), expected = ExpectedTime.CountUp, ) } @Test @EnableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME) + fun extractTime_countDownZero() { + assertExtractedTime( + hasTime = false, + hasChronometer = true, + isCountDown = true, + provided = ProvidedTime.Value(0L), + expected = ExpectedTime.CountDown, + ) + } + + @Test + @EnableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME) fun extractTime_countDownNow() { assertExtractedTime( hasTime = false, hasChronometer = true, isCountDown = true, - whenOffset = Duration.ZERO, + provided = ProvidedTime.Offset(Duration.ZERO), expected = ExpectedTime.CountDown, ) } @@ -276,7 +342,7 @@ class PromotedNotificationContentExtractorImplTest : SysuiTestCase() { hasTime = false, hasChronometer = true, isCountDown = true, - whenOffset = (-5).minutes, + provided = ProvidedTime.Offset((-5).minutes), expected = ExpectedTime.CountDown, ) } @@ -288,7 +354,7 @@ class PromotedNotificationContentExtractorImplTest : SysuiTestCase() { hasTime = false, hasChronometer = true, isCountDown = true, - whenOffset = 5.minutes, + provided = ProvidedTime.Offset(5.minutes), expected = ExpectedTime.CountDown, ) } @@ -299,6 +365,12 @@ class PromotedNotificationContentExtractorImplTest : SysuiTestCase() { assertExtractedTime(hasTime = true, hasChronometer = true, expected = ExpectedTime.CountUp) } + private sealed class ProvidedTime { + data class Value(val value: Long) : ProvidedTime() + + data class Offset(val offset: Duration = Duration.ZERO) : ProvidedTime() + } + private enum class ExpectedTime { Null, Time, @@ -310,7 +382,7 @@ class PromotedNotificationContentExtractorImplTest : SysuiTestCase() { hasTime: Boolean = false, hasChronometer: Boolean = false, isCountDown: Boolean = false, - whenOffset: Duration = Duration.ZERO, + provided: ProvidedTime = ProvidedTime.Offset(), expected: ExpectedTime, ) { // Set the two timebases to different (arbitrary) numbers, so we can verify whether the @@ -318,48 +390,60 @@ class PromotedNotificationContentExtractorImplTest : SysuiTestCase() { systemClock.setCurrentTimeMillis(1_739_570_992_579L) systemClock.setElapsedRealtime(1_380_967_080L) - val whenCurrentTime = systemClock.currentTimeMillis() + whenOffset.inWholeMilliseconds - val whenElapsedRealtime = systemClock.elapsedRealtime() + whenOffset.inWholeMilliseconds + val providedCurrentTime = + when (provided) { + is ProvidedTime.Value -> provided.value + is ProvidedTime.Offset -> + systemClock.currentTimeMillis() + provided.offset.inWholeMilliseconds + } + + val expectedCurrentTime = + when (providedCurrentTime) { + 0L -> systemClock.currentTimeMillis() + else -> providedCurrentTime + } val entry = createEntry { setShowWhen(hasTime) setUsesChronometer(hasChronometer) setChronometerCountDown(isCountDown) - setWhen(whenCurrentTime) + setWhen(providedCurrentTime) } - val content = extractContent(entry) - - assertThat(content).isNotNull() + val content = requireContent(entry).privateVersion when (expected) { - ExpectedTime.Null -> assertThat(content?.time).isNull() + ExpectedTime.Null -> assertThat(content.time).isNull() ExpectedTime.Time -> { - val actual = content?.time as? When.Time - assertThat(actual).isNotNull() - assertThat(actual?.currentTimeMillis).isEqualTo(whenCurrentTime) + val actual = assertNotNull(content.time as? When.Time) + assertThat(actual.currentTimeMillis).isEqualTo(expectedCurrentTime) } ExpectedTime.CountDown, ExpectedTime.CountUp -> { - val actual = content?.time as? When.Chronometer - assertThat(actual).isNotNull() - assertThat(actual?.elapsedRealtimeMillis).isEqualTo(whenElapsedRealtime) - assertThat(actual?.isCountDown).isEqualTo(expected == ExpectedTime.CountDown) + val expectedElapsedRealtime = + expectedCurrentTime + systemClock.elapsedRealtime() - + systemClock.currentTimeMillis() + + val actual = assertNotNull(content.time as? When.Chronometer) + assertThat(actual.elapsedRealtimeMillis).isEqualTo(expectedElapsedRealtime) + assertThat(actual.isCountDown).isEqualTo(expected == ExpectedTime.CountDown) } } } + // TODO: Add tests for the style of the publicVersion once we implement that + @Test @EnableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME) fun extractContent_fromBaseStyle() { val entry = createEntry { setStyle(null) } - val content = extractContent(entry) + val content = requireContent(entry) - assertThat(content).isNotNull() - assertThat(content?.style).isEqualTo(Style.Base) + assertThat(content.privateVersion.style).isEqualTo(Style.Base) + assertThat(content.publicVersion.style).isEqualTo(Style.Base) } @Test @@ -367,10 +451,10 @@ class PromotedNotificationContentExtractorImplTest : SysuiTestCase() { fun extractContent_fromBigPictureStyle() { val entry = createEntry { setStyle(BigPictureStyle()) } - val content = extractContent(entry) + val content = requireContent(entry) - assertThat(content).isNotNull() - assertThat(content?.style).isEqualTo(Style.BigPicture) + assertThat(content.privateVersion.style).isEqualTo(Style.BigPicture) + assertThat(content.publicVersion.style).isEqualTo(Style.Base) } @Test @@ -387,12 +471,15 @@ class PromotedNotificationContentExtractorImplTest : SysuiTestCase() { ) } - val content = extractContent(entry) + val content = requireContent(entry) - assertThat(content).isNotNull() - assertThat(content?.style).isEqualTo(Style.BigText) - assertThat(content?.title).isEqualTo(TEST_BIG_CONTENT_TITLE) - assertThat(content?.text).isEqualTo(TEST_BIG_TEXT) + assertThat(content.privateVersion.style).isEqualTo(Style.BigText) + assertThat(content.privateVersion.title).isEqualTo(TEST_BIG_CONTENT_TITLE) + assertThat(content.privateVersion.text).isEqualTo(TEST_BIG_TEXT) + + assertThat(content.publicVersion.style).isEqualTo(Style.Base) + assertThat(content.publicVersion.title).isNull() + assertThat(content.publicVersion.text).isNull() } @Test @@ -409,12 +496,15 @@ class PromotedNotificationContentExtractorImplTest : SysuiTestCase() { ) } - val content = extractContent(entry) + val content = requireContent(entry) - assertThat(content).isNotNull() - assertThat(content?.style).isEqualTo(Style.BigText) - assertThat(content?.title).isEqualTo(TEST_CONTENT_TITLE) - assertThat(content?.text).isEqualTo(TEST_BIG_TEXT) + assertThat(content.privateVersion.style).isEqualTo(Style.BigText) + assertThat(content.privateVersion.title).isEqualTo(TEST_CONTENT_TITLE) + assertThat(content.privateVersion.text).isEqualTo(TEST_BIG_TEXT) + + assertThat(content.publicVersion.style).isEqualTo(Style.Base) + assertThat(content.publicVersion.title).isNull() + assertThat(content.publicVersion.text).isNull() } @Test @@ -431,12 +521,15 @@ class PromotedNotificationContentExtractorImplTest : SysuiTestCase() { ) } - val content = extractContent(entry) + val content = requireContent(entry) - assertThat(content).isNotNull() - assertThat(content?.style).isEqualTo(Style.BigText) - assertThat(content?.title).isEqualTo(TEST_BIG_CONTENT_TITLE) - assertThat(content?.text).isEqualTo(TEST_CONTENT_TEXT) + assertThat(content.privateVersion.style).isEqualTo(Style.BigText) + assertThat(content.privateVersion.title).isEqualTo(TEST_BIG_CONTENT_TITLE) + assertThat(content.privateVersion.text).isEqualTo(TEST_CONTENT_TEXT) + + assertThat(content.publicVersion.style).isEqualTo(Style.Base) + assertThat(content.publicVersion.title).isNull() + assertThat(content.publicVersion.text).isNull() } @Test @@ -451,11 +544,14 @@ class PromotedNotificationContentExtractorImplTest : SysuiTestCase() { ) val entry = createEntry { setStyle(CallStyle.forOngoingCall(TEST_PERSON, hangUpIntent)) } - val content = extractContent(entry) + val content = requireContent(entry) - assertThat(content).isNotNull() - assertThat(content?.style).isEqualTo(Style.Call) - assertThat(content?.title).isEqualTo(TEST_PERSON_NAME) + assertThat(content.privateVersion.style).isEqualTo(Style.Call) + assertThat(content.privateVersion.title).isEqualTo(TEST_PERSON_NAME) + + assertThat(content.publicVersion.style).isEqualTo(Style.Base) + assertThat(content.publicVersion.title).isNull() + assertThat(content.publicVersion.text).isNull() } @Test @@ -469,13 +565,17 @@ class PromotedNotificationContentExtractorImplTest : SysuiTestCase() { setStyle(ProgressStyle().addProgressSegment(Segment(100)).setProgress(75)) } - val content = extractContent(entry) + val content = requireContent(entry) - assertThat(content).isNotNull() - assertThat(content?.style).isEqualTo(Style.Progress) - assertThat(content?.newProgress).isNotNull() - assertThat(content?.newProgress?.progress).isEqualTo(75) - assertThat(content?.newProgress?.progressMax).isEqualTo(100) + assertThat(content.privateVersion.style).isEqualTo(Style.Progress) + val newProgress = assertNotNull(content.privateVersion.newProgress) + assertThat(newProgress.progress).isEqualTo(75) + assertThat(newProgress.progressMax).isEqualTo(100) + + assertThat(content.publicVersion.style).isEqualTo(Style.Base) + assertThat(content.publicVersion.title).isNull() + assertThat(content.publicVersion.text).isNull() + assertThat(content.publicVersion.newProgress).isNull() } @Test @@ -485,10 +585,11 @@ class PromotedNotificationContentExtractorImplTest : SysuiTestCase() { setStyle(MessagingStyle(TEST_PERSON).addMessage("message text", 0L, TEST_PERSON)) } - val content = extractContent(entry) + val content = requireContent(entry) - assertThat(content).isNotNull() - assertThat(content?.style).isEqualTo(Style.Ineligible) + assertThat(content.privateVersion.style).isEqualTo(Style.Ineligible) + + assertThat(content.publicVersion.style).isEqualTo(Style.Ineligible) } @Test @@ -498,18 +599,13 @@ class PromotedNotificationContentExtractorImplTest : SysuiTestCase() { setProgress(TEST_PROGRESS_MAX, TEST_PROGRESS, /* indeterminate= */ false) } - val content = extractContent(entry) - - assertThat(content).isNotNull() + val content = requireContent(entry) - val oldProgress = content?.oldProgress - assertThat(oldProgress).isNotNull() + val oldProgress = assertNotNull(content.privateVersion.oldProgress) - assertThat(content).isNotNull() - assertThat(content?.oldProgress).isNotNull() - assertThat(content?.oldProgress?.progress).isEqualTo(TEST_PROGRESS) - assertThat(content?.oldProgress?.max).isEqualTo(TEST_PROGRESS_MAX) - assertThat(content?.oldProgress?.isIndeterminate).isFalse() + assertThat(oldProgress.progress).isEqualTo(TEST_PROGRESS) + assertThat(oldProgress.max).isEqualTo(TEST_PROGRESS_MAX) + assertThat(oldProgress.isIndeterminate).isFalse() } @Test @@ -519,18 +615,25 @@ class PromotedNotificationContentExtractorImplTest : SysuiTestCase() { setProgress(TEST_PROGRESS_MAX, TEST_PROGRESS, /* indeterminate= */ true) } - val content = extractContent(entry) + val content = requireContent(entry) + val oldProgress = assertNotNull(content.privateVersion.oldProgress) - assertThat(content).isNotNull() - assertThat(content?.oldProgress).isNotNull() - assertThat(content?.oldProgress?.progress).isEqualTo(TEST_PROGRESS) - assertThat(content?.oldProgress?.max).isEqualTo(TEST_PROGRESS_MAX) - assertThat(content?.oldProgress?.isIndeterminate).isTrue() + assertThat(oldProgress.progress).isEqualTo(TEST_PROGRESS) + assertThat(oldProgress.max).isEqualTo(TEST_PROGRESS_MAX) + assertThat(oldProgress.isIndeterminate).isTrue() } - private fun extractContent(entry: NotificationEntry): PromotedNotificationContentModel? { + private fun requireContent( + entry: NotificationEntry, + redactionType: Int = REDACTION_TYPE_PUBLIC, + ): PromotedNotificationContentModels = assertNotNull(extractContent(entry, redactionType)) + + private fun extractContent( + entry: NotificationEntry, + redactionType: Int = REDACTION_TYPE_PUBLIC, + ): PromotedNotificationContentModels? { val recoveredBuilder = Notification.Builder(context, entry.sbn.notification) - return underTest.extractContent(entry, recoveredBuilder, imageModelProvider) + return underTest.extractContent(entry, recoveredBuilder, redactionType, imageModelProvider) } private fun createEntry( @@ -545,6 +648,11 @@ class PromotedNotificationContentExtractorImplTest : SysuiTestCase() { if (promoted) { notif.flags = FLAG_PROMOTED_ONGOING } + // Notification uses System.currentTimeMillis() to initialize creationTime; overwrite that + // with the value from our mock clock. + if (notif.creationTime != 0L) { + notif.creationTime = systemClock.currentTimeMillis() + } return NotificationEntryBuilder().setNotification(notif).build() } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/PromotedNotificationsInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/PromotedNotificationsInteractorTest.kt index 6192399c522b..42c3f6603ad8 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/PromotedNotificationsInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/PromotedNotificationsInteractorTest.kt @@ -16,6 +16,8 @@ package com.android.systemui.statusbar.notification.promoted.domain.interactor +import android.app.Notification.FLAG_FOREGROUND_SERVICE +import android.app.Notification.FLAG_ONGOING_EVENT import android.platform.test.annotations.EnableFlags import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest @@ -25,15 +27,26 @@ import com.android.systemui.kosmos.Kosmos.Fixture import com.android.systemui.kosmos.collectLastValue import com.android.systemui.kosmos.runTest import com.android.systemui.kosmos.useUnconfinedTestDispatcher +import com.android.systemui.mediaprojection.data.model.MediaProjectionState +import com.android.systemui.mediaprojection.data.repository.fakeMediaProjectionRepository +import com.android.systemui.mediaprojection.taskswitcher.FakeActivityTaskManager.Companion.createTask +import com.android.systemui.screenrecord.data.model.ScreenRecordModel +import com.android.systemui.screenrecord.data.repository.screenRecordRepository +import com.android.systemui.statusbar.chips.call.ui.viewmodel.CallChipViewModelTest.Companion.createStatusBarIconViewOrNull import com.android.systemui.statusbar.chips.notification.domain.interactor.statusBarNotificationChipsInteractor import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips import com.android.systemui.statusbar.core.StatusBarRootModernization import com.android.systemui.statusbar.notification.collection.buildNotificationEntry import com.android.systemui.statusbar.notification.collection.buildOngoingCallEntry import com.android.systemui.statusbar.notification.collection.buildPromotedOngoingEntry +import com.android.systemui.statusbar.notification.data.model.activeNotificationModel +import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository +import com.android.systemui.statusbar.notification.data.repository.addNotif import com.android.systemui.statusbar.notification.domain.interactor.renderNotificationListInteractor import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUi +import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentBuilder import com.android.systemui.statusbar.phone.ongoingcall.StatusBarChipsModernization +import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallTestHelper.addOngoingCallState import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import org.junit.Before @@ -76,6 +89,7 @@ class PromotedNotificationsInteractorTest : SysuiTestCase() { // THEN the order of the notification keys should be the call then the RON assertThat(orderedChipNotificationKeys) .containsExactly("0|test_pkg|0|call|0", "0|test_pkg|0|ron|0") + .inOrder() } @Test @@ -96,6 +110,521 @@ class PromotedNotificationsInteractorTest : SysuiTestCase() { // THEN the order of the notification keys should be the call then the RON assertThat(orderedChipNotificationKeys) .containsExactly("0|test_pkg|0|call|0", "0|test_pkg|0|ron|0") + .inOrder() + } + + @Test + fun orderedChipNotificationKeys_noScreenRecordNotif_isEmpty() = + kosmos.runTest { + screenRecordRepository.screenRecordState.value = ScreenRecordModel.Recording + fakeMediaProjectionRepository.mediaProjectionState.value = + MediaProjectionState.Projecting.EntireScreen(hostPackage = "test_pkg") + + renderNotificationListInteractor.setRenderedList(emptyList()) + + val orderedChipNotificationKeys by + collectLastValue(underTest.orderedChipNotificationKeys) + + assertThat(orderedChipNotificationKeys).isEmpty() + } + + @Test + fun orderedChipNotificationKeys_nullHostPackageForScreenRecord_isEmpty() = + kosmos.runTest { + screenRecordRepository.screenRecordState.value = ScreenRecordModel.Recording + // hostPackage would be provided through mediaProjectionState + fakeMediaProjectionRepository.mediaProjectionState.value = + MediaProjectionState.NotProjecting + + val entry = buildNotificationEntry(tag = "record", promoted = false) + renderNotificationListInteractor.setRenderedList(listOf(entry)) + + val orderedChipNotificationKeys by + collectLastValue(underTest.orderedChipNotificationKeys) + + assertThat(orderedChipNotificationKeys).isEmpty() + } + + @Test + fun orderedChipNotificationKeys_containsPromotedScreenRecordNotif() = + kosmos.runTest { + screenRecordRepository.screenRecordState.value = ScreenRecordModel.Recording + fakeMediaProjectionRepository.mediaProjectionState.value = + MediaProjectionState.Projecting.EntireScreen(hostPackage = "test_pkg") + + val screenRecordEntry = buildNotificationEntry(tag = "record", promoted = true) + renderNotificationListInteractor.setRenderedList(listOf(screenRecordEntry)) + + val orderedChipNotificationKeys by + collectLastValue(underTest.orderedChipNotificationKeys) + + assertThat(orderedChipNotificationKeys) + .containsExactly("0|test_pkg|0|record|0") + .inOrder() + } + + @Test + fun orderedChipNotificationKeys_containsNotPromotedScreenRecordNotif_ifOngoing() = + kosmos.runTest { + screenRecordRepository.screenRecordState.value = ScreenRecordModel.Recording + fakeMediaProjectionRepository.mediaProjectionState.value = + MediaProjectionState.Projecting.EntireScreen(hostPackage = "test_pkg") + + val screenRecordEntry = + buildNotificationEntry(tag = "record", promoted = false) { + setFlag(context, FLAG_ONGOING_EVENT, true) + } + renderNotificationListInteractor.setRenderedList(listOf(screenRecordEntry)) + + val orderedChipNotificationKeys by + collectLastValue(underTest.orderedChipNotificationKeys) + + assertThat(orderedChipNotificationKeys) + .containsExactly("0|test_pkg|0|record|0") + .inOrder() + } + + @Test + fun orderedChipNotificationKeys_containsNotPromotedScreenRecordNotif_ifFgs() = + kosmos.runTest { + screenRecordRepository.screenRecordState.value = ScreenRecordModel.Recording + fakeMediaProjectionRepository.mediaProjectionState.value = + MediaProjectionState.Projecting.EntireScreen(hostPackage = "test_pkg") + + val screenRecordEntry = + buildNotificationEntry(tag = "record", promoted = false) { + setFlag(context, FLAG_FOREGROUND_SERVICE, true) + } + renderNotificationListInteractor.setRenderedList(listOf(screenRecordEntry)) + + val orderedChipNotificationKeys by + collectLastValue(underTest.orderedChipNotificationKeys) + + assertThat(orderedChipNotificationKeys) + .containsExactly("0|test_pkg|0|record|0") + .inOrder() + } + + @Test + fun orderedChipNotificationKeys_doesNotContainScreenRecordNotif_ifNotOngoingOrFgs() = + kosmos.runTest { + screenRecordRepository.screenRecordState.value = ScreenRecordModel.Recording + fakeMediaProjectionRepository.mediaProjectionState.value = + MediaProjectionState.Projecting.EntireScreen(hostPackage = "test_pkg") + + val screenRecordEntry = + buildNotificationEntry(tag = "record", promoted = false) { + setFlag(context, FLAG_ONGOING_EVENT, false) + setFlag(context, FLAG_FOREGROUND_SERVICE, false) + } + renderNotificationListInteractor.setRenderedList(listOf(screenRecordEntry)) + + val orderedChipNotificationKeys by + collectLastValue(underTest.orderedChipNotificationKeys) + + assertThat(orderedChipNotificationKeys).isEmpty() + } + + @Test + fun orderedChipNotificationKeys_containsFgsScreenRecordNotif_whenNonFgsNotifExists() = + kosmos.runTest { + screenRecordRepository.screenRecordState.value = ScreenRecordModel.Recording + fakeMediaProjectionRepository.mediaProjectionState.value = + MediaProjectionState.Projecting.EntireScreen(hostPackage = "test_pkg") + + val fgsEntry = + buildNotificationEntry(tag = "recordFgs", promoted = false) { + setFlag(context, FLAG_FOREGROUND_SERVICE, true) + } + val notFgsEntry = + buildNotificationEntry(tag = "recordNotFgs", promoted = false) { + setFlag(context, FLAG_FOREGROUND_SERVICE, false) + } + renderNotificationListInteractor.setRenderedList(listOf(fgsEntry, notFgsEntry)) + + val orderedChipNotificationKeys by + collectLastValue(underTest.orderedChipNotificationKeys) + + assertThat(orderedChipNotificationKeys) + .containsExactly("0|test_pkg|0|recordFgs|0") + .inOrder() + } + + @Test + fun orderedChipNotificationKeys_containsOngoingScreenRecordNotif_whenNonOngoingNotifExists() = + kosmos.runTest { + screenRecordRepository.screenRecordState.value = ScreenRecordModel.Recording + fakeMediaProjectionRepository.mediaProjectionState.value = + MediaProjectionState.Projecting.EntireScreen(hostPackage = "test_pkg") + + val ongoingEntry = + buildNotificationEntry(tag = "recordOngoing", promoted = false) { + setFlag(context, FLAG_ONGOING_EVENT, true) + } + val notOngoingEntry = + buildNotificationEntry(tag = "recordNotOngoing", promoted = false) { + setFlag(context, FLAG_ONGOING_EVENT, false) + } + renderNotificationListInteractor.setRenderedList(listOf(notOngoingEntry, ongoingEntry)) + + val orderedChipNotificationKeys by + collectLastValue(underTest.orderedChipNotificationKeys) + + assertThat(orderedChipNotificationKeys) + .containsExactly("0|test_pkg|0|recordOngoing|0") + .inOrder() + } + + @Test + fun orderedChipNotificationKeys_containsFgsOngoingScreenRecordNotif_whenNonFgsOngoingNotifExists() = + kosmos.runTest { + screenRecordRepository.screenRecordState.value = ScreenRecordModel.Recording + fakeMediaProjectionRepository.mediaProjectionState.value = + MediaProjectionState.Projecting.EntireScreen(hostPackage = "test_pkg") + + val ongoingAndFgsEntry = + buildNotificationEntry(tag = "recordBoth", promoted = false) { + setFlag(context, FLAG_FOREGROUND_SERVICE, true) + setFlag(context, FLAG_ONGOING_EVENT, true) + } + val ongoingButNotFgsEntry = + buildNotificationEntry(tag = "recordOngoing", promoted = false) { + setFlag(context, FLAG_ONGOING_EVENT, true) + setFlag(context, FLAG_FOREGROUND_SERVICE, false) + } + val fgsButNotOngoingEntry = + buildNotificationEntry(tag = "recordFgs", promoted = false) { + setFlag(context, FLAG_FOREGROUND_SERVICE, true) + setFlag(context, FLAG_ONGOING_EVENT, false) + } + renderNotificationListInteractor.setRenderedList( + listOf(fgsButNotOngoingEntry, ongoingButNotFgsEntry, ongoingAndFgsEntry) + ) + + val orderedChipNotificationKeys by + collectLastValue(underTest.orderedChipNotificationKeys) + + assertThat(orderedChipNotificationKeys) + .containsExactly("0|test_pkg|0|recordBoth|0") + .inOrder() + } + + @Test + fun orderedChipNotificationKeys_twoEquivalentNotifsForScreenRecord_isEmpty() = + kosmos.runTest { + screenRecordRepository.screenRecordState.value = ScreenRecordModel.Recording + fakeMediaProjectionRepository.mediaProjectionState.value = + MediaProjectionState.Projecting.EntireScreen(hostPackage = "test_pkg") + + val entry1 = + buildNotificationEntry(tag = "entry1", promoted = false) { + setFlag(context, FLAG_FOREGROUND_SERVICE, true) + } + val entry2 = + buildNotificationEntry(tag = "entry2", promoted = false) { + setFlag(context, FLAG_FOREGROUND_SERVICE, true) + } + renderNotificationListInteractor.setRenderedList(listOf(entry1, entry2)) + + val orderedChipNotificationKeys by + collectLastValue(underTest.orderedChipNotificationKeys) + + assertThat(orderedChipNotificationKeys).isEmpty() + } + + @Test + fun orderedChipNotificationKeys_noMediProjNotif_isEmpty() = + kosmos.runTest { + fakeMediaProjectionRepository.mediaProjectionState.value = + MediaProjectionState.Projecting.SingleTask( + hostPackage = "test_pkg", + hostDeviceName = null, + createTask(taskId = 1), + ) + + renderNotificationListInteractor.setRenderedList(emptyList()) + + val orderedChipNotificationKeys by + collectLastValue(underTest.orderedChipNotificationKeys) + + assertThat(orderedChipNotificationKeys).isEmpty() + } + + @Test + fun orderedChipNotificationKeys_containsPromotedMediaProjNotif() = + kosmos.runTest { + fakeMediaProjectionRepository.mediaProjectionState.value = + MediaProjectionState.Projecting.SingleTask( + hostPackage = "test_pkg", + hostDeviceName = null, + createTask(taskId = 1), + ) + + val mediaProjEntry = buildNotificationEntry(tag = "proj", promoted = true) + renderNotificationListInteractor.setRenderedList(listOf(mediaProjEntry)) + + val orderedChipNotificationKeys by + collectLastValue(underTest.orderedChipNotificationKeys) + + assertThat(orderedChipNotificationKeys).containsExactly("0|test_pkg|0|proj|0").inOrder() + } + + @Test + fun orderedChipNotificationKeys_containsNotPromotedMediaProjNotif_ifOngoing() = + kosmos.runTest { + fakeMediaProjectionRepository.mediaProjectionState.value = + MediaProjectionState.Projecting.SingleTask( + hostPackage = "test_pkg", + hostDeviceName = null, + createTask(taskId = 1), + ) + + val mediaProjEntry = + buildNotificationEntry(tag = "proj", promoted = false) { + setFlag(context, FLAG_ONGOING_EVENT, true) + } + renderNotificationListInteractor.setRenderedList(listOf(mediaProjEntry)) + + val orderedChipNotificationKeys by + collectLastValue(underTest.orderedChipNotificationKeys) + + assertThat(orderedChipNotificationKeys).containsExactly("0|test_pkg|0|proj|0").inOrder() + } + + @Test + fun orderedChipNotificationKeys_containsNotPromotedMediaProjNotif_ifFgs() = + kosmos.runTest { + fakeMediaProjectionRepository.mediaProjectionState.value = + MediaProjectionState.Projecting.SingleTask( + hostPackage = "test_pkg", + hostDeviceName = null, + createTask(taskId = 1), + ) + + val mediaProjEntry = + buildNotificationEntry(tag = "proj", promoted = false) { + setFlag(context, FLAG_FOREGROUND_SERVICE, true) + } + renderNotificationListInteractor.setRenderedList(listOf(mediaProjEntry)) + + val orderedChipNotificationKeys by + collectLastValue(underTest.orderedChipNotificationKeys) + + assertThat(orderedChipNotificationKeys).containsExactly("0|test_pkg|0|proj|0").inOrder() + } + + @Test + fun orderedChipNotificationKeys_doesNotContainMediaProjNotif_ifNotOngoingOrFgs() = + kosmos.runTest { + fakeMediaProjectionRepository.mediaProjectionState.value = + MediaProjectionState.Projecting.SingleTask( + hostPackage = "test_pkg", + hostDeviceName = null, + createTask(taskId = 1), + ) + + val mediaProjEntry = + buildNotificationEntry(tag = "proj", promoted = false) { + setFlag(context, FLAG_ONGOING_EVENT, false) + setFlag(context, FLAG_FOREGROUND_SERVICE, false) + } + renderNotificationListInteractor.setRenderedList(listOf(mediaProjEntry)) + + val orderedChipNotificationKeys by + collectLastValue(underTest.orderedChipNotificationKeys) + + assertThat(orderedChipNotificationKeys).isEmpty() + } + + @Test + fun orderedChipNotificationKeys_containsFgsMediaProjNotif_whenNonFgsNotifExists() = + kosmos.runTest { + fakeMediaProjectionRepository.mediaProjectionState.value = + MediaProjectionState.Projecting.SingleTask( + hostPackage = "test_pkg", + hostDeviceName = null, + createTask(taskId = 1), + ) + + val fgsEntry = + buildNotificationEntry(tag = "projFgs", promoted = false) { + setFlag(context, FLAG_FOREGROUND_SERVICE, true) + } + val notFgsEntry = + buildNotificationEntry(tag = "projNotFgs", promoted = false) { + setFlag(context, FLAG_FOREGROUND_SERVICE, false) + } + renderNotificationListInteractor.setRenderedList(listOf(fgsEntry, notFgsEntry)) + + val orderedChipNotificationKeys by + collectLastValue(underTest.orderedChipNotificationKeys) + + assertThat(orderedChipNotificationKeys) + .containsExactly("0|test_pkg|0|projFgs|0") + .inOrder() + } + + @Test + fun orderedChipNotificationKeys_containsOngoingMediaProjNotif_whenNonOngoingNotifExists() = + kosmos.runTest { + fakeMediaProjectionRepository.mediaProjectionState.value = + MediaProjectionState.Projecting.SingleTask( + hostPackage = "test_pkg", + hostDeviceName = null, + createTask(taskId = 1), + ) + + val ongoingEntry = + buildNotificationEntry(tag = "projOngoing", promoted = false) { + setFlag(context, FLAG_ONGOING_EVENT, true) + } + val notOngoingEntry = + buildNotificationEntry(tag = "projNotOngoing", promoted = false) { + setFlag(context, FLAG_ONGOING_EVENT, false) + } + renderNotificationListInteractor.setRenderedList(listOf(notOngoingEntry, ongoingEntry)) + + val orderedChipNotificationKeys by + collectLastValue(underTest.orderedChipNotificationKeys) + + assertThat(orderedChipNotificationKeys) + .containsExactly("0|test_pkg|0|projOngoing|0") + .inOrder() + } + + @Test + fun orderedChipNotificationKeys_containsFgsOngoingMediaProjNotif_whenNonFgsOngoingNotifExists() = + kosmos.runTest { + fakeMediaProjectionRepository.mediaProjectionState.value = + MediaProjectionState.Projecting.SingleTask( + hostPackage = "test_pkg", + hostDeviceName = null, + createTask(taskId = 1), + ) + + val ongoingAndFgsEntry = + buildNotificationEntry(tag = "projBoth", promoted = false) { + setFlag(context, FLAG_FOREGROUND_SERVICE, true) + setFlag(context, FLAG_ONGOING_EVENT, true) + } + val ongoingButNotFgsEntry = + buildNotificationEntry(tag = "projOngoing", promoted = false) { + setFlag(context, FLAG_ONGOING_EVENT, true) + setFlag(context, FLAG_FOREGROUND_SERVICE, false) + } + val fgsButNotOngoingEntry = + buildNotificationEntry(tag = "projFgs", promoted = false) { + setFlag(context, FLAG_FOREGROUND_SERVICE, true) + setFlag(context, FLAG_ONGOING_EVENT, false) + } + renderNotificationListInteractor.setRenderedList( + listOf(fgsButNotOngoingEntry, ongoingButNotFgsEntry, ongoingAndFgsEntry) + ) + + val orderedChipNotificationKeys by + collectLastValue(underTest.orderedChipNotificationKeys) + + assertThat(orderedChipNotificationKeys) + .containsExactly("0|test_pkg|0|projBoth|0") + .inOrder() + } + + @Test + fun orderedChipNotificationKeys_twoEquivalentNotifsForMediaProj_isEmpty() = + kosmos.runTest { + fakeMediaProjectionRepository.mediaProjectionState.value = + MediaProjectionState.Projecting.SingleTask( + hostPackage = "test_pkg", + hostDeviceName = null, + createTask(taskId = 1), + ) + + val entry1 = + buildNotificationEntry(tag = "entry1", promoted = false) { + setFlag(context, FLAG_FOREGROUND_SERVICE, true) + } + val entry2 = + buildNotificationEntry(tag = "entry2", promoted = false) { + setFlag(context, FLAG_FOREGROUND_SERVICE, true) + } + renderNotificationListInteractor.setRenderedList(listOf(entry1, entry2)) + + val orderedChipNotificationKeys by + collectLastValue(underTest.orderedChipNotificationKeys) + + assertThat(orderedChipNotificationKeys).isEmpty() + } + + @Test + fun orderedChipNotificationKeys_maintainsPromotedNotifOrder() = + kosmos.runTest { + activeNotificationListRepository.addNotif( + activeNotificationModel( + key = "notif1", + statusBarChipIcon = createStatusBarIconViewOrNull(), + promotedContent = PromotedNotificationContentBuilder("notif1").build(), + ) + ) + activeNotificationListRepository.addNotif( + activeNotificationModel( + key = "notif2", + statusBarChipIcon = createStatusBarIconViewOrNull(), + promotedContent = PromotedNotificationContentBuilder("notif2").build(), + ) + ) + + val orderedChipNotificationKeys by + collectLastValue(underTest.orderedChipNotificationKeys) + + assertThat(orderedChipNotificationKeys).containsExactly("notif1", "notif2").inOrder() + } + + // The ranking between different chips should stay consistent between + // PromotedNotificationsInteractor and OngoingActivityChipsViewModel. + // See OngoingActivityChipsWithNotifsViewModelTest#chips_screenRecordAndCallAndPromotedNotifs + // test for the right ranking. + @Test + fun orderedChipNotificationKeys_rankingIsCorrect() = + kosmos.runTest { + // Screen record + screenRecordRepository.screenRecordState.value = ScreenRecordModel.Recording + fakeMediaProjectionRepository.mediaProjectionState.value = + MediaProjectionState.Projecting.SingleTask( + hostPackage = "screen.record.package", + hostDeviceName = null, + createTask(taskId = 1), + ) + activeNotificationListRepository.addNotif( + activeNotificationModel( + key = "screenRecordKey", + packageName = "screen.record.package", + isOngoingEvent = true, + ) + ) + // Call + addOngoingCallState(key = "callKey") + // Other promoted notifs + activeNotificationListRepository.addNotif( + activeNotificationModel( + key = "notif1", + statusBarChipIcon = createStatusBarIconViewOrNull(), + promotedContent = PromotedNotificationContentBuilder("notif1").build(), + ) + ) + activeNotificationListRepository.addNotif( + activeNotificationModel( + key = "notif2", + statusBarChipIcon = createStatusBarIconViewOrNull(), + promotedContent = PromotedNotificationContentBuilder("notif2").build(), + ) + ) + + val orderedChipNotificationKeys by + collectLastValue(underTest.orderedChipNotificationKeys) + + assertThat(orderedChipNotificationKeys) + .containsExactly("screenRecordKey", "callKey", "notif1", "notif2") + .inOrder() } @Test @@ -114,8 +643,7 @@ class PromotedNotificationsInteractorTest : SysuiTestCase() { collectLastValue(underTest.aodPromotedNotification) // THEN the ron is first because the call has no content - assertThat(topPromotedNotificationContent?.identity?.key) - .isEqualTo("0|test_pkg|0|ron|0") + assertThat(topPromotedNotificationContent?.key).isEqualTo("0|test_pkg|0|ron|0") } @Test @@ -134,8 +662,7 @@ class PromotedNotificationsInteractorTest : SysuiTestCase() { collectLastValue(underTest.aodPromotedNotification) // THEN the call is the top notification - assertThat(topPromotedNotificationContent?.identity?.key) - .isEqualTo("0|test_pkg|0|call|0") + assertThat(topPromotedNotificationContent?.key).isEqualTo("0|test_pkg|0|call|0") } @Test diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java index 99f2596dbf1d..19b1046f1931 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationContentInflaterTest.java @@ -67,11 +67,11 @@ import com.android.systemui.media.controls.util.MediaFeatureFlag; import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.chips.notification.shared.StatusBarNotifChips; import com.android.systemui.statusbar.notification.ConversationNotificationProcessor; -import com.android.systemui.statusbar.notification.collection.EntryAdapter; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.promoted.FakePromotedNotificationContentExtractor; import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUi; -import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel; +import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentBuilder; +import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModels; import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.BindParams; import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationCallback; import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag; @@ -389,8 +389,8 @@ public class NotificationContentInflaterTest extends SysuiTestCase { @Test @DisableFlags({PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME}) public void testExtractsPromotedContent_notWhenBothFlagsDisabled() throws Exception { - final PromotedNotificationContentModel content = - new PromotedNotificationContentModel.Builder("key").build(); + final PromotedNotificationContentModels content = + new PromotedNotificationContentBuilder("key").build(); mPromotedNotificationContentExtractor.resetForEntry(mRow.getEntry(), content); inflateAndWait(mNotificationInflater, FLAG_CONTENT_VIEW_ALL, mRow); @@ -401,43 +401,43 @@ public class NotificationContentInflaterTest extends SysuiTestCase { @Test @EnableFlags(PromotedNotificationUi.FLAG_NAME) @DisableFlags(StatusBarNotifChips.FLAG_NAME) - public void testExtractsPromotedContent_whenPromotedNotificationUiFlagEnabled() + public void testExtractsPromotedContent_whePromotedNotificationUiFlagEnabled() throws Exception { - final PromotedNotificationContentModel content = - new PromotedNotificationContentModel.Builder("key").build(); + final PromotedNotificationContentModels content = + new PromotedNotificationContentBuilder("key").build(); mPromotedNotificationContentExtractor.resetForEntry(mRow.getEntry(), content); inflateAndWait(mNotificationInflater, FLAG_CONTENT_VIEW_ALL, mRow); mPromotedNotificationContentExtractor.verifyOneExtractCall(); - assertEquals(content, mRow.getEntry().getPromotedNotificationContentModel()); + assertEquals(content, mRow.getEntry().getPromotedNotificationContentModels()); } @Test @EnableFlags(StatusBarNotifChips.FLAG_NAME) @DisableFlags(PromotedNotificationUi.FLAG_NAME) public void testExtractsPromotedContent_whenStatusBarNotifChipsFlagEnabled() throws Exception { - final PromotedNotificationContentModel content = - new PromotedNotificationContentModel.Builder("key").build(); + final PromotedNotificationContentModels content = + new PromotedNotificationContentBuilder("key").build(); mPromotedNotificationContentExtractor.resetForEntry(mRow.getEntry(), content); inflateAndWait(mNotificationInflater, FLAG_CONTENT_VIEW_ALL, mRow); mPromotedNotificationContentExtractor.verifyOneExtractCall(); - assertEquals(content, mRow.getEntry().getPromotedNotificationContentModel()); + assertEquals(content, mRow.getEntry().getPromotedNotificationContentModels()); } @Test @EnableFlags({PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME}) public void testExtractsPromotedContent_whenBothFlagsEnabled() throws Exception { - final PromotedNotificationContentModel content = - new PromotedNotificationContentModel.Builder("key").build(); + final PromotedNotificationContentModels content = + new PromotedNotificationContentBuilder("key").build(); mPromotedNotificationContentExtractor.resetForEntry(mRow.getEntry(), content); inflateAndWait(mNotificationInflater, FLAG_CONTENT_VIEW_ALL, mRow); mPromotedNotificationContentExtractor.verifyOneExtractCall(); - assertEquals(content, mRow.getEntry().getPromotedNotificationContentModel()); + assertEquals(content, mRow.getEntry().getPromotedNotificationContentModels()); } @Test @@ -448,7 +448,7 @@ public class NotificationContentInflaterTest extends SysuiTestCase { inflateAndWait(mNotificationInflater, FLAG_CONTENT_VIEW_ALL, mRow); mPromotedNotificationContentExtractor.verifyOneExtractCall(); - assertNull(mRow.getEntry().getPromotedNotificationContentModel()); + assertNull(mRow.getEntry().getPromotedNotificationContentModels()); } @Test diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.kt index 0ac5fe95957c..16663def16a4 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationInfoTest.kt @@ -67,8 +67,8 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntryB import com.android.systemui.statusbar.notification.promoted.domain.interactor.PackageDemotionInteractor import com.android.systemui.statusbar.notification.row.icon.AppIconProvider import com.android.systemui.statusbar.notification.row.icon.NotificationIconStyleProvider -import com.android.systemui.statusbar.notification.row.icon.appIconProvider -import com.android.systemui.statusbar.notification.row.icon.notificationIconStyleProvider +import com.android.systemui.statusbar.notification.row.icon.mockAppIconProvider +import com.android.systemui.statusbar.notification.row.icon.mockNotificationIconStyleProvider import com.android.systemui.testKosmos import com.android.telecom.telecomManager import com.google.common.truth.Truth.assertThat @@ -80,6 +80,7 @@ import org.mockito.ArgumentMatchers.anyBoolean import org.mockito.ArgumentMatchers.anyInt import org.mockito.ArgumentMatchers.anyString import org.mockito.kotlin.any +import org.mockito.kotlin.anyOrNull import org.mockito.kotlin.argumentCaptor import org.mockito.kotlin.eq import org.mockito.kotlin.mock @@ -101,6 +102,8 @@ class NotificationInfoTest : SysuiTestCase() { private lateinit var entry: NotificationEntry private val mockPackageManager = kosmos.mockPackageManager + private val mockAppIconProvider = kosmos.mockAppIconProvider + private val mockIconStyleProvider = kosmos.mockNotificationIconStyleProvider private val uiEventLogger = kosmos.uiEventLoggerFake private val testableLooper by lazy { kosmos.testableLooper } @@ -202,7 +205,8 @@ class NotificationInfoTest : SysuiTestCase() { } @Test - fun testBindNotification_SetsPackageIcon() { + @DisableFlags(com.android.systemui.Flags.FLAG_NOTIFICATIONS_REDESIGN_GUTS) + fun testBindNotification_SetsPackageIcon_flagOff() { val iconDrawable = mock<Drawable>() whenever(mockPackageManager.getApplicationIcon(any<ApplicationInfo>())) .thenReturn(iconDrawable) @@ -212,6 +216,26 @@ class NotificationInfoTest : SysuiTestCase() { } @Test + @EnableFlags(com.android.systemui.Flags.FLAG_NOTIFICATIONS_REDESIGN_GUTS) + fun testBindNotification_SetsPackageIcon_flagOn() { + val iconDrawable = mock<Drawable>() + whenever(mockIconStyleProvider.shouldShowWorkProfileBadge(anyOrNull(), anyOrNull())) + .thenReturn(false) + whenever( + mockAppIconProvider.getOrFetchAppIcon( + anyOrNull(), + anyOrNull(), + anyBoolean(), + anyBoolean(), + ) + ) + .thenReturn(iconDrawable) + bindNotification() + val iconView = underTest.findViewById<ImageView>(R.id.pkg_icon) + assertThat(iconView.drawable).isEqualTo(iconDrawable) + } + + @Test fun testBindNotification_noDelegate() { bindNotification() val nameView = underTest.findViewById<TextView>(R.id.delegate_name) @@ -894,8 +918,8 @@ class NotificationInfoTest : SysuiTestCase() { private fun bindNotification( pm: PackageManager = this.mockPackageManager, iNotificationManager: INotificationManager = this.mockINotificationManager, - appIconProvider: AppIconProvider = kosmos.appIconProvider, - iconStyleProvider: NotificationIconStyleProvider = kosmos.notificationIconStyleProvider, + appIconProvider: AppIconProvider = this.mockAppIconProvider, + iconStyleProvider: NotificationIconStyleProvider = this.mockIconStyleProvider, onUserInteractionCallback: OnUserInteractionCallback = this.onUserInteractionCallback, channelEditorDialogController: ChannelEditorDialogController = this.channelEditorDialogController, diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java index 95366568a37a..5ad4a4fab056 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationMenuRowTest.java @@ -103,46 +103,6 @@ public class NotificationMenuRowTest extends LeakCheckedTest { row.resetMenu(); } - - @Test - public void testNoAppOpsInSlowSwipe() { - when(mRow.getShowSnooze()).thenReturn(false); - Settings.Global.putInt(mContext.getContentResolver(), SHOW_NEW_NOTIF_DISMISS, 0); - - NotificationMenuRow row = new NotificationMenuRow(mContext, mPeopleNotificationIdentifier); - row.createMenu(mRow); - - ViewGroup container = (ViewGroup) row.getMenuView(); - // noti blocking - assertEquals(1, container.getChildCount()); - } - - @Test - public void testNoSnoozeInSlowSwipe() { - when(mRow.getShowSnooze()).thenReturn(false); - Settings.Global.putInt(mContext.getContentResolver(), SHOW_NEW_NOTIF_DISMISS, 0); - - NotificationMenuRow row = new NotificationMenuRow(mContext, mPeopleNotificationIdentifier); - row.createMenu(mRow); - - ViewGroup container = (ViewGroup) row.getMenuView(); - // just for noti blocking - assertEquals(1, container.getChildCount()); - } - - @Test - public void testSnoozeInSlowSwipe() { - when(mRow.getShowSnooze()).thenReturn(true); - Settings.Global.putInt(mContext.getContentResolver(), SHOW_NEW_NOTIF_DISMISS, 0); - - NotificationMenuRow row = new NotificationMenuRow(mContext, mPeopleNotificationIdentifier); - row.createMenu(mRow); - - ViewGroup container = (ViewGroup) row.getMenuView(); - // one for snooze and one for noti blocking - assertEquals(2, container.getChildCount()); - } - @Test public void testSlowSwipe_newDismiss() { when(mRow.getShowSnooze()).thenReturn(true); @@ -237,6 +197,7 @@ public class NotificationMenuRowTest extends LeakCheckedTest { new NotificationMenuRow(mContext, mPeopleNotificationIdentifier)); doReturn(30f).when(row).getSnapBackThreshold(); doReturn(50f).when(row).getDismissThreshold(); + doReturn(70).when(row).getSpaceForMenu(); when(row.isMenuOnLeft()).thenReturn(true); when(row.getTranslation()).thenReturn(40f); diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt index 063a04ab9f37..dcba3e447dda 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImplTest.kt @@ -44,7 +44,7 @@ import com.android.systemui.statusbar.notification.ConversationNotificationProce import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.promoted.FakePromotedNotificationContentExtractor import com.android.systemui.statusbar.notification.promoted.PromotedNotificationUi -import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel +import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentBuilder import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.BindParams import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_ALL import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.FLAG_CONTENT_VIEW_CONTRACTED @@ -456,7 +456,7 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() { @Test @DisableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME) fun testExtractsPromotedContent_notWhenBothFlagsDisabled() { - val content = PromotedNotificationContentModel.Builder("key").build() + val content = PromotedNotificationContentBuilder("key").build() promotedNotificationContentExtractor.resetForEntry(row.entry, content) inflateAndWait(notificationInflater, FLAG_CONTENT_VIEW_ALL, row) @@ -468,38 +468,38 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() { @EnableFlags(PromotedNotificationUi.FLAG_NAME) @DisableFlags(StatusBarNotifChips.FLAG_NAME) fun testExtractsPromotedContent_whenPromotedNotificationUiFlagEnabled() { - val content = PromotedNotificationContentModel.Builder("key").build() + val content = PromotedNotificationContentBuilder("key").build() promotedNotificationContentExtractor.resetForEntry(row.entry, content) inflateAndWait(notificationInflater, FLAG_CONTENT_VIEW_ALL, row) promotedNotificationContentExtractor.verifyOneExtractCall() - Assert.assertEquals(content, row.entry.promotedNotificationContentModel) + Assert.assertEquals(content, row.entry.promotedNotificationContentModels) } @Test @EnableFlags(StatusBarNotifChips.FLAG_NAME) @DisableFlags(PromotedNotificationUi.FLAG_NAME) fun testExtractsPromotedContent_whenStatusBarNotifChipsFlagEnabled() { - val content = PromotedNotificationContentModel.Builder("key").build() + val content = PromotedNotificationContentBuilder("key").build() promotedNotificationContentExtractor.resetForEntry(row.entry, content) inflateAndWait(notificationInflater, FLAG_CONTENT_VIEW_ALL, row) promotedNotificationContentExtractor.verifyOneExtractCall() - Assert.assertEquals(content, row.entry.promotedNotificationContentModel) + Assert.assertEquals(content, row.entry.promotedNotificationContentModels) } @Test @EnableFlags(PromotedNotificationUi.FLAG_NAME, StatusBarNotifChips.FLAG_NAME) fun testExtractsPromotedContent_whenBothFlagsEnabled() { - val content = PromotedNotificationContentModel.Builder("key").build() + val content = PromotedNotificationContentBuilder("key").build() promotedNotificationContentExtractor.resetForEntry(row.entry, content) inflateAndWait(notificationInflater, FLAG_CONTENT_VIEW_ALL, row) promotedNotificationContentExtractor.verifyOneExtractCall() - Assert.assertEquals(content, row.entry.promotedNotificationContentModel) + Assert.assertEquals(content, row.entry.promotedNotificationContentModels) } @Test @@ -510,7 +510,7 @@ class NotificationRowContentBinderImplTest : SysuiTestCase() { inflateAndWait(notificationInflater, FLAG_CONTENT_VIEW_ALL, row) promotedNotificationContentExtractor.verifyOneExtractCall() - Assert.assertNull(row.entry.promotedNotificationContentModel) + Assert.assertNull(row.entry.promotedNotificationContentModels) } @Test diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManagerImplTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManagerImplTest.kt index 6c6ba933c03a..f52f96efb9d1 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManagerImplTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManagerImplTest.kt @@ -139,7 +139,7 @@ class MagneticNotificationRowManagerImplTest : SysuiTestCase() { underTest.setMagneticRowTranslation(swipedRow, translation = 100f) // WHEN setting a translation that will fall below the threshold - val translation = threshold / underTest.swipedRowMultiplier - 50f + val translation = 50f underTest.setMagneticRowTranslation(swipedRow, translation) // THEN the targets continue to be pulled and translations are set @@ -162,7 +162,7 @@ class MagneticNotificationRowManagerImplTest : SysuiTestCase() { underTest.setMagneticRowTranslation(swipedRow, translation = 100f) // WHEN setting a translation that will fall below the threshold - val translation = threshold / underTest.swipedRowMultiplier - 50f + val translation = 50f underTest.setMagneticRowTranslation(swipedRow, translation) // THEN the targets continue to be pulled and reduced translations are set @@ -185,7 +185,7 @@ class MagneticNotificationRowManagerImplTest : SysuiTestCase() { underTest.setMagneticRowTranslation(swipedRow, translation = 100f) // WHEN setting a translation that will fall above the threshold - val translation = threshold / underTest.swipedRowMultiplier + 50f + val translation = 150f underTest.setMagneticRowTranslation(swipedRow, translation) // THEN the swiped view detaches and the correct detach haptics play @@ -208,7 +208,7 @@ class MagneticNotificationRowManagerImplTest : SysuiTestCase() { underTest.setMagneticRowTranslation(swipedRow, translation = 100f) // WHEN setting a translation that will fall above the threshold - val translation = threshold / underTest.swipedRowMultiplier + 50f + val translation = 150f underTest.setMagneticRowTranslation(swipedRow, translation) // THEN the swiped view does not detach and the reduced translation is set @@ -248,6 +248,19 @@ class MagneticNotificationRowManagerImplTest : SysuiTestCase() { } @Test + fun onMagneticInteractionEnd_whileTargetsSet_goesToIdle() = + kosmos.testScope.runTest { + // GIVEN that targets are set + setTargets() + + // WHEN the interaction ends on the row + underTest.onMagneticInteractionEnd(swipedRow, velocity = null) + + // THEN the state resets + assertThat(underTest.currentState).isEqualTo(State.IDLE) + } + + @Test fun onMagneticInteractionEnd_whileDetached_goesToIdle() = kosmos.testScope.runTest { // GIVEN the swiped row is detached @@ -342,7 +355,7 @@ class MagneticNotificationRowManagerImplTest : SysuiTestCase() { underTest.setMagneticRowTranslation(swipedRow, translation = 100f) // Set a translation that will fall above the threshold - val translation = threshold / underTest.swipedRowMultiplier + 50f + val translation = 150f underTest.setMagneticRowTranslation(swipedRow, translation) assertThat(underTest.currentState).isEqualTo(State.DETACHED) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java index 1ea41de63e64..716353945be2 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java @@ -186,7 +186,8 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase { .thenReturn(false); mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT, BiometricSourceType.FINGERPRINT, true /* isStrongBiometric */); - verify(mStatusBarKeyguardViewManager).showPrimaryBouncer(anyBoolean()); + verify(mStatusBarKeyguardViewManager).showPrimaryBouncer(anyBoolean(), + eq("BiometricUnlockController#MODE_SHOW_BOUNCER")); verify(mStatusBarKeyguardViewManager, never()).notifyKeyguardAuthenticated(anyBoolean()); assertThat(mBiometricUnlockController.getMode()) .isEqualTo(BiometricUnlockController.MODE_SHOW_BOUNCER); @@ -198,7 +199,8 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase { .thenReturn(false); mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT, BiometricSourceType.FINGERPRINT, false /* isStrongBiometric */); - verify(mStatusBarKeyguardViewManager).showPrimaryBouncer(anyBoolean()); + verify(mStatusBarKeyguardViewManager).showPrimaryBouncer(anyBoolean(), + eq("BiometricUnlockController#MODE_SHOW_BOUNCER")); assertThat(mBiometricUnlockController.getMode()) .isEqualTo(BiometricUnlockController.MODE_SHOW_BOUNCER); assertThat(mBiometricUnlockController.getBiometricType()) @@ -248,7 +250,8 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase { mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT, BiometricSourceType.FINGERPRINT, true /* isStrongBiometric */); - verify(mStatusBarKeyguardViewManager, never()).showPrimaryBouncer(anyBoolean()); + verify(mStatusBarKeyguardViewManager, never()).showPrimaryBouncer(anyBoolean(), + eq("BiometricUnlockController#MODE_SHOW_BOUNCER")); verify(mStatusBarKeyguardViewManager).notifyKeyguardAuthenticated(eq(false)); assertThat(mBiometricUnlockController.getMode()) .isEqualTo(BiometricUnlockController.MODE_UNLOCK_COLLAPSING); @@ -327,7 +330,8 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase { mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT, BiometricSourceType.FACE, true /* isStrongBiometric */); - verify(mStatusBarKeyguardViewManager).showPrimaryBouncer(anyBoolean()); + verify(mStatusBarKeyguardViewManager).showPrimaryBouncer(anyBoolean(), + eq("BiometricUnlockController#MODE_SHOW_BOUNCER")); assertThat(mBiometricUnlockController.getMode()) .isEqualTo(BiometricUnlockController.MODE_SHOW_BOUNCER); } @@ -359,7 +363,8 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase { mBiometricUnlockController.onBiometricAuthenticated(UserHandle.USER_CURRENT, BiometricSourceType.FACE, true /* isStrongBiometric */); - verify(mStatusBarKeyguardViewManager, never()).showPrimaryBouncer(anyBoolean()); + verify(mStatusBarKeyguardViewManager, never()).showPrimaryBouncer(anyBoolean(), + eq("BiometricUnlockController#MODE_SHOW_BOUNCER")); assertThat(mBiometricUnlockController.getMode()) .isEqualTo(BiometricUnlockController.MODE_NONE); } @@ -438,17 +443,20 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase { // WHEN udfps fails once - then don't show the bouncer yet mBiometricUnlockController.onBiometricAuthFailed(BiometricSourceType.FINGERPRINT); - verify(mStatusBarKeyguardViewManager, never()).showPrimaryBouncer(anyBoolean()); + verify(mStatusBarKeyguardViewManager, never()).showPrimaryBouncer(anyBoolean(), + eq("BiometricUnlockController#MODE_SHOW_BOUNCER")); // WHEN udfps fails the second time - then don't show the bouncer yet mBiometricUnlockController.onBiometricAuthFailed(BiometricSourceType.FINGERPRINT); - verify(mStatusBarKeyguardViewManager, never()).showPrimaryBouncer(anyBoolean()); + verify(mStatusBarKeyguardViewManager, never()).showPrimaryBouncer(anyBoolean(), + eq("BiometricUnlockController#MODE_SHOW_BOUNCER")); // WHEN udpfs fails the third time mBiometricUnlockController.onBiometricAuthFailed(BiometricSourceType.FINGERPRINT); // THEN show the bouncer - verify(mStatusBarKeyguardViewManager).showPrimaryBouncer(true); + verify(mStatusBarKeyguardViewManager).showPrimaryBouncer(true, + "BiometricUnlockController#MODE_SHOW_BOUNCER"); } @Test @@ -460,14 +468,16 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase { mBiometricUnlockController.onBiometricAuthFailed(BiometricSourceType.FINGERPRINT); mBiometricUnlockController.onBiometricAuthFailed(BiometricSourceType.FINGERPRINT); mBiometricUnlockController.onBiometricAuthFailed(BiometricSourceType.FINGERPRINT); - verify(mStatusBarKeyguardViewManager, never()).showPrimaryBouncer(anyBoolean()); + verify(mStatusBarKeyguardViewManager, never()).showPrimaryBouncer(anyBoolean(), + eq("BiometricUnlockController#MODE_SHOW_BOUNCER")); // WHEN lockout is received mBiometricUnlockController.onBiometricError(FingerprintManager.FINGERPRINT_ERROR_LOCKOUT, "Lockout", BiometricSourceType.FINGERPRINT); // THEN show bouncer - verify(mStatusBarKeyguardViewManager).showPrimaryBouncer(true); + verify(mStatusBarKeyguardViewManager).showPrimaryBouncer(true, + "BiometricUnlockController#MODE_SHOW_BOUNCER"); } @Test @@ -544,7 +554,8 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase { BiometricSourceType.FINGERPRINT, true /* isStrongBiometric */); // THEN shows primary bouncer - verify(mStatusBarKeyguardViewManager).showPrimaryBouncer(anyBoolean()); + verify(mStatusBarKeyguardViewManager).showPrimaryBouncer(anyBoolean(), + eq("BiometricUnlockController#MODE_SHOW_BOUNCER")); } @Test @@ -554,7 +565,8 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase { BiometricSourceType.FACE, false /* isStrongBiometric */); // THEN shows primary bouncer - verify(mStatusBarKeyguardViewManager).showPrimaryBouncer(anyBoolean()); + verify(mStatusBarKeyguardViewManager).showPrimaryBouncer(anyBoolean(), + eq("BiometricUnlockController#MODE_SHOW_BOUNCER")); } private void givenFingerprintModeUnlockCollapsing() { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java index 1cc291199531..d9e256228428 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallbackTest.java @@ -124,7 +124,8 @@ public class StatusBarRemoteInputCallbackTest extends SysuiTestCase { mRemoteInputCallback.onLockedRemoteInput( mock(ExpandableNotificationRow.class), mock(View.class)); - verify(mStatusBarKeyguardViewManager).showBouncer(true); + verify(mStatusBarKeyguardViewManager).showBouncer(true, + "StatusBarRemoteInputCallback#onLockedRemoteInput"); } @Test @DisableFlags(ExpandHeadsUpOnInlineReply.FLAG_NAME) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt index c58b4bc9953c..18074d53e87b 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallControllerTest.kt @@ -40,7 +40,7 @@ import com.android.systemui.statusbar.notification.data.model.activeNotification import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor -import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel +import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentBuilder import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel import com.android.systemui.statusbar.notification.shared.CallType import com.android.systemui.statusbar.phone.ongoingcall.data.repository.ongoingCallRepository @@ -170,7 +170,7 @@ class OngoingCallControllerTest : SysuiTestCase() { @Test fun interactorHasOngoingCallNotif_repoHasPromotedContent() = testScope.runTest { - val promotedContent = PromotedNotificationContentModel.Builder("ongoingNotif").build() + val promotedContent = PromotedNotificationContentBuilder("ongoingNotif").build() setNotifOnRepo( activeNotificationModel( key = "ongoingNotif", diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractorTest.kt index 84f1d5cd4895..c071327ae398 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ongoingcall/domain/interactor/OngoingCallInteractorTest.kt @@ -29,7 +29,7 @@ import com.android.systemui.kosmos.useUnconfinedTestDispatcher import com.android.systemui.statusbar.StatusBarIconView import com.android.systemui.statusbar.data.repository.fakeStatusBarModeRepository import com.android.systemui.statusbar.gesture.swipeStatusBarAwayGestureHandler -import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel +import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentBuilder import com.android.systemui.statusbar.phone.ongoingcall.EnableChipsModernization import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallTestHelper.addOngoingCallState @@ -75,7 +75,7 @@ class OngoingCallInteractorTest : SysuiTestCase() { val startTimeMs = 1000L val testIconView: StatusBarIconView = mock() val testIntent: PendingIntent = mock() - val testPromotedContent = PromotedNotificationContentModel.Builder(key).build() + val testPromotedContent = PromotedNotificationContentBuilder(key).build() addOngoingCallState( key = key, startTimeMs = startTimeMs, @@ -106,7 +106,7 @@ class OngoingCallInteractorTest : SysuiTestCase() { val startTimeMs = 1000L val testIconView: StatusBarIconView = mock() val testIntent: PendingIntent = mock() - val testPromotedContent = PromotedNotificationContentModel.Builder(key).build() + val testPromotedContent = PromotedNotificationContentBuilder(key).build() addOngoingCallState( key = key, startTimeMs = startTimeMs, diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ui/IconManagerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ui/IconManagerTest.kt index 90732d0183d2..318eb87f500b 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ui/IconManagerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ui/IconManagerTest.kt @@ -14,6 +14,8 @@ * limitations under the License. */ +@file:OptIn(ExperimentalKairosApi::class) + package com.android.systemui.statusbar.phone.ui import android.app.Flags @@ -28,13 +30,17 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.internal.statusbar.StatusBarIcon import com.android.systemui.SysuiTestCase +import com.android.systemui.kairos.ExperimentalKairosApi +import com.android.systemui.kairos.KairosNetwork import com.android.systemui.statusbar.StatusBarIconView import com.android.systemui.statusbar.connectivity.ui.MobileContextProvider import com.android.systemui.statusbar.phone.StatusBarLocation import com.android.systemui.statusbar.pipeline.mobile.ui.MobileUiAdapter +import com.android.systemui.statusbar.pipeline.mobile.ui.MobileUiAdapterKairos import com.android.systemui.statusbar.pipeline.wifi.ui.WifiUiAdapter import com.android.systemui.util.Assert import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.CoroutineScope import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -58,7 +64,10 @@ class IconManagerTest : SysuiTestCase() { StatusBarLocation.HOME, mock<WifiUiAdapter>(defaultAnswer = RETURNS_DEEP_STUBS), mock<MobileUiAdapter>(defaultAnswer = RETURNS_DEEP_STUBS), + { mock<MobileUiAdapterKairos>(defaultAnswer = RETURNS_DEEP_STUBS) }, mock<MobileContextProvider>(defaultAnswer = RETURNS_DEEP_STUBS), + mock<KairosNetwork>(defaultAnswer = RETURNS_DEEP_STUBS), + mock<CoroutineScope>(defaultAnswer = RETURNS_DEEP_STUBS), ) } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ui/StatusBarIconControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ui/StatusBarIconControllerTest.java index 891ff38764fe..8e3117f47f86 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ui/StatusBarIconControllerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/phone/ui/StatusBarIconControllerTest.java @@ -35,6 +35,7 @@ import androidx.test.filters.SmallTest; import com.android.internal.statusbar.StatusBarIcon; import com.android.systemui.demomode.DemoModeController; import com.android.systemui.dump.DumpManager; +import com.android.systemui.kairos.KairosNetwork; import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.statusbar.CommandQueue; import com.android.systemui.statusbar.StatusBarIconView; @@ -45,12 +46,15 @@ import com.android.systemui.statusbar.phone.StatusBarLocation; import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags; import com.android.systemui.statusbar.pipeline.icons.shared.BindableIconsRegistry; import com.android.systemui.statusbar.pipeline.mobile.ui.MobileUiAdapter; +import com.android.systemui.statusbar.pipeline.mobile.ui.MobileUiAdapterKairos; import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsViewModel; import com.android.systemui.statusbar.pipeline.wifi.ui.WifiUiAdapter; import com.android.systemui.statusbar.policy.ConfigurationController; import com.android.systemui.tuner.TunerService; import com.android.systemui.utils.leaks.LeakCheckedTest; +import kotlinx.coroutines.CoroutineScope; + import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -76,7 +80,9 @@ public class StatusBarIconControllerTest extends LeakCheckedTest { public void testSetCalledOnAdd_IconManager() { LinearLayout layout = new LinearLayout(mContext); TestIconManager manager = - new TestIconManager(layout, mMobileUiAdapter, mMobileContextProvider); + new TestIconManager(layout, mMobileUiAdapter, mMobileContextProvider, + mock(MobileUiAdapterKairos.class), mock( + KairosNetwork.class), mock(CoroutineScope.class)); testCallOnAdd_forManager(manager); } @@ -89,7 +95,9 @@ public class StatusBarIconControllerTest extends LeakCheckedTest { mock(WifiUiAdapter.class), mMobileUiAdapter, mMobileContextProvider, - mock(DarkIconDispatcher.class)); + mock(DarkIconDispatcher.class), + mock(MobileUiAdapterKairos.class), mock(KairosNetwork.class), + mock(CoroutineScope.class)); testCallOnAdd_forManager(manager); } @@ -139,12 +147,18 @@ public class StatusBarIconControllerTest extends LeakCheckedTest { WifiUiAdapter wifiUiAdapter, MobileUiAdapter mobileUiAdapter, MobileContextProvider contextProvider, - DarkIconDispatcher darkIconDispatcher) { + DarkIconDispatcher darkIconDispatcher, + MobileUiAdapterKairos mobileUiAdapterKairos, + KairosNetwork kairosNetwork, + CoroutineScope appScope) { super(group, location, wifiUiAdapter, mobileUiAdapter, + () -> mobileUiAdapterKairos, contextProvider, + kairosNetwork, + appScope, darkIconDispatcher); } @@ -167,13 +181,19 @@ public class StatusBarIconControllerTest extends LeakCheckedTest { TestIconManager( ViewGroup group, MobileUiAdapter adapter, - MobileContextProvider contextProvider + MobileContextProvider contextProvider, + MobileUiAdapterKairos adapterKairos, + KairosNetwork kairosNetwork, + CoroutineScope appScope ) { super(group, StatusBarLocation.HOME, mock(WifiUiAdapter.class), adapter, - contextProvider); + () -> adapterKairos, + contextProvider, + kairosNetwork, + appScope); } @Override diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt index 9e914ad0a660..9e914ad0a660 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileIconViewModelKairosTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileIconViewModelKairosTest.kt index 57e63a595b8f..9042ac45cd4d 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileIconViewModelKairosTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileIconViewModelKairosTest.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 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. @@ -19,110 +19,69 @@ package com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase -import com.android.systemui.flags.FakeFeatureFlagsClassic import com.android.systemui.flags.Flags +import com.android.systemui.flags.fake +import com.android.systemui.flags.featureFlagsClassic +import com.android.systemui.kairos.ActivatedKairosFixture +import com.android.systemui.kairos.ExperimentalKairosApi +import com.android.systemui.kairos.KairosTestScope +import com.android.systemui.kairos.kairos +import com.android.systemui.kairos.runKairosTest +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.useUnconfinedTestDispatcher import com.android.systemui.log.table.logcatTableLogBuffer import com.android.systemui.statusbar.connectivity.MobileIconCarrierIdOverridesFake -import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository -import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor +import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.airplaneModeInteractor import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType -import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionRepository -import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository -import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconInteractor -import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconInteractorImpl -import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractor -import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractorImpl +import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel +import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionRepositoryKairos +import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepositoryKairos +import com.android.systemui.statusbar.pipeline.mobile.data.repository.mobileConnectionsRepositoryKairos +import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconInteractorKairos +import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconInteractorKairosImpl +import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractorKairos +import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.mobileIconsInteractorKairos import com.android.systemui.statusbar.pipeline.mobile.domain.model.SignalIconModel import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants -import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository -import com.android.systemui.statusbar.policy.data.repository.FakeUserSetupRepository +import com.android.systemui.statusbar.pipeline.shared.data.repository.connectivityRepository +import com.android.systemui.statusbar.pipeline.shared.data.repository.fake import com.android.systemui.testKosmos -import com.android.systemui.util.CarrierConfigTracker -import com.android.systemui.util.mockito.mock import com.google.common.truth.Truth.assertThat -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.test.TestScope -import kotlinx.coroutines.test.UnconfinedTestDispatcher -import kotlinx.coroutines.test.runTest -import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.mockito.Mock -import org.mockito.MockitoAnnotations +import org.mockito.kotlin.mock -@Suppress("EXPERIMENTAL_IS_NOT_ENABLED") -@OptIn(ExperimentalCoroutinesApi::class) +@OptIn(ExperimentalKairosApi::class) @SmallTest @RunWith(AndroidJUnit4::class) class LocationBasedMobileIconViewModelKairosTest : SysuiTestCase() { - private val kosmos = testKosmos() - - private lateinit var commonImpl: MobileIconViewModelCommonKairos - private lateinit var homeIcon: HomeMobileIconViewModelKairos - private lateinit var qsIcon: QsMobileIconViewModelKairos - private lateinit var keyguardIcon: KeyguardMobileIconViewModelKairos - private lateinit var iconsInteractor: MobileIconsInteractor - private lateinit var interactor: MobileIconInteractor - private val connectionsRepository = kosmos.fakeMobileConnectionsRepository - private lateinit var repository: FakeMobileConnectionRepository - private lateinit var airplaneModeInteractor: AirplaneModeInteractor - - private val connectivityRepository = FakeConnectivityRepository() - private val flags = - FakeFeatureFlagsClassic().also { - it.set(Flags.FILTER_PROVISIONING_NETWORK_SUBSCRIPTIONS, true) - } - @Mock private lateinit var constants: ConnectivityConstants - private val tableLogBuffer = - logcatTableLogBuffer(kosmos, "LocationBasedMobileIconViewModelTest") - @Mock private lateinit var carrierConfigTracker: CarrierConfigTracker - - private val testDispatcher = UnconfinedTestDispatcher() - private val testScope = TestScope(testDispatcher) - - @Before - fun setUp() { - MockitoAnnotations.initMocks(this) - airplaneModeInteractor = - AirplaneModeInteractor( - FakeAirplaneModeRepository(), - FakeConnectivityRepository(), - connectionsRepository, - ) - repository = - FakeMobileConnectionRepository(SUB_1_ID, tableLogBuffer).apply { - isInService.value = true - cdmaLevel.value = 1 - primaryLevel.value = 1 - isEmergencyOnly.value = false - numberOfLevels.value = 4 - resolvedNetworkType.value = ResolvedNetworkType.DefaultNetworkType(lookupKey = "3G") - dataConnectionState.value = DataConnectionState.Connected - } + private val Kosmos.commonImpl: MobileIconViewModelKairosCommon by ActivatedKairosFixture { + MobileIconViewModelKairos( + SUB_1_ID, + interactor, + airplaneModeInteractor, + constants, + featureFlagsClassic, + ) + } - connectionsRepository.activeMobileDataRepository.value = repository + private val Kosmos.homeIcon: HomeMobileIconViewModelKairos by + Kosmos.Fixture { HomeMobileIconViewModelKairos(commonImpl, mock()) } - connectivityRepository.apply { setMobileConnected() } + private val Kosmos.qsIcon: QsMobileIconViewModelKairos by + Kosmos.Fixture { QsMobileIconViewModelKairos(commonImpl) } - iconsInteractor = - MobileIconsInteractorImpl( - connectionsRepository, - carrierConfigTracker, - tableLogBuffer, - connectivityRepository, - FakeUserSetupRepository(), - testScope.backgroundScope, - context, - flags, - ) + private val Kosmos.keyguardIcon: KeyguardMobileIconViewModelKairos by + Kosmos.Fixture { KeyguardMobileIconViewModelKairos(commonImpl) } + + private val Kosmos.iconsInteractor: MobileIconsInteractorKairos + get() = mobileIconsInteractorKairos - interactor = - MobileIconInteractorImpl( - testScope.backgroundScope, + private val Kosmos.interactor: MobileIconInteractorKairos by + Kosmos.Fixture { + MobileIconInteractorKairosImpl( iconsInteractor.activeDataConnectionHasDataEnabled, iconsInteractor.alwaysShowDataRatIcon, iconsInteractor.alwaysUseCdmaLevel, @@ -136,50 +95,74 @@ class LocationBasedMobileIconViewModelKairosTest : SysuiTestCase() { context, MobileIconCarrierIdOverridesFake(), ) + } - commonImpl = - MobileIconViewModelKairos( - SUB_1_ID, - interactor, - airplaneModeInteractor, - constants, - testScope.backgroundScope, - ) - - homeIcon = HomeMobileIconViewModelKairos(commonImpl, mock()) - qsIcon = QsMobileIconViewModelKairos(commonImpl) - keyguardIcon = KeyguardMobileIconViewModelKairos(commonImpl) - } + private val Kosmos.repository: FakeMobileConnectionRepositoryKairos by + Kosmos.Fixture { + FakeMobileConnectionRepositoryKairos(SUB_1_ID, kairos, tableLogBuffer).apply { + isInService.setValue(true) + cdmaLevel.setValue(1) + primaryLevel.setValue(1) + isEmergencyOnly.setValue(false) + numberOfLevels.setValue(4) + resolvedNetworkType.setValue( + ResolvedNetworkType.DefaultNetworkType(lookupKey = "3G") + ) + dataConnectionState.setValue(DataConnectionState.Connected) + } + } - @Test - fun locationBasedViewModelsReceiveSameIconIdWhenCommonImplUpdates() = - testScope.runTest { - var latestHome: SignalIconModel? = null - val homeJob = homeIcon.icon.onEach { latestHome = it }.launchIn(this) + private val Kosmos.constants: ConnectivityConstants by Kosmos.Fixture { mock() } + private val Kosmos.tableLogBuffer by + Kosmos.Fixture { logcatTableLogBuffer(this, "LocationBasedMobileIconViewModelTest") } + + private val kosmos = + testKosmos().apply { + useUnconfinedTestDispatcher() + mobileConnectionsRepositoryKairos = + fakeMobileConnectionsRepositoryKairos.apply { + setActiveMobileDataSubscriptionId(SUB_1_ID) + subscriptions.setValue( + listOf( + SubscriptionModel( + SUB_1_ID, + carrierName = "carrierName", + profileClass = 0, + ) + ) + ) + } + connectivityRepository.fake.apply { setMobileConnected() } + featureFlagsClassic.fake.apply { + set(Flags.FILTER_PROVISIONING_NETWORK_SUBSCRIPTIONS, true) + } + } - var latestQs: SignalIconModel? = null - val qsJob = qsIcon.icon.onEach { latestQs = it }.launchIn(this) + private fun runTest(block: suspend KairosTestScope.() -> Unit) = + kosmos.run { runKairosTest { block() } } - var latestKeyguard: SignalIconModel? = null - val keyguardJob = keyguardIcon.icon.onEach { latestKeyguard = it }.launchIn(this) + @Test + fun locationBasedViewModelsReceiveSameIconIdWhenCommonImplUpdates() = runTest { + repository.dataEnabled.setValue(true) + repository.isInService.setValue(true) - var expected = defaultSignal(level = 1) + val latestHome by homeIcon.icon.collectLastValue() + val latestQs by qsIcon.icon.collectLastValue() + val latestKeyguard by keyguardIcon.icon.collectLastValue() - assertThat(latestHome).isEqualTo(expected) - assertThat(latestQs).isEqualTo(expected) - assertThat(latestKeyguard).isEqualTo(expected) + var expected = defaultSignal(level = 1) - repository.setAllLevels(2) - expected = defaultSignal(level = 2) + assertThat(latestHome).isEqualTo(expected) + assertThat(latestQs).isEqualTo(expected) + assertThat(latestKeyguard).isEqualTo(expected) - assertThat(latestHome).isEqualTo(expected) - assertThat(latestQs).isEqualTo(expected) - assertThat(latestKeyguard).isEqualTo(expected) + repository.setAllLevels(2) + expected = defaultSignal(level = 2) - homeJob.cancel() - qsJob.cancel() - keyguardJob.cancel() - } + assertThat(latestHome).isEqualTo(expected) + assertThat(latestQs).isEqualTo(expected) + assertThat(latestKeyguard).isEqualTo(expected) + } companion object { private const val SUB_1_ID = 1 diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelKairosTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelKairosTest.kt index 6b114a8256f2..68499d1bc57c 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelKairosTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelKairosTest.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 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. @@ -28,1039 +28,893 @@ import com.android.systemui.Flags.FLAG_STATUS_BAR_STATIC_INOUT_INDICATORS import com.android.systemui.SysuiTestCase import com.android.systemui.common.shared.model.ContentDescription import com.android.systemui.common.shared.model.Icon -import com.android.systemui.coroutines.collectLastValue -import com.android.systemui.flags.FakeFeatureFlagsClassic import com.android.systemui.flags.Flags +import com.android.systemui.flags.fake +import com.android.systemui.flags.featureFlagsClassic +import com.android.systemui.kairos.ActivatedKairosFixture +import com.android.systemui.kairos.ExperimentalKairosApi +import com.android.systemui.kairos.KairosTestScope +import com.android.systemui.kairos.kairos +import com.android.systemui.kairos.map +import com.android.systemui.kairos.runKairosTest +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.useUnconfinedTestDispatcher import com.android.systemui.log.table.logcatTableLogBuffer import com.android.systemui.res.R import com.android.systemui.statusbar.connectivity.MobileIconCarrierIdOverridesFake import com.android.systemui.statusbar.core.NewStatusBarIcons import com.android.systemui.statusbar.core.StatusBarRootModernization -import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository -import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor +import com.android.systemui.statusbar.pipeline.airplane.data.repository.airplaneModeRepository +import com.android.systemui.statusbar.pipeline.airplane.data.repository.fake +import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.airplaneModeInteractor import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel -import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionRepository +import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionRepository.Companion.DEFAULT_NETWORK_NAME -import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository -import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository -import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconInteractorImpl -import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractorImpl +import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionRepositoryKairos +import com.android.systemui.statusbar.pipeline.mobile.data.repository.fake +import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepositoryKairos +import com.android.systemui.statusbar.pipeline.mobile.data.repository.mobileConnectionsRepositoryKairos +import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconInteractorKairos +import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconInteractorKairosImpl +import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.mobileIconsInteractorKairos import com.android.systemui.statusbar.pipeline.mobile.domain.model.SignalIconModel import com.android.systemui.statusbar.pipeline.mobile.ui.model.MobileContentDescription -import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel -import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository -import com.android.systemui.statusbar.policy.data.repository.FakeUserSetupRepository +import com.android.systemui.statusbar.pipeline.shared.data.repository.connectivityRepository +import com.android.systemui.statusbar.pipeline.shared.data.repository.fake import com.android.systemui.testKosmos -import com.android.systemui.util.CarrierConfigTracker -import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertWithMessage -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.flow.filterIsInstance -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.test.TestScope -import kotlinx.coroutines.test.UnconfinedTestDispatcher -import kotlinx.coroutines.test.runTest import kotlinx.coroutines.yield -import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.mockito.Mock -import org.mockito.MockitoAnnotations +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.mock +import org.mockito.kotlin.stub -@Suppress("EXPERIMENTAL_IS_NOT_ENABLED") -@OptIn(ExperimentalCoroutinesApi::class) +@OptIn(ExperimentalKairosApi::class) @SmallTest @RunWith(AndroidJUnit4::class) class MobileIconViewModelKairosTest : SysuiTestCase() { - private val kosmos = testKosmos() - - private var connectivityRepository = FakeConnectivityRepository() - - private lateinit var underTest: MobileIconViewModelKairos - private lateinit var interactor: MobileIconInteractorImpl - private lateinit var iconsInteractor: MobileIconsInteractorImpl - private lateinit var repository: FakeMobileConnectionRepository - private lateinit var connectionsRepository: FakeMobileConnectionsRepository - private lateinit var airplaneModeRepository: FakeAirplaneModeRepository - private lateinit var airplaneModeInteractor: AirplaneModeInteractor - @Mock private lateinit var constants: ConnectivityConstants - private val tableLogBuffer = logcatTableLogBuffer(kosmos, "MobileIconViewModelTest") - @Mock private lateinit var carrierConfigTracker: CarrierConfigTracker - - private val flags = - FakeFeatureFlagsClassic().also { - it.set(Flags.FILTER_PROVISIONING_NETWORK_SUBSCRIPTIONS, true) + + private val Kosmos.underTest: MobileIconViewModelKairos by ActivatedKairosFixture { + MobileIconViewModelKairos( + SUB_1_ID, + interactor, + airplaneModeInteractor, + constants, + featureFlagsClassic, + ) + } + private val Kosmos.interactor: MobileIconInteractorKairos by ActivatedKairosFixture { + MobileIconInteractorKairosImpl( + mobileIconsInteractorKairos.activeDataConnectionHasDataEnabled, + mobileIconsInteractorKairos.alwaysShowDataRatIcon, + mobileIconsInteractorKairos.alwaysUseCdmaLevel, + mobileIconsInteractorKairos.isSingleCarrier, + mobileIconsInteractorKairos.mobileIsDefault, + mobileIconsInteractorKairos.defaultMobileIconMapping, + mobileIconsInteractorKairos.defaultMobileIconGroup, + mobileIconsInteractorKairos.isDefaultConnectionFailed, + mobileIconsInteractorKairos.isForceHidden, + repository, + context, + MobileIconCarrierIdOverridesFake(), + ) + } + private val Kosmos.repository: FakeMobileConnectionRepositoryKairos by + Kosmos.Fixture { + FakeMobileConnectionRepositoryKairos(SUB_1_ID, kairos, tableLogBuffer) + .also { + mobileConnectionsRepositoryKairos.fake.setActiveMobileDataSubscriptionId( + SUB_1_ID + ) + mobileConnectionsRepositoryKairos.fake.subscriptions.setValue( + listOf( + SubscriptionModel( + SUB_1_ID, + carrierName = "carrierName", + profileClass = 0, + ) + ) + ) + } + .apply { + isInService.setValue(true) + dataConnectionState.setValue(DataConnectionState.Connected) + dataEnabled.setValue(true) + setNetworkTypeKey(mobileConnectionsRepositoryKairos.fake.GSM_KEY) + } } - private val testDispatcher = UnconfinedTestDispatcher() - private val testScope = TestScope(testDispatcher) - - @Before - fun setUp() { - MockitoAnnotations.initMocks(this) - whenever(constants.hasDataCapabilities).thenReturn(true) - - connectionsRepository = - FakeMobileConnectionsRepository(FakeMobileMappingsProxy(), tableLogBuffer) - - repository = - FakeMobileConnectionRepository(SUB_1_ID, tableLogBuffer).apply { - setNetworkTypeKey(connectionsRepository.GSM_KEY) - isInService.value = true - dataConnectionState.value = DataConnectionState.Connected - dataEnabled.value = true + private val Kosmos.constants: ConnectivityConstants by + Kosmos.Fixture { mock { on { hasDataCapabilities } doReturn true } } + private val Kosmos.tableLogBuffer by + Kosmos.Fixture { logcatTableLogBuffer(this, "MobileIconViewModelKairosTest") } + + private val kosmos = + testKosmos().apply { + useUnconfinedTestDispatcher() + mobileConnectionsRepositoryKairos = + fakeMobileConnectionsRepositoryKairos.apply { mobileIsDefault.setValue(true) } + featureFlagsClassic.fake.apply { + set(Flags.FILTER_PROVISIONING_NETWORK_SUBSCRIPTIONS, true) } - connectionsRepository.activeMobileDataRepository.value = repository - connectionsRepository.mobileIsDefault.value = true - - airplaneModeRepository = FakeAirplaneModeRepository() - airplaneModeInteractor = - AirplaneModeInteractor( - airplaneModeRepository, - connectivityRepository, - kosmos.fakeMobileConnectionsRepository, - ) - - iconsInteractor = - MobileIconsInteractorImpl( - connectionsRepository, - carrierConfigTracker, - tableLogBuffer, - connectivityRepository, - FakeUserSetupRepository(), - testScope.backgroundScope, - context, - flags, - ) + } - interactor = - MobileIconInteractorImpl( - testScope.backgroundScope, - iconsInteractor.activeDataConnectionHasDataEnabled, - iconsInteractor.alwaysShowDataRatIcon, - iconsInteractor.alwaysUseCdmaLevel, - iconsInteractor.isSingleCarrier, - iconsInteractor.mobileIsDefault, - iconsInteractor.defaultMobileIconMapping, - iconsInteractor.defaultMobileIconGroup, - iconsInteractor.isDefaultConnectionFailed, - iconsInteractor.isForceHidden, - repository, - context, - MobileIconCarrierIdOverridesFake(), - ) - createAndSetViewModel() - } + private fun runTest(block: suspend KairosTestScope.() -> Unit) = + kosmos.run { runKairosTest { block() } } @Test - fun isVisible_notDataCapable_alwaysFalse() = - testScope.runTest { - // Create a new view model here so the constants are properly read - whenever(constants.hasDataCapabilities).thenReturn(false) - createAndSetViewModel() - - var latest: Boolean? = null - val job = underTest.isVisible.onEach { latest = it }.launchIn(this) + fun isVisible_notDataCapable_alwaysFalse() = runTest { + // Create a new view model here so the constants are properly read + constants.stub { on { hasDataCapabilities } doReturn false } - assertThat(latest).isFalse() + val latest by underTest.isVisible.collectLastValue() - job.cancel() - } + assertThat(latest).isFalse() + } @Test - fun isVisible_notAirplane_notForceHidden_true() = - testScope.runTest { - var latest: Boolean? = null - val job = underTest.isVisible.onEach { latest = it }.launchIn(this) + fun isVisible_notAirplane_notForceHidden_true() = runTest { + val latest by underTest.isVisible.collectLastValue() - airplaneModeRepository.setIsAirplaneMode(false) + airplaneModeRepository.fake.setIsAirplaneMode(false) - assertThat(latest).isTrue() - - job.cancel() - } + assertThat(latest).isTrue() + } @Test - fun isVisible_airplaneAndNotAllowed_false() = - testScope.runTest { - var latest: Boolean? = null - val job = underTest.isVisible.onEach { latest = it }.launchIn(this) - - airplaneModeRepository.setIsAirplaneMode(true) - repository.isAllowedDuringAirplaneMode.value = false - connectivityRepository.setForceHiddenIcons(setOf()) + fun isVisible_airplaneAndNotAllowed_false() = runTest { + val latest by underTest.isVisible.collectLastValue() - assertThat(latest).isFalse() + airplaneModeRepository.fake.setIsAirplaneMode(true) + repository.isAllowedDuringAirplaneMode.setValue(false) + connectivityRepository.fake.setForceHiddenIcons(setOf()) - job.cancel() - } + assertThat(latest).isEqualTo(false) + } /** Regression test for b/291993542. */ @Test - fun isVisible_airplaneButAllowed_true() = - testScope.runTest { - var latest: Boolean? = null - val job = underTest.isVisible.onEach { latest = it }.launchIn(this) - - airplaneModeRepository.setIsAirplaneMode(true) - repository.isAllowedDuringAirplaneMode.value = true - connectivityRepository.setForceHiddenIcons(setOf()) + fun isVisible_airplaneButAllowed_true() = runTest { + val latest by underTest.isVisible.collectLastValue() - assertThat(latest).isTrue() + airplaneModeRepository.fake.setIsAirplaneMode(true) + repository.isAllowedDuringAirplaneMode.setValue(true) + connectivityRepository.fake.setForceHiddenIcons(setOf()) - job.cancel() - } + assertThat(latest).isTrue() + } @Test - fun isVisible_forceHidden_false() = - testScope.runTest { - var latest: Boolean? = null - val job = underTest.isVisible.onEach { latest = it }.launchIn(this) + fun isVisible_forceHidden_false() = runTest { + val latest by underTest.isVisible.collectLastValue() - airplaneModeRepository.setIsAirplaneMode(false) - connectivityRepository.setForceHiddenIcons(setOf(ConnectivitySlot.MOBILE)) + airplaneModeRepository.fake.setIsAirplaneMode(false) + connectivityRepository.fake.setForceHiddenIcons(setOf(ConnectivitySlot.MOBILE)) - assertThat(latest).isFalse() - - job.cancel() - } + assertThat(latest).isFalse() + } @Test - fun isVisible_respondsToUpdates() = - testScope.runTest { - var latest: Boolean? = null - val job = underTest.isVisible.onEach { latest = it }.launchIn(this) - - airplaneModeRepository.setIsAirplaneMode(false) - connectivityRepository.setForceHiddenIcons(setOf()) + fun isVisible_respondsToUpdates() = runTest { + val latest by underTest.isVisible.collectLastValue() - assertThat(latest).isTrue() + airplaneModeRepository.fake.setIsAirplaneMode(false) + connectivityRepository.fake.setForceHiddenIcons(setOf()) - airplaneModeRepository.setIsAirplaneMode(true) - assertThat(latest).isFalse() + assertThat(latest).isEqualTo(true) - repository.isAllowedDuringAirplaneMode.value = true - assertThat(latest).isTrue() + airplaneModeRepository.fake.setIsAirplaneMode(true) + assertThat(latest).isEqualTo(false) - connectivityRepository.setForceHiddenIcons(setOf(ConnectivitySlot.MOBILE)) - assertThat(latest).isFalse() + repository.isAllowedDuringAirplaneMode.setValue(true) + assertThat(latest).isEqualTo(true) - job.cancel() - } + connectivityRepository.fake.setForceHiddenIcons(setOf(ConnectivitySlot.MOBILE)) + assertThat(latest).isEqualTo(false) + } @Test - fun isVisible_satellite_respectsAirplaneMode() = - testScope.runTest { - val latest by collectLastValue(underTest.isVisible) + fun isVisible_satellite_respectsAirplaneMode() = runTest { + val latest by underTest.isVisible.collectLastValue() - repository.isNonTerrestrial.value = true - airplaneModeInteractor.setIsAirplaneMode(false) + repository.isNonTerrestrial.setValue(true) + airplaneModeInteractor.setIsAirplaneMode(false) - assertThat(latest).isTrue() + assertThat(latest).isTrue() - airplaneModeInteractor.setIsAirplaneMode(true) + airplaneModeInteractor.setIsAirplaneMode(true) - assertThat(latest).isFalse() - } + assertThat(latest).isFalse() + } @Test - fun contentDescription_notInService_usesNoPhone() = - testScope.runTest { - val latest by collectLastValue(underTest.contentDescription) + fun contentDescription_notInService_usesNoPhone() = runTest { + val latest by underTest.contentDescription.collectLastValue() - repository.isInService.value = false + repository.isInService.setValue(false) - assertThat(latest as MobileContentDescription.Cellular) - .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, NO_SIGNAL)) - } + assertThat(latest) + .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, NO_SIGNAL)) + } @Test - fun contentDescription_includesNetworkName() = - testScope.runTest { - val latest by collectLastValue(underTest.contentDescription) + fun contentDescription_includesNetworkName() = runTest { + val latest by underTest.contentDescription.collectLastValue() - repository.isInService.value = true - repository.networkName.value = NetworkNameModel.SubscriptionDerived("Test Network Name") - repository.numberOfLevels.value = 5 - repository.setAllLevels(3) + repository.isInService.setValue(true) + repository.networkName.setValue(NetworkNameModel.SubscriptionDerived("Test Network Name")) + repository.numberOfLevels.setValue(5) + repository.setAllLevels(3) - assertThat(latest as MobileContentDescription.Cellular) - .isEqualTo(MobileContentDescription.Cellular("Test Network Name", THREE_BARS)) - } + assertThat(latest) + .isEqualTo(MobileContentDescription.Cellular("Test Network Name", THREE_BARS)) + } @Test - fun contentDescription_inService_usesLevel() = - testScope.runTest { - val latest by collectLastValue(underTest.contentDescription) + fun contentDescription_inService_usesLevel() = runTest { + val latest by underTest.contentDescription.collectLastValue() - repository.setAllLevels(2) + repository.setAllLevels(2) - assertThat(latest as MobileContentDescription.Cellular) - .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, TWO_BARS)) + assertThat(latest as MobileContentDescription.Cellular) + .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, TWO_BARS)) - repository.setAllLevels(0) + repository.setAllLevels(0) - assertThat(latest as MobileContentDescription.Cellular) - .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, NO_SIGNAL)) - } + assertThat(latest as MobileContentDescription.Cellular) + .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, NO_SIGNAL)) + } @Test - fun contentDescription_nonInflated_invalidLevelUsesNoSignalText() = - testScope.runTest { - val latest by collectLastValue(underTest.contentDescription) + fun contentDescription_nonInflated_invalidLevelUsesNoSignalText() = runTest { + val latest by underTest.contentDescription.collectLastValue() - repository.inflateSignalStrength.value = false - repository.setAllLevels(-1) + repository.inflateSignalStrength.setValue(false) + repository.setAllLevels(-1) - assertThat(latest as MobileContentDescription.Cellular) - .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, NO_SIGNAL)) + assertThat(latest) + .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, NO_SIGNAL)) - repository.setAllLevels(100) + repository.setAllLevels(100) - assertThat(latest as MobileContentDescription.Cellular) - .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, NO_SIGNAL)) - } + assertThat(latest) + .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, NO_SIGNAL)) + } @Test - fun contentDescription_nonInflated_levelStrings() = - testScope.runTest { - val latest by collectLastValue(underTest.contentDescription) + fun contentDescription_nonInflated_levelStrings() = runTest { + val latest by underTest.contentDescription.collectLastValue() - repository.inflateSignalStrength.value = false - repository.setAllLevels(0) + repository.inflateSignalStrength.setValue(false) + repository.setAllLevels(0) - assertThat(latest as MobileContentDescription.Cellular) - .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, NO_SIGNAL)) + assertThat(latest as MobileContentDescription.Cellular) + .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, NO_SIGNAL)) - repository.setAllLevels(1) + repository.setAllLevels(1) - assertThat(latest as MobileContentDescription.Cellular) - .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, ONE_BAR)) + assertThat(latest as MobileContentDescription.Cellular) + .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, ONE_BAR)) - repository.setAllLevels(2) + repository.setAllLevels(2) - assertThat(latest as MobileContentDescription.Cellular) - .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, TWO_BARS)) + assertThat(latest as MobileContentDescription.Cellular) + .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, TWO_BARS)) - repository.setAllLevels(3) + repository.setAllLevels(3) - assertThat(latest as MobileContentDescription.Cellular) - .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, THREE_BARS)) + assertThat(latest as MobileContentDescription.Cellular) + .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, THREE_BARS)) - repository.setAllLevels(4) + repository.setAllLevels(4) - assertThat(latest as MobileContentDescription.Cellular) - .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, FULL_BARS)) - } + assertThat(latest as MobileContentDescription.Cellular) + .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, FULL_BARS)) + } @Test - fun contentDescription_inflated_invalidLevelUsesNoSignalText() = - testScope.runTest { - val latest by collectLastValue(underTest.contentDescription) + fun contentDescription_inflated_invalidLevelUsesNoSignalText() = runTest { + val latest by underTest.contentDescription.collectLastValue() - repository.inflateSignalStrength.value = true - repository.numberOfLevels.value = 6 + repository.inflateSignalStrength.setValue(true) + repository.numberOfLevels.setValue(6) + repository.setAllLevels(-2) - repository.setAllLevels(-2) + assertThat(latest as MobileContentDescription.Cellular) + .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, NO_SIGNAL)) - assertThat(latest as MobileContentDescription.Cellular) - .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, NO_SIGNAL)) + repository.setAllLevels(100) - repository.setAllLevels(100) - - assertThat(latest as MobileContentDescription.Cellular) - .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, NO_SIGNAL)) - } + assertThat(latest as MobileContentDescription.Cellular) + .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, NO_SIGNAL)) + } @Test - fun contentDescription_inflated_levelStrings() = - testScope.runTest { - val latest by collectLastValue(underTest.contentDescription) + fun contentDescription_inflated_levelStrings() = runTest { + val latest by underTest.contentDescription.collectLastValue() - repository.inflateSignalStrength.value = true - repository.numberOfLevels.value = 6 + repository.inflateSignalStrength.setValue(true) + repository.numberOfLevels.setValue(6) - // Note that the _repo_ level is 1 lower than the reported level through the interactor + // Note that the _repo_ level is 1 lower than the reported level through the interactor - repository.setAllLevels(0) + repository.setAllLevels(0) - assertThat(latest as MobileContentDescription.Cellular) - .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, ONE_BAR)) + assertThat(latest) + .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, ONE_BAR)) - repository.setAllLevels(1) + repository.setAllLevels(1) - assertThat(latest as MobileContentDescription.Cellular) - .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, TWO_BARS)) + assertThat(latest) + .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, TWO_BARS)) - repository.setAllLevels(2) + repository.setAllLevels(2) - assertThat(latest as MobileContentDescription.Cellular) - .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, THREE_BARS)) + assertThat(latest) + .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, THREE_BARS)) - repository.setAllLevels(3) + repository.setAllLevels(3) - assertThat(latest as MobileContentDescription.Cellular) - .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, FOUR_BARS)) + assertThat(latest) + .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, FOUR_BARS)) - repository.setAllLevels(4) + repository.setAllLevels(4) - assertThat(latest as MobileContentDescription.Cellular) - .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, FULL_BARS)) - } + assertThat(latest) + .isEqualTo(MobileContentDescription.Cellular(DEFAULT_NETWORK_NAME, FULL_BARS)) + } @Test - fun contentDescription_nonInflated_testABunchOfLevelsForNull() = - testScope.runTest { - val latest by collectLastValue(underTest.contentDescription) - - repository.inflateSignalStrength.value = false - repository.numberOfLevels.value = 5 - - // -1 and 5 are out of the bounds for non-inflated content descriptions - for (i in -1..5) { - repository.setAllLevels(i) - when (i) { - -1, - 5 -> - assertWithMessage("Level $i is expected to be 'no signal'") - .that((latest as MobileContentDescription.Cellular).levelDescriptionRes) - .isEqualTo(NO_SIGNAL) - else -> - assertWithMessage("Level $i is expected not to be null") - .that(latest) - .isNotNull() - } + fun contentDescription_nonInflated_testABunchOfLevelsForNull() = runTest { + val latest by underTest.contentDescription.collectLastValue() + + repository.inflateSignalStrength.setValue(false) + repository.numberOfLevels.setValue(5) + + // -1 and 5 are out of the bounds for non-inflated content descriptions + for (i in -1..5) { + repository.setAllLevels(i) + when (i) { + -1, + 5 -> + assertWithMessage("Level $i is expected to be null") + .that((latest as MobileContentDescription.Cellular).levelDescriptionRes) + .isEqualTo(NO_SIGNAL) + else -> + assertWithMessage("Level $i is expected not to be null") + .that(latest) + .isNotNull() } } + } @Test - fun contentDescription_inflated_testABunchOfLevelsForNull() = - testScope.runTest { - val latest by collectLastValue(underTest.contentDescription) - repository.inflateSignalStrength.value = true - repository.numberOfLevels.value = 6 - // -1 and 6 are out of the bounds for inflated content descriptions - // Note that the interactor adds 1 to the reported level, hence the -2 to 5 range - for (i in -2..5) { - repository.setAllLevels(i) - when (i) { - -2, - 5 -> - assertWithMessage("Level $i is expected to be 'no signal'") - .that((latest as MobileContentDescription.Cellular).levelDescriptionRes) - .isEqualTo(NO_SIGNAL) - else -> - assertWithMessage("Level $i is not expected to be null") - .that(latest) - .isNotNull() - } + fun contentDescription_inflated_testABunchOfLevelsForNull() = runTest { + val latest by underTest.contentDescription.collectLastValue() + repository.inflateSignalStrength.setValue(true) + repository.numberOfLevels.setValue(6) + // -1 and 6 are out of the bounds for inflated content descriptions + // Note that the interactor adds 1 to the reported level, hence the -2 to 5 range + for (i in -2..5) { + repository.setAllLevels(i) + when (i) { + -2, + 5 -> + assertWithMessage("Level $i is expected to be null") + .that((latest as MobileContentDescription.Cellular).levelDescriptionRes) + .isEqualTo(NO_SIGNAL) + else -> + assertWithMessage("Level $i is not expected to be null") + .that(latest) + .isNotNull() } } + } @Test - fun networkType_dataEnabled_groupIsRepresented() = - testScope.runTest { - val expected = - Icon.Resource( - THREE_G.dataType, - ContentDescription.Resource(THREE_G.dataContentDescription), - ) - connectionsRepository.mobileIsDefault.value = true - repository.setNetworkTypeKey(connectionsRepository.GSM_KEY) - - var latest: Icon? = null - val job = underTest.networkTypeIcon.onEach { latest = it }.launchIn(this) - - assertThat(latest).isEqualTo(expected) - - job.cancel() - } - - @Test - fun networkType_null_whenDisabled() = - testScope.runTest { - repository.setNetworkTypeKey(connectionsRepository.GSM_KEY) - repository.setDataEnabled(false) - connectionsRepository.mobileIsDefault.value = true - var latest: Icon? = null - val job = underTest.networkTypeIcon.onEach { latest = it }.launchIn(this) + fun networkType_dataEnabled_groupIsRepresented() = runTest { + val expected = + Icon.Resource( + THREE_G.dataType, + ContentDescription.Resource(THREE_G.dataContentDescription), + ) + mobileConnectionsRepositoryKairos.fake.mobileIsDefault.setValue(true) + repository.setNetworkTypeKey(mobileConnectionsRepositoryKairos.fake.GSM_KEY) - assertThat(latest).isNull() + val latest by underTest.networkTypeIcon.collectLastValue() - job.cancel() - } + assertThat(latest).isEqualTo(expected) + } @Test - fun networkType_null_whenCarrierNetworkChangeActive() = - testScope.runTest { - repository.setNetworkTypeKey(connectionsRepository.GSM_KEY) - repository.carrierNetworkChangeActive.value = true - connectionsRepository.mobileIsDefault.value = true - var latest: Icon? = null - val job = underTest.networkTypeIcon.onEach { latest = it }.launchIn(this) - - assertThat(latest).isNull() + fun networkType_null_whenDisabled() = runTest { + repository.setNetworkTypeKey(mobileConnectionsRepositoryKairos.fake.GSM_KEY) + repository.dataEnabled.setValue(false) + mobileConnectionsRepositoryKairos.fake.mobileIsDefault.setValue(true) + val latest by underTest.networkTypeIcon.collectLastValue() - job.cancel() - } + assertThat(latest).isNull() + } @Test - fun networkTypeIcon_notNull_whenEnabled() = - testScope.runTest { - val expected = - Icon.Resource( - THREE_G.dataType, - ContentDescription.Resource(THREE_G.dataContentDescription), - ) - repository.setNetworkTypeKey(connectionsRepository.GSM_KEY) - repository.setDataEnabled(true) - repository.dataConnectionState.value = DataConnectionState.Connected - connectionsRepository.mobileIsDefault.value = true - var latest: Icon? = null - val job = underTest.networkTypeIcon.onEach { latest = it }.launchIn(this) - - assertThat(latest).isEqualTo(expected) - - job.cancel() - } + fun networkType_null_whenCarrierNetworkChangeActive() = runTest { + repository.setNetworkTypeKey(mobileConnectionsRepositoryKairos.fake.GSM_KEY) + repository.carrierNetworkChangeActive.setValue(true) + mobileConnectionsRepositoryKairos.fake.mobileIsDefault.setValue(true) + val latest by underTest.networkTypeIcon.collectLastValue() + + assertThat(latest).isNull() + } @Test - fun networkType_nullWhenDataDisconnects() = - testScope.runTest { - val initial = - Icon.Resource( - THREE_G.dataType, - ContentDescription.Resource(THREE_G.dataContentDescription), - ) + fun networkTypeIcon_notNull_whenEnabled() = runTest { + val expected = + Icon.Resource( + THREE_G.dataType, + ContentDescription.Resource(THREE_G.dataContentDescription), + ) + repository.setNetworkTypeKey(mobileConnectionsRepositoryKairos.fake.GSM_KEY) + repository.dataEnabled.setValue(true) + repository.dataConnectionState.setValue(DataConnectionState.Connected) + mobileConnectionsRepositoryKairos.fake.mobileIsDefault.setValue(true) + val latest by underTest.networkTypeIcon.collectLastValue() - repository.setNetworkTypeKey(connectionsRepository.GSM_KEY) - var latest: Icon? = null - val job = underTest.networkTypeIcon.onEach { latest = it }.launchIn(this) + assertThat(latest).isEqualTo(expected) + } - assertThat(latest).isEqualTo(initial) + @Test + fun networkType_nullWhenDataDisconnects() = runTest { + val initial = + Icon.Resource( + THREE_G.dataType, + ContentDescription.Resource(THREE_G.dataContentDescription), + ) - repository.dataConnectionState.value = DataConnectionState.Disconnected + repository.setNetworkTypeKey(mobileConnectionsRepositoryKairos.fake.GSM_KEY) + val latest by underTest.networkTypeIcon.collectLastValue() - assertThat(latest).isNull() + assertThat(latest).isEqualTo(initial) - job.cancel() - } + repository.dataConnectionState.setValue(DataConnectionState.Disconnected) - @Test - fun networkType_null_changeToDisabled() = - testScope.runTest { - val expected = - Icon.Resource( - THREE_G.dataType, - ContentDescription.Resource(THREE_G.dataContentDescription), - ) - repository.dataEnabled.value = true - var latest: Icon? = null - val job = underTest.networkTypeIcon.onEach { latest = it }.launchIn(this) + assertThat(latest).isNull() + } - assertThat(latest).isEqualTo(expected) + @Test + fun networkType_null_changeToDisabled() = runTest { + val expected = + Icon.Resource( + THREE_G.dataType, + ContentDescription.Resource(THREE_G.dataContentDescription), + ) + repository.dataEnabled.setValue(true) + val latest by underTest.networkTypeIcon.collectLastValue() - repository.dataEnabled.value = false + assertThat(latest).isEqualTo(expected) - assertThat(latest).isNull() + repository.dataEnabled.setValue(false) - job.cancel() - } + assertThat(latest).isNull() + } @Test - fun networkType_alwaysShow_shownEvenWhenDisabled() = - testScope.runTest { - repository.dataEnabled.value = false - - connectionsRepository.defaultDataSubRatConfig.value = - MobileMappings.Config().also { it.alwaysShowDataRatIcon = true } + fun networkType_alwaysShow_shownEvenWhenDisabled() = runTest { + repository.dataEnabled.setValue(false) - var latest: Icon? = null - val job = underTest.networkTypeIcon.onEach { latest = it }.launchIn(this) + mobileConnectionsRepositoryKairos.fake.defaultDataSubRatConfig.setValue( + MobileMappings.Config().also { it.alwaysShowDataRatIcon = true } + ) - val expected = - Icon.Resource( - THREE_G.dataType, - ContentDescription.Resource(THREE_G.dataContentDescription), - ) - assertThat(latest).isEqualTo(expected) + val latest by underTest.networkTypeIcon.collectLastValue() - job.cancel() - } + val expected = + Icon.Resource( + THREE_G.dataType, + ContentDescription.Resource(THREE_G.dataContentDescription), + ) + assertThat(latest).isEqualTo(expected) + } @Test - fun networkType_alwaysShow_shownEvenWhenDisconnected() = - testScope.runTest { - repository.setNetworkTypeKey(connectionsRepository.GSM_KEY) - repository.dataConnectionState.value = DataConnectionState.Disconnected + fun networkType_alwaysShow_shownEvenWhenDisconnected() = runTest { + repository.setNetworkTypeKey(mobileConnectionsRepositoryKairos.fake.GSM_KEY) + repository.dataConnectionState.setValue(DataConnectionState.Disconnected) - connectionsRepository.defaultDataSubRatConfig.value = - MobileMappings.Config().also { it.alwaysShowDataRatIcon = true } + mobileConnectionsRepositoryKairos.fake.defaultDataSubRatConfig.setValue( + MobileMappings.Config().also { it.alwaysShowDataRatIcon = true } + ) - var latest: Icon? = null - val job = underTest.networkTypeIcon.onEach { latest = it }.launchIn(this) + val latest by underTest.networkTypeIcon.collectLastValue() - val expected = - Icon.Resource( - THREE_G.dataType, - ContentDescription.Resource(THREE_G.dataContentDescription), - ) - assertThat(latest).isEqualTo(expected) - - job.cancel() - } + val expected = + Icon.Resource( + THREE_G.dataType, + ContentDescription.Resource(THREE_G.dataContentDescription), + ) + assertThat(latest).isEqualTo(expected) + } @Test - fun networkType_alwaysShow_shownEvenWhenFailedConnection() = - testScope.runTest { - repository.setNetworkTypeKey(connectionsRepository.GSM_KEY) - connectionsRepository.mobileIsDefault.value = true - connectionsRepository.defaultDataSubRatConfig.value = - MobileMappings.Config().also { it.alwaysShowDataRatIcon = true } - - var latest: Icon? = null - val job = underTest.networkTypeIcon.onEach { latest = it }.launchIn(this) - - val expected = - Icon.Resource( - THREE_G.dataType, - ContentDescription.Resource(THREE_G.dataContentDescription), - ) - assertThat(latest).isEqualTo(expected) - - job.cancel() - } + fun networkType_alwaysShow_shownEvenWhenFailedConnection() = runTest { + repository.setNetworkTypeKey(mobileConnectionsRepositoryKairos.fake.GSM_KEY) + mobileConnectionsRepositoryKairos.fake.mobileIsDefault.setValue(true) + mobileConnectionsRepositoryKairos.fake.defaultDataSubRatConfig.setValue( + MobileMappings.Config().also { it.alwaysShowDataRatIcon = true } + ) - @Test - fun networkType_alwaysShow_usesDefaultIconWhenInvalid() = - testScope.runTest { - // The UNKNOWN icon group doesn't have a valid data type icon ID, and the logic from the - // old pipeline was to use the default icon group if the map doesn't exist - repository.setNetworkTypeKey(UNKNOWN.name) - connectionsRepository.defaultDataSubRatConfig.value = - MobileMappings.Config().also { it.alwaysShowDataRatIcon = true } - - var latest: Icon? = null - val job = underTest.networkTypeIcon.onEach { latest = it }.launchIn(this) - - val expected = - Icon.Resource( - connectionsRepository.defaultMobileIconGroup.value.dataType, - ContentDescription.Resource(G.dataContentDescription), - ) - - assertThat(latest).isEqualTo(expected) - - job.cancel() - } + val latest by underTest.networkTypeIcon.collectLastValue() - @Test - fun networkType_alwaysShow_shownWhenNotDefault() = - testScope.runTest { - repository.setNetworkTypeKey(connectionsRepository.GSM_KEY) - connectionsRepository.mobileIsDefault.value = false - connectionsRepository.defaultDataSubRatConfig.value = - MobileMappings.Config().also { it.alwaysShowDataRatIcon = true } - - var latest: Icon? = null - val job = underTest.networkTypeIcon.onEach { latest = it }.launchIn(this) - - val expected = - Icon.Resource( - THREE_G.dataType, - ContentDescription.Resource(THREE_G.dataContentDescription), - ) - assertThat(latest).isEqualTo(expected) - - job.cancel() - } + val expected = + Icon.Resource( + THREE_G.dataType, + ContentDescription.Resource(THREE_G.dataContentDescription), + ) + assertThat(latest).isEqualTo(expected) + } @Test - fun networkType_notShownWhenNotDefault() = - testScope.runTest { - repository.setNetworkTypeKey(connectionsRepository.GSM_KEY) - repository.dataConnectionState.value = DataConnectionState.Connected - connectionsRepository.mobileIsDefault.value = false + fun networkType_alwaysShow_usesDefaultIconWhenInvalid() = runTest { + // The UNKNOWN icon group doesn't have a valid data type icon ID, and the logic from the + // old pipeline was to use the default icon group if the map doesn't exist + repository.setNetworkTypeKey(UNKNOWN.name) + mobileConnectionsRepositoryKairos.fake.defaultDataSubRatConfig.setValue( + MobileMappings.Config().also { it.alwaysShowDataRatIcon = true } + ) - var latest: Icon? = null - val job = underTest.networkTypeIcon.onEach { latest = it }.launchIn(this) + val latest by underTest.networkTypeIcon.collectLastValue() - assertThat(latest).isNull() + val expected = + Icon.Resource( + kairos.transact { + mobileConnectionsRepositoryKairos.fake.defaultMobileIconGroup.sample().dataType + }, + ContentDescription.Resource(G.dataContentDescription), + ) - job.cancel() - } + assertThat(latest).isEqualTo(expected) + } @Test - fun roaming() = - testScope.runTest { - repository.setAllRoaming(true) + fun networkType_alwaysShow_shownWhenNotDefault() = runTest { + repository.setNetworkTypeKey(mobileConnectionsRepositoryKairos.fake.GSM_KEY) + mobileConnectionsRepositoryKairos.fake.mobileIsDefault.setValue(false) + mobileConnectionsRepositoryKairos.fake.defaultDataSubRatConfig.setValue( + MobileMappings.Config().also { it.alwaysShowDataRatIcon = true } + ) - var latest: Boolean? = null - val job = underTest.roaming.onEach { latest = it }.launchIn(this) + val latest by underTest.networkTypeIcon.collectLastValue() - assertThat(latest).isTrue() + val expected = + Icon.Resource( + THREE_G.dataType, + ContentDescription.Resource(THREE_G.dataContentDescription), + ) + assertThat(latest).isEqualTo(expected) + } - repository.setAllRoaming(false) + @Test + fun networkType_notShownWhenNotDefault() = runTest { + repository.setNetworkTypeKey(mobileConnectionsRepositoryKairos.fake.GSM_KEY) + repository.dataConnectionState.setValue(DataConnectionState.Connected) + mobileConnectionsRepositoryKairos.fake.mobileIsDefault.setValue(false) - assertThat(latest).isFalse() + val latest by underTest.networkTypeIcon.collectLastValue() - job.cancel() - } + assertThat(latest).isNull() + } @Test - fun dataActivity_nullWhenConfigIsOff() = - testScope.runTest { - // Create a new view model here so the constants are properly read - whenever(constants.shouldShowActivityConfig).thenReturn(false) - createAndSetViewModel() + fun roaming() = runTest { + repository.setAllRoaming(true) - var inVisible: Boolean? = null - val inJob = underTest.activityInVisible.onEach { inVisible = it }.launchIn(this) + val latest by underTest.roaming.collectLastValue() - var outVisible: Boolean? = null - val outJob = underTest.activityInVisible.onEach { outVisible = it }.launchIn(this) + assertThat(latest).isTrue() - var containerVisible: Boolean? = null - val containerJob = - underTest.activityInVisible.onEach { containerVisible = it }.launchIn(this) + repository.setAllRoaming(false) - repository.dataActivityDirection.value = - DataActivityModel(hasActivityIn = true, hasActivityOut = true) + assertThat(latest).isFalse() + } - assertThat(inVisible).isFalse() - assertThat(outVisible).isFalse() - assertThat(containerVisible).isFalse() + @Test + fun dataActivity_nullWhenConfigIsOff() = runTest { + constants.stub { on { shouldShowActivityConfig } doReturn false } - inJob.cancel() - outJob.cancel() - containerJob.cancel() - } + val inVisible by underTest.activityInVisible.collectLastValue() + + val outVisible by underTest.activityInVisible.collectLastValue() + + val containerVisible by underTest.activityInVisible.collectLastValue() + + repository.dataActivityDirection.setValue( + DataActivityModel(hasActivityIn = true, hasActivityOut = true) + ) + + assertThat(inVisible).isFalse() + assertThat(outVisible).isFalse() + assertThat(containerVisible).isFalse() + } @Test @DisableFlags(FLAG_STATUS_BAR_STATIC_INOUT_INDICATORS) - fun dataActivity_configOn_testIndicators_staticFlagOff() = - testScope.runTest { - // Create a new view model here so the constants are properly read - whenever(constants.shouldShowActivityConfig).thenReturn(true) - createAndSetViewModel() + fun dataActivity_configOn_testIndicators_staticFlagOff() = runTest { + constants.stub { on { shouldShowActivityConfig } doReturn true } - var inVisible: Boolean? = null - val inJob = underTest.activityInVisible.onEach { inVisible = it }.launchIn(this) + val inVisible by underTest.activityInVisible.collectLastValue() - var outVisible: Boolean? = null - val outJob = underTest.activityOutVisible.onEach { outVisible = it }.launchIn(this) + val outVisible by underTest.activityOutVisible.collectLastValue() - var containerVisible: Boolean? = null - val containerJob = - underTest.activityContainerVisible.onEach { containerVisible = it }.launchIn(this) + val containerVisible by underTest.activityContainerVisible.collectLastValue() - repository.dataActivityDirection.value = - DataActivityModel(hasActivityIn = true, hasActivityOut = false) + repository.dataActivityDirection.setValue( + DataActivityModel(hasActivityIn = true, hasActivityOut = false) + ) - yield() + yield() - assertThat(inVisible).isTrue() - assertThat(outVisible).isFalse() - assertThat(containerVisible).isTrue() + assertThat(inVisible).isTrue() + assertThat(outVisible).isFalse() + assertThat(containerVisible).isTrue() - repository.dataActivityDirection.value = - DataActivityModel(hasActivityIn = false, hasActivityOut = true) + repository.dataActivityDirection.setValue( + DataActivityModel(hasActivityIn = false, hasActivityOut = true) + ) - assertThat(inVisible).isFalse() - assertThat(outVisible).isTrue() - assertThat(containerVisible).isTrue() + assertThat(inVisible).isFalse() + assertThat(outVisible).isTrue() + assertThat(containerVisible).isTrue() - repository.dataActivityDirection.value = - DataActivityModel(hasActivityIn = false, hasActivityOut = false) + repository.dataActivityDirection.setValue( + DataActivityModel(hasActivityIn = false, hasActivityOut = false) + ) - assertThat(inVisible).isFalse() - assertThat(outVisible).isFalse() - assertThat(containerVisible).isFalse() - - inJob.cancel() - outJob.cancel() - containerJob.cancel() - } + assertThat(inVisible).isFalse() + assertThat(outVisible).isFalse() + assertThat(containerVisible).isFalse() + } @Test @EnableFlags(FLAG_STATUS_BAR_STATIC_INOUT_INDICATORS) - fun dataActivity_configOn_testIndicators_staticFlagOn() = - testScope.runTest { - // Create a new view model here so the constants are properly read - whenever(constants.shouldShowActivityConfig).thenReturn(true) - createAndSetViewModel() - - var inVisible: Boolean? = null - val inJob = underTest.activityInVisible.onEach { inVisible = it }.launchIn(this) + fun dataActivity_configOn_testIndicators_staticFlagOn() = runTest { + constants.stub { on { shouldShowActivityConfig } doReturn true } - var outVisible: Boolean? = null - val outJob = underTest.activityOutVisible.onEach { outVisible = it }.launchIn(this) + val inVisible by underTest.activityInVisible.collectLastValue() - var containerVisible: Boolean? = null - val containerJob = - underTest.activityContainerVisible.onEach { containerVisible = it }.launchIn(this) + val outVisible by underTest.activityOutVisible.collectLastValue() - repository.dataActivityDirection.value = - DataActivityModel(hasActivityIn = true, hasActivityOut = false) + val containerVisible by underTest.activityContainerVisible.collectLastValue() - yield() + repository.dataActivityDirection.setValue( + DataActivityModel(hasActivityIn = true, hasActivityOut = false) + ) - assertThat(inVisible).isTrue() - assertThat(outVisible).isFalse() - assertThat(containerVisible).isTrue() + yield() - repository.dataActivityDirection.value = - DataActivityModel(hasActivityIn = false, hasActivityOut = true) + assertThat(inVisible).isTrue() + assertThat(outVisible).isFalse() + assertThat(containerVisible).isTrue() - assertThat(inVisible).isFalse() - assertThat(outVisible).isTrue() - assertThat(containerVisible).isTrue() + repository.dataActivityDirection.setValue( + DataActivityModel(hasActivityIn = false, hasActivityOut = true) + ) - repository.dataActivityDirection.value = - DataActivityModel(hasActivityIn = false, hasActivityOut = false) + assertThat(inVisible).isFalse() + assertThat(outVisible).isTrue() + assertThat(containerVisible).isTrue() - assertThat(inVisible).isFalse() - assertThat(outVisible).isFalse() - assertThat(containerVisible).isTrue() + repository.dataActivityDirection.setValue( + DataActivityModel(hasActivityIn = false, hasActivityOut = false) + ) - inJob.cancel() - outJob.cancel() - containerJob.cancel() - } + assertThat(inVisible).isFalse() + assertThat(outVisible).isFalse() + assertThat(containerVisible).isTrue() + } @Test - fun netTypeBackground_nullWhenNoPrioritizedCapabilities() = - testScope.runTest { - createAndSetViewModel() - - val latest by collectLastValue(underTest.networkTypeBackground) + fun netTypeBackground_nullWhenNoPrioritizedCapabilities() = runTest { + val latest by underTest.networkTypeBackground.collectLastValue() - repository.hasPrioritizedNetworkCapabilities.value = false + repository.hasPrioritizedNetworkCapabilities.setValue(false) - assertThat(latest).isNull() - } + assertThat(latest).isNull() + } @Test @EnableFlags(NewStatusBarIcons.FLAG_NAME, StatusBarRootModernization.FLAG_NAME) - fun netTypeBackground_sliceUiEnabled_notNullWhenPrioritizedCapabilities_newIcons() = - testScope.runTest { - createAndSetViewModel() + fun netTypeBackground_sliceUiEnabled_notNullWhenPrioritizedCapabilities_newIcons() = runTest { + val latest by underTest.networkTypeBackground.collectLastValue() - val latest by collectLastValue(underTest.networkTypeBackground) + repository.hasPrioritizedNetworkCapabilities.setValue(true) - repository.hasPrioritizedNetworkCapabilities.value = true - - assertThat(latest) - .isEqualTo(Icon.Resource(R.drawable.mobile_network_type_background_updated, null)) - } + assertThat(latest) + .isEqualTo(Icon.Resource(R.drawable.mobile_network_type_background_updated, null)) + } @Test @DisableFlags(NewStatusBarIcons.FLAG_NAME, StatusBarRootModernization.FLAG_NAME) - fun netTypeBackground_sliceUiDisabled_notNullWhenPrioritizedCapabilities_oldIcons() = - testScope.runTest { - createAndSetViewModel() - - val latest by collectLastValue(underTest.networkTypeBackground) + fun netTypeBackground_sliceUiDisabled_notNullWhenPrioritizedCapabilities_oldIcons() = runTest { + val latest by underTest.networkTypeBackground.collectLastValue() - repository.hasPrioritizedNetworkCapabilities.value = true + repository.allowNetworkSliceIndicator.setValue(true) + repository.hasPrioritizedNetworkCapabilities.setValue(true) - assertThat(latest) - .isEqualTo(Icon.Resource(R.drawable.mobile_network_type_background, null)) - } + assertThat(latest).isEqualTo(Icon.Resource(R.drawable.mobile_network_type_background, null)) + } @Test - fun nonTerrestrial_defaultProperties() = - testScope.runTest { - repository.isNonTerrestrial.value = true - - val roaming by collectLastValue(underTest.roaming) - val networkTypeIcon by collectLastValue(underTest.networkTypeIcon) - val networkTypeBackground by collectLastValue(underTest.networkTypeBackground) - val activityInVisible by collectLastValue(underTest.activityInVisible) - val activityOutVisible by collectLastValue(underTest.activityOutVisible) - val activityContainerVisible by collectLastValue(underTest.activityContainerVisible) - - assertThat(roaming).isFalse() - assertThat(networkTypeIcon).isNull() - assertThat(networkTypeBackground).isNull() - assertThat(activityInVisible).isFalse() - assertThat(activityOutVisible).isFalse() - assertThat(activityContainerVisible).isFalse() - } + fun nonTerrestrial_defaultProperties() = runTest { + repository.isNonTerrestrial.setValue(true) + + val roaming by underTest.roaming.collectLastValue() + val networkTypeIcon by underTest.networkTypeIcon.collectLastValue() + val networkTypeBackground by underTest.networkTypeBackground.collectLastValue() + val activityInVisible by underTest.activityInVisible.collectLastValue() + val activityOutVisible by underTest.activityOutVisible.collectLastValue() + val activityContainerVisible by underTest.activityContainerVisible.collectLastValue() + + assertThat(roaming).isFalse() + assertThat(networkTypeIcon).isNull() + assertThat(networkTypeBackground).isNull() + assertThat(activityInVisible).isFalse() + assertThat(activityOutVisible).isFalse() + assertThat(activityContainerVisible).isFalse() + } @Test - fun nonTerrestrial_ignoresDefaultProperties() = - testScope.runTest { - repository.isNonTerrestrial.value = true - - val roaming by collectLastValue(underTest.roaming) - val networkTypeIcon by collectLastValue(underTest.networkTypeIcon) - val networkTypeBackground by collectLastValue(underTest.networkTypeBackground) - val activityInVisible by collectLastValue(underTest.activityInVisible) - val activityOutVisible by collectLastValue(underTest.activityOutVisible) - val activityContainerVisible by collectLastValue(underTest.activityContainerVisible) - - repository.setAllRoaming(true) - repository.setNetworkTypeKey(connectionsRepository.LTE_KEY) - // sets the background on cellular - repository.hasPrioritizedNetworkCapabilities.value = true - repository.dataActivityDirection.value = - DataActivityModel(hasActivityIn = true, hasActivityOut = true) - - assertThat(roaming).isFalse() - assertThat(networkTypeIcon).isNull() - assertThat(networkTypeBackground).isNull() - assertThat(activityInVisible).isFalse() - assertThat(activityOutVisible).isFalse() - assertThat(activityContainerVisible).isFalse() - } + fun nonTerrestrial_ignoresDefaultProperties() = runTest { + repository.isNonTerrestrial.setValue(true) + + val roaming by underTest.roaming.collectLastValue() + val networkTypeIcon by underTest.networkTypeIcon.collectLastValue() + val networkTypeBackground by underTest.networkTypeBackground.collectLastValue() + val activityInVisible by underTest.activityInVisible.collectLastValue() + val activityOutVisible by underTest.activityOutVisible.collectLastValue() + val activityContainerVisible by underTest.activityContainerVisible.collectLastValue() + + repository.setAllRoaming(true) + repository.setNetworkTypeKey(mobileConnectionsRepositoryKairos.fake.LTE_KEY) + // sets the background on cellular + repository.hasPrioritizedNetworkCapabilities.setValue(true) + repository.dataActivityDirection.setValue( + DataActivityModel(hasActivityIn = true, hasActivityOut = true) + ) + + assertThat(roaming).isFalse() + assertThat(networkTypeIcon).isNull() + assertThat(networkTypeBackground).isNull() + assertThat(activityInVisible).isFalse() + assertThat(activityOutVisible).isFalse() + assertThat(activityContainerVisible).isFalse() + } @DisableFlags(com.android.internal.telephony.flags.Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN) @Test - fun nonTerrestrial_usesSatelliteIcon_flagOff() = - testScope.runTest { - repository.isNonTerrestrial.value = true - repository.setAllLevels(0) - repository.satelliteLevel.value = 0 - - val latest by - collectLastValue(underTest.icon.filterIsInstance(SignalIconModel.Satellite::class)) - - // Level 0 -> no connection - assertThat(latest).isNotNull() - assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_0) - - // 1-2 -> 1 bar - repository.setAllLevels(1) - repository.satelliteLevel.value = 1 - assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_1) - - repository.setAllLevels(2) - repository.satelliteLevel.value = 2 - assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_1) - - // 3-4 -> 2 bars - repository.setAllLevels(3) - repository.satelliteLevel.value = 3 - assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_2) - - repository.setAllLevels(4) - repository.satelliteLevel.value = 4 - assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_2) - } + fun nonTerrestrial_usesSatelliteIcon_flagOff() = runTest { + repository.isNonTerrestrial.setValue(true) + repository.setAllLevels(0) + repository.satelliteLevel.setValue(0) + + val latest by underTest.icon.map { it as SignalIconModel.Satellite }.collectLastValue() + + // Level 0 -> no connection + assertThat(latest).isNotNull() + assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_0) + + // 1-2 -> 1 bar + repository.setAllLevels(1) + repository.satelliteLevel.setValue(1) + assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_1) + + repository.setAllLevels(2) + repository.satelliteLevel.setValue(2) + assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_1) + + // 3-4 -> 2 bars + repository.setAllLevels(3) + repository.satelliteLevel.setValue(3) + assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_2) + + repository.setAllLevels(4) + repository.satelliteLevel.setValue(4) + assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_2) + } @EnableFlags(com.android.internal.telephony.flags.Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN) @Test - fun nonTerrestrial_usesSatelliteIcon_flagOn() = - testScope.runTest { - repository.isNonTerrestrial.value = true - repository.satelliteLevel.value = 0 + fun nonTerrestrial_usesSatelliteIcon_flagOn() = runTest { + repository.isNonTerrestrial.setValue(true) + repository.satelliteLevel.setValue(0) - val latest by - collectLastValue(underTest.icon.filterIsInstance(SignalIconModel.Satellite::class)) + val latest by underTest.icon.map { it as SignalIconModel.Satellite }.collectLastValue() - // Level 0 -> no connection - assertThat(latest).isNotNull() - assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_0) + // Level 0 -> no connection + assertThat(latest).isNotNull() + assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_0) - // 1-2 -> 1 bar - repository.satelliteLevel.value = 1 - assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_1) + // 1-2 -> 1 bar + repository.satelliteLevel.setValue(1) + assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_1) - repository.satelliteLevel.value = 2 - assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_1) + repository.satelliteLevel.setValue(2) + assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_1) - // 3-4 -> 2 bars - repository.satelliteLevel.value = 3 - assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_2) + // 3-4 -> 2 bars + repository.satelliteLevel.setValue(3) + assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_2) - repository.satelliteLevel.value = 4 - assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_2) - } + repository.satelliteLevel.setValue(4) + assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_2) + } @DisableFlags(com.android.internal.telephony.flags.Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN) @Test - fun satelliteIcon_ignoresInflateSignalStrength_flagOff() = - testScope.runTest { - // Note that this is the exact same test as above, but with inflateSignalStrength set to - // true we note that the level is unaffected by inflation - repository.inflateSignalStrength.value = true - repository.isNonTerrestrial.value = true - repository.setAllLevels(0) - repository.satelliteLevel.value = 0 - - val latest by - collectLastValue(underTest.icon.filterIsInstance(SignalIconModel.Satellite::class)) - - // Level 0 -> no connection - assertThat(latest).isNotNull() - assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_0) - - // 1-2 -> 1 bar - repository.setAllLevels(1) - repository.satelliteLevel.value = 1 - assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_1) - - repository.setAllLevels(2) - repository.satelliteLevel.value = 2 - assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_1) - - // 3-4 -> 2 bars - repository.setAllLevels(3) - repository.satelliteLevel.value = 3 - assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_2) - - repository.setAllLevels(4) - repository.satelliteLevel.value = 4 - assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_2) - } + fun satelliteIcon_ignoresInflateSignalStrength_flagOff() = runTest { + // Note that this is the exact same test as above, but with inflateSignalStrength set to + // true we note that the level is unaffected by inflation + repository.inflateSignalStrength.setValue(true) + repository.isNonTerrestrial.setValue(true) + repository.setAllLevels(0) + repository.satelliteLevel.setValue(0) + + val latest by underTest.icon.map { it as SignalIconModel.Satellite }.collectLastValue() + + // Level 0 -> no connection + assertThat(latest).isNotNull() + assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_0) + + // 1-2 -> 1 bar + repository.setAllLevels(1) + repository.satelliteLevel.setValue(1) + assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_1) + + repository.setAllLevels(2) + repository.satelliteLevel.setValue(2) + assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_1) + + // 3-4 -> 2 bars + repository.setAllLevels(3) + repository.satelliteLevel.setValue(3) + assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_2) + + repository.setAllLevels(4) + repository.satelliteLevel.setValue(4) + assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_2) + } @EnableFlags(com.android.internal.telephony.flags.Flags.FLAG_CARRIER_ROAMING_NB_IOT_NTN) @Test - fun satelliteIcon_ignoresInflateSignalStrength_flagOn() = - testScope.runTest { - // Note that this is the exact same test as above, but with inflateSignalStrength set to - // true we note that the level is unaffected by inflation - repository.inflateSignalStrength.value = true - repository.isNonTerrestrial.value = true - repository.satelliteLevel.value = 0 - - val latest by - collectLastValue(underTest.icon.filterIsInstance(SignalIconModel.Satellite::class)) - - // Level 0 -> no connection - assertThat(latest).isNotNull() - assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_0) - - // 1-2 -> 1 bar - repository.satelliteLevel.value = 1 - assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_1) - - repository.satelliteLevel.value = 2 - assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_1) - - // 3-4 -> 2 bars - repository.satelliteLevel.value = 3 - assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_2) - - repository.satelliteLevel.value = 4 - assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_2) - } + fun satelliteIcon_ignoresInflateSignalStrength_flagOn() = runTest { + // Note that this is the exact same test as above, but with inflateSignalStrength set to + // true we note that the level is unaffected by inflation + repository.inflateSignalStrength.setValue(true) + repository.isNonTerrestrial.setValue(true) + repository.satelliteLevel.setValue(0) - private fun createAndSetViewModel() { - underTest = - MobileIconViewModelKairos( - SUB_1_ID, - interactor, - airplaneModeInteractor, - constants, - testScope.backgroundScope, - ) + val latest by underTest.icon.map { it as SignalIconModel.Satellite }.collectLastValue() + + // Level 0 -> no connection + assertThat(latest).isNotNull() + assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_0) + + // 1-2 -> 1 bar + repository.satelliteLevel.setValue(1) + assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_1) + + repository.satelliteLevel.setValue(2) + assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_1) + + // 3-4 -> 2 bars + repository.satelliteLevel.setValue(3) + assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_2) + + repository.satelliteLevel.setValue(4) + assertThat(latest!!.icon.res).isEqualTo(R.drawable.ic_satellite_connected_2) } companion object { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelKairosTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelKairosTest.kt index e921430394c2..b11bad6f3ad3 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelKairosTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelKairosTest.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 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. @@ -16,348 +16,323 @@ package com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel +import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID import android.telephony.SubscriptionManager.PROFILE_CLASS_UNSET import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.settingslib.mobile.TelephonyIcons import com.android.systemui.SysuiTestCase -import com.android.systemui.flags.FakeFeatureFlagsClassic -import com.android.systemui.statusbar.phone.StatusBarLocation -import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository -import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor +import com.android.systemui.flags.Flags +import com.android.systemui.flags.fake +import com.android.systemui.flags.featureFlagsClassic +import com.android.systemui.kairos.ExperimentalKairosApi +import com.android.systemui.kairos.KairosTestScope +import com.android.systemui.kairos.runKairosTest +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.useUnconfinedTestDispatcher +import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState +import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType.DefaultNetworkType +import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType.UnknownNetworkType import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel -import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepository -import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.FakeMobileIconsInteractor -import com.android.systemui.statusbar.pipeline.mobile.domain.model.NetworkTypeIconModel -import com.android.systemui.statusbar.pipeline.mobile.ui.MobileViewLogger -import com.android.systemui.statusbar.pipeline.mobile.ui.VerboseMobileViewLogger -import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy -import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants -import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository +import com.android.systemui.statusbar.pipeline.mobile.data.repository.fake +import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepositoryKairos +import com.android.systemui.statusbar.pipeline.mobile.data.repository.mobileConnectionsRepositoryKairos import com.android.systemui.testKosmos -import com.android.systemui.util.mockito.mock import com.google.common.truth.Truth.assertThat -import junit.framework.Assert.assertFalse -import junit.framework.Assert.assertTrue -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.isActive -import kotlinx.coroutines.test.TestScope -import kotlinx.coroutines.test.UnconfinedTestDispatcher -import kotlinx.coroutines.test.runTest -import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.mockito.Mock -import org.mockito.MockitoAnnotations -@Suppress("EXPERIMENTAL_IS_NOT_ENABLED") -@OptIn(ExperimentalCoroutinesApi::class) +@OptIn(ExperimentalKairosApi::class) @SmallTest @RunWith(AndroidJUnit4::class) class MobileIconsViewModelKairosTest : SysuiTestCase() { - private val kosmos = testKosmos() - private lateinit var underTest: MobileIconsViewModelKairos - private val interactor = FakeMobileIconsInteractor(FakeMobileMappingsProxy(), mock()) - private val flags = FakeFeatureFlagsClassic() - - private lateinit var airplaneModeInteractor: AirplaneModeInteractor - @Mock private lateinit var constants: ConnectivityConstants - @Mock private lateinit var logger: MobileViewLogger - @Mock private lateinit var verboseLogger: VerboseMobileViewLogger - - private val testDispatcher = UnconfinedTestDispatcher() - private val testScope = TestScope(testDispatcher) + private val Kosmos.underTest + get() = mobileIconsViewModelKairos + + private val kosmos = + testKosmos().apply { + useUnconfinedTestDispatcher() + featureFlagsClassic.fake.apply { + setDefault(Flags.FILTER_PROVISIONING_NETWORK_SUBSCRIPTIONS) + } + mobileConnectionsRepositoryKairos = + fakeMobileConnectionsRepositoryKairos.apply { + val subList = listOf(SUB_1, SUB_2) + setActiveMobileDataSubscriptionId(SUB_1.subscriptionId) + subscriptions.setValue(subList) + } + } - @Before - fun setUp() { - MockitoAnnotations.initMocks(this) + private fun runTest(block: suspend KairosTestScope.() -> Unit) = + kosmos.run { runKairosTest { block() } } + + private fun KairosTestScope.setSubscriptions( + subList: List<SubscriptionModel>, + activeSubId: Int = subList.getOrNull(0)?.subscriptionId ?: INVALID_SUBSCRIPTION_ID, + ) { + println("setSubscriptions: mobileConnectionsRepositoryKairos.fake.subscriptions") + mobileConnectionsRepositoryKairos.fake.subscriptions.setValue(subList) + println( + "setSubscriptions: mobileConnectionsRepositoryKairos.fake.setActiveMobileDataSubscriptionId" + ) + mobileConnectionsRepositoryKairos.fake.setActiveMobileDataSubscriptionId(activeSubId) + } - airplaneModeInteractor = - AirplaneModeInteractor( - FakeAirplaneModeRepository(), - FakeConnectivityRepository(), - kosmos.fakeMobileConnectionsRepository, + @Test + fun subscriptionIdsFlow_matchesInteractor() = runTest { + val latest by underTest.subscriptionIds.collectLastValue() + setSubscriptions( + listOf( + SubscriptionModel( + subscriptionId = 1, + isOpportunistic = false, + carrierName = "Carrier 1", + profileClass = PROFILE_CLASS_UNSET, + ) ) - - underTest = - MobileIconsViewModelKairos( - logger, - verboseLogger, - interactor, - airplaneModeInteractor, - constants, - testScope.backgroundScope, + ) + assertThat(latest).isEqualTo(listOf(1)) + + setSubscriptions( + listOf( + SubscriptionModel( + subscriptionId = 2, + isOpportunistic = false, + carrierName = "Carrier 2", + profileClass = PROFILE_CLASS_UNSET, + ), + SubscriptionModel( + subscriptionId = 5, + isOpportunistic = true, + carrierName = "Carrier 5", + profileClass = PROFILE_CLASS_UNSET, + ), + SubscriptionModel( + subscriptionId = 7, + isOpportunistic = true, + carrierName = "Carrier 7", + profileClass = PROFILE_CLASS_UNSET, + ), ) + ) + assertThat(latest).isEqualTo(listOf(2, 5, 7)) - interactor.filteredSubscriptions.value = listOf(SUB_1, SUB_2) + setSubscriptions(emptyList()) + assertThat(latest).isEmpty() } @Test - fun subscriptionIdsFlow_matchesInteractor() = - testScope.runTest { - var latest: List<Int>? = null - val job = underTest.subscriptionIdsFlow.onEach { latest = it }.launchIn(this) - - interactor.filteredSubscriptions.value = - listOf( - SubscriptionModel( - subscriptionId = 1, - isOpportunistic = false, - carrierName = "Carrier 1", - profileClass = PROFILE_CLASS_UNSET, - ) - ) - assertThat(latest).isEqualTo(listOf(1)) - - interactor.filteredSubscriptions.value = - listOf( - SubscriptionModel( - subscriptionId = 2, - isOpportunistic = false, - carrierName = "Carrier 2", - profileClass = PROFILE_CLASS_UNSET, - ), - SubscriptionModel( - subscriptionId = 5, - isOpportunistic = true, - carrierName = "Carrier 5", - profileClass = PROFILE_CLASS_UNSET, - ), - SubscriptionModel( - subscriptionId = 7, - isOpportunistic = true, - carrierName = "Carrier 7", - profileClass = PROFILE_CLASS_UNSET, - ), - ) - assertThat(latest).isEqualTo(listOf(2, 5, 7)) + fun firstMobileSubShowingNetworkTypeIcon_noSubs_false() = runTest { + val latest by underTest.firstMobileSubShowingNetworkTypeIcon.collectLastValue() - interactor.filteredSubscriptions.value = emptyList() - assertThat(latest).isEmpty() + setSubscriptions(emptyList()) - job.cancel() - } + assertThat(latest).isEqualTo(false) + } @Test - fun caching_mobileIconViewModelIsReusedForSameSubId() = - testScope.runTest { - val model1 = underTest.viewModelForSub(1, StatusBarLocation.HOME) - val model2 = underTest.viewModelForSub(1, StatusBarLocation.QS) + fun firstMobileSubShowingNetworkTypeIcon_oneSub_notShowingRat_false() = runTest { + val latest by underTest.firstMobileSubShowingNetworkTypeIcon.collectLastValue() - assertThat(model1.commonImpl).isSameInstanceAs(model2.commonImpl) - } + setSubscriptions(listOf(SUB_1)) + + // The unknown icon group doesn't show a RAT + mobileConnectionsRepositoryKairos.fake.mobileConnectionsBySubId + .sample()[SUB_1.subscriptionId] + ?.resolvedNetworkType + ?.setValue(UnknownNetworkType) + + assertThat(latest).isFalse() + } @Test - fun caching_invalidViewModelsAreRemovedFromCacheWhenSubDisappears() = - testScope.runTest { - // Retrieve models to trigger caching - val model1 = underTest.viewModelForSub(1, StatusBarLocation.HOME) - val model2 = underTest.viewModelForSub(2, StatusBarLocation.QS) + fun firstMobileSubShowingNetworkTypeIcon_oneSub_showingRat_true() = runTest { + val latest by underTest.firstMobileSubShowingNetworkTypeIcon.collectLastValue() + setSubscriptions(listOf(SUB_1)) - // Both impls are cached - assertThat(underTest.reuseCache.keys).containsExactly(1, 2) + mobileConnectionsRepositoryKairos.fake.mobileIsDefault.setValue(true) - // SUB_1 is removed from the list... - interactor.filteredSubscriptions.value = listOf(SUB_2) + // The 3G icon group will show a RAT + val repo = + mobileConnectionsRepositoryKairos.fake.mobileConnectionsBySubId + .sample()[SUB_1.subscriptionId]!! - // ... and dropped from the cache - assertThat(underTest.reuseCache.keys).containsExactly(2) - } + repo.resolvedNetworkType.setValue( + DefaultNetworkType(mobileConnectionsRepositoryKairos.fake.GSM_KEY) + ) + repo.dataConnectionState.setValue(DataConnectionState.Connected) + + assertThat(latest).isEqualTo(true) + } @Test - fun caching_invalidatedViewModelsAreCanceled() = - testScope.runTest { - // Retrieve models to trigger caching - val model1 = underTest.viewModelForSub(1, StatusBarLocation.HOME) - val model2 = underTest.viewModelForSub(2, StatusBarLocation.QS) + fun firstMobileSubShowingNetworkTypeIcon_updatesAsSubUpdates() = runTest { + val latest by underTest.firstMobileSubShowingNetworkTypeIcon.collectLastValue() + setSubscriptions(listOf(SUB_1)) - var scope1 = underTest.reuseCache[1]?.second - var scope2 = underTest.reuseCache[2]?.second + mobileConnectionsRepositoryKairos.fake.mobileIsDefault.setValue(true) - // Scopes are not canceled - assertTrue(scope1!!.isActive) - assertTrue(scope2!!.isActive) + val repo = + mobileConnectionsRepositoryKairos.fake.mobileConnectionsBySubId + .sample()[SUB_1.subscriptionId]!! - // SUB_1 is removed from the list... - interactor.filteredSubscriptions.value = listOf(SUB_2) + repo.dataConnectionState.setValue(DataConnectionState.Connected) - // scope1 is canceled - assertFalse(scope1!!.isActive) - assertTrue(scope2!!.isActive) - } + repo.resolvedNetworkType.setValue( + DefaultNetworkType(mobileConnectionsRepositoryKairos.fake.GSM_KEY) + ) + assertThat(latest).isEqualTo(true) + + mobileConnectionsRepositoryKairos.fake.defaultMobileIconGroup.setValue( + TelephonyIcons.UNKNOWN + ) + + repo.resolvedNetworkType.setValue(UnknownNetworkType) + assertThat(latest).isEqualTo(false) + + repo.resolvedNetworkType.setValue( + DefaultNetworkType(mobileConnectionsRepositoryKairos.fake.LTE_KEY) + ) + assertThat(latest).isEqualTo(true) + } @Test - fun firstMobileSubShowingNetworkTypeIcon_noSubs_false() = - testScope.runTest { - var latest: Boolean? = null - val job = - underTest.firstMobileSubShowingNetworkTypeIcon.onEach { latest = it }.launchIn(this) + fun firstMobileSubShowingNetworkTypeIcon_multipleSubs_lastSubNotShowingRat_false() = runTest { + val latest by underTest.firstMobileSubShowingNetworkTypeIcon.collectLastValue() - interactor.filteredSubscriptions.value = emptyList() + mobileConnectionsRepositoryKairos.fake.defaultMobileIconGroup.setValue( + TelephonyIcons.UNKNOWN + ) + mobileConnectionsRepositoryKairos.fake.mobileIsDefault.setValue(true) - assertThat(latest).isFalse() + val repo1 = + mobileConnectionsRepositoryKairos.fake.mobileConnectionsBySubId + .sample()[SUB_1.subscriptionId]!! - job.cancel() - } + repo1.resolvedNetworkType.setValue( + DefaultNetworkType(mobileConnectionsRepositoryKairos.fake.GSM_KEY) + ) + + val repo2 = + mobileConnectionsRepositoryKairos.fake.mobileConnectionsBySubId + .sample()[SUB_2.subscriptionId]!! + + repo2.resolvedNetworkType.setValue(UnknownNetworkType) + + assertThat(latest).isFalse() + } @Test - fun firstMobileSubShowingNetworkTypeIcon_oneSub_notShowingRat_false() = - testScope.runTest { - var latest: Boolean? = null - val job = - underTest.firstMobileSubShowingNetworkTypeIcon.onEach { latest = it }.launchIn(this) + fun firstMobileSubShowingNetworkTypeIcon_multipleSubs_lastSubShowingRat_true() = runTest { + val latest by underTest.firstMobileSubShowingNetworkTypeIcon.collectLastValue() - interactor.filteredSubscriptions.value = listOf(SUB_1) - // The unknown icon group doesn't show a RAT - interactor.getInteractorForSubId(1)!!.networkTypeIconGroup.value = - NetworkTypeIconModel.DefaultIcon(TelephonyIcons.UNKNOWN) + mobileConnectionsRepositoryKairos.fake.defaultMobileIconGroup.setValue( + TelephonyIcons.UNKNOWN + ) + mobileConnectionsRepositoryKairos.fake.mobileIsDefault.setValue(true) - assertThat(latest).isFalse() + val repo1 = + mobileConnectionsRepositoryKairos.fake.mobileConnectionsBySubId + .sample()[SUB_1.subscriptionId]!! - job.cancel() - } + repo1.dataConnectionState.setValue(DataConnectionState.Connected) + repo1.resolvedNetworkType.setValue(UnknownNetworkType) + + val repo2 = + mobileConnectionsRepositoryKairos.fake.mobileConnectionsBySubId + .sample()[SUB_2.subscriptionId]!! + + repo2.dataConnectionState.setValue(DataConnectionState.Connected) + repo2.resolvedNetworkType.setValue( + DefaultNetworkType(mobileConnectionsRepositoryKairos.fake.GSM_KEY) + ) + + assertThat(latest).isEqualTo(true) + } @Test - fun firstMobileSubShowingNetworkTypeIcon_oneSub_showingRat_true() = - testScope.runTest { - var latest: Boolean? = null - val job = - underTest.firstMobileSubShowingNetworkTypeIcon.onEach { latest = it }.launchIn(this) + fun firstMobileSubShowingNetworkTypeIcon_subListUpdates_valAlsoUpdates() = runTest { + val latest by underTest.firstMobileSubShowingNetworkTypeIcon.collectLastValue() - interactor.filteredSubscriptions.value = listOf(SUB_1) - // The 3G icon group will show a RAT - interactor.getInteractorForSubId(1)!!.networkTypeIconGroup.value = - NetworkTypeIconModel.DefaultIcon(TelephonyIcons.THREE_G) + mobileConnectionsRepositoryKairos.fake.defaultMobileIconGroup.setValue( + TelephonyIcons.UNKNOWN + ) + mobileConnectionsRepositoryKairos.fake.mobileIsDefault.setValue(true) - assertThat(latest).isTrue() + val repo1 = + mobileConnectionsRepositoryKairos.fake.mobileConnectionsBySubId + .sample()[SUB_1.subscriptionId]!! - job.cancel() - } + repo1.dataConnectionState.setValue(DataConnectionState.Connected) + repo1.resolvedNetworkType.setValue(UnknownNetworkType) - @Test - fun firstMobileSubShowingNetworkTypeIcon_updatesAsSubUpdates() = - testScope.runTest { - var latest: Boolean? = null - val job = - underTest.firstMobileSubShowingNetworkTypeIcon.onEach { latest = it }.launchIn(this) + val repo2 = + mobileConnectionsRepositoryKairos.fake.mobileConnectionsBySubId + .sample()[SUB_2.subscriptionId]!! - interactor.filteredSubscriptions.value = listOf(SUB_1) - val sub1Interactor = interactor.getInteractorForSubId(1)!! + repo2.dataConnectionState.setValue(DataConnectionState.Connected) + repo2.resolvedNetworkType.setValue( + DefaultNetworkType(mobileConnectionsRepositoryKairos.fake.GSM_KEY) + ) - sub1Interactor.networkTypeIconGroup.value = - NetworkTypeIconModel.DefaultIcon(TelephonyIcons.THREE_G) - assertThat(latest).isTrue() + assertThat(latest).isEqualTo(true) - sub1Interactor.networkTypeIconGroup.value = - NetworkTypeIconModel.DefaultIcon(TelephonyIcons.UNKNOWN) - assertThat(latest).isFalse() + // WHEN the sub list gets new subscriptions where the last subscription is not showing + // the network type icon + setSubscriptions(listOf(SUB_1, SUB_2, SUB_3)) - sub1Interactor.networkTypeIconGroup.value = - NetworkTypeIconModel.DefaultIcon(TelephonyIcons.LTE) - assertThat(latest).isTrue() + val repo3 = + mobileConnectionsRepositoryKairos.fake.mobileConnectionsBySubId + .sample()[SUB_3.subscriptionId]!! - job.cancel() - } + repo3.dataConnectionState.setValue(DataConnectionState.Connected) + repo3.resolvedNetworkType.setValue(UnknownNetworkType) + + // THEN the flow updates + assertThat(latest).isEqualTo(false) + } @Test - fun firstMobileSubShowingNetworkTypeIcon_multipleSubs_lastSubNotShowingRat_false() = - testScope.runTest { - var latest: Boolean? = null - val job = - underTest.firstMobileSubShowingNetworkTypeIcon.onEach { latest = it }.launchIn(this) + fun firstMobileSubShowingNetworkTypeIcon_subListReorders_valAlsoUpdates() = runTest { + val latest by underTest.firstMobileSubShowingNetworkTypeIcon.collectLastValue() - interactor.filteredSubscriptions.value = listOf(SUB_1, SUB_2) - interactor.getInteractorForSubId(1)?.networkTypeIconGroup?.value = - NetworkTypeIconModel.DefaultIcon(TelephonyIcons.THREE_G) - interactor.getInteractorForSubId(2)!!.networkTypeIconGroup.value = - NetworkTypeIconModel.DefaultIcon(TelephonyIcons.UNKNOWN) + mobileConnectionsRepositoryKairos.fake.defaultMobileIconGroup.setValue( + TelephonyIcons.UNKNOWN + ) + mobileConnectionsRepositoryKairos.fake.mobileIsDefault.setValue(true) - assertThat(latest).isFalse() + setSubscriptions(listOf(SUB_1, SUB_2)) + // Immediately switch the order so that we've created both interactors + setSubscriptions(listOf(SUB_2, SUB_1)) - job.cancel() - } + val repos = mobileConnectionsRepositoryKairos.fake.mobileConnectionsBySubId.sample() + val repo1 = repos[SUB_1.subscriptionId]!! + repo1.dataConnectionState.setValue(DataConnectionState.Connected) - @Test - fun firstMobileSubShowingNetworkTypeIcon_multipleSubs_lastSubShowingRat_true() = - testScope.runTest { - var latest: Boolean? = null - val job = - underTest.firstMobileSubShowingNetworkTypeIcon.onEach { latest = it }.launchIn(this) - - interactor.filteredSubscriptions.value = listOf(SUB_1, SUB_2) - interactor.getInteractorForSubId(1)?.networkTypeIconGroup?.value = - NetworkTypeIconModel.DefaultIcon(TelephonyIcons.UNKNOWN) - interactor.getInteractorForSubId(2)!!.networkTypeIconGroup.value = - NetworkTypeIconModel.DefaultIcon(TelephonyIcons.THREE_G) - - assertThat(latest).isTrue() - job.cancel() - } + val repo2 = repos[SUB_2.subscriptionId]!! + repo2.dataConnectionState.setValue(DataConnectionState.Connected) - @Test - fun firstMobileSubShowingNetworkTypeIcon_subListUpdates_valAlsoUpdates() = - testScope.runTest { - var latest: Boolean? = null - val job = - underTest.firstMobileSubShowingNetworkTypeIcon.onEach { latest = it }.launchIn(this) - - interactor.filteredSubscriptions.value = listOf(SUB_1, SUB_2) - interactor.getInteractorForSubId(1)?.networkTypeIconGroup?.value = - NetworkTypeIconModel.DefaultIcon(TelephonyIcons.UNKNOWN) - interactor.getInteractorForSubId(2)!!.networkTypeIconGroup.value = - NetworkTypeIconModel.DefaultIcon(TelephonyIcons.THREE_G) - - assertThat(latest).isTrue() - - // WHEN the sub list gets new subscriptions where the last subscription is not showing - // the network type icon - interactor.filteredSubscriptions.value = listOf(SUB_1, SUB_2, SUB_3) - interactor.getInteractorForSubId(3)!!.networkTypeIconGroup.value = - NetworkTypeIconModel.DefaultIcon(TelephonyIcons.UNKNOWN) - - // THEN the flow updates - assertThat(latest).isFalse() - - job.cancel() - } + setSubscriptions(listOf(SUB_1, SUB_2)) + repo1.resolvedNetworkType.setValue(UnknownNetworkType) + repo2.resolvedNetworkType.setValue( + DefaultNetworkType(mobileConnectionsRepositoryKairos.fake.GSM_KEY) + ) - @Test - fun firstMobileSubShowingNetworkTypeIcon_subListReorders_valAlsoUpdates() = - testScope.runTest { - var latest: Boolean? = null - val job = - underTest.firstMobileSubShowingNetworkTypeIcon.onEach { latest = it }.launchIn(this) - - interactor.filteredSubscriptions.value = listOf(SUB_1, SUB_2) - // Immediately switch the order so that we've created both interactors - interactor.filteredSubscriptions.value = listOf(SUB_2, SUB_1) - val sub1Interactor = interactor.getInteractorForSubId(1)!! - val sub2Interactor = interactor.getInteractorForSubId(2)!! - - interactor.filteredSubscriptions.value = listOf(SUB_1, SUB_2) - sub1Interactor.networkTypeIconGroup.value = - NetworkTypeIconModel.DefaultIcon(TelephonyIcons.UNKNOWN) - sub2Interactor.networkTypeIconGroup.value = - NetworkTypeIconModel.DefaultIcon(TelephonyIcons.THREE_G) - assertThat(latest).isTrue() - - // WHEN sub1 becomes last and sub1 has no network type icon - interactor.filteredSubscriptions.value = listOf(SUB_2, SUB_1) - - // THEN the flow updates - assertThat(latest).isFalse() - - // WHEN sub2 becomes last and sub2 has a network type icon - interactor.filteredSubscriptions.value = listOf(SUB_1, SUB_2) - - // THEN the flow updates - assertThat(latest).isTrue() - - job.cancel() - } + assertThat(latest).isEqualTo(true) + + // WHEN sub1 becomes last and sub1 has no network type icon + setSubscriptions(listOf(SUB_2, SUB_1)) + + // THEN the flow updates + assertThat(latest).isEqualTo(false) + + // WHEN sub2 becomes last and sub2 has a network type icon + setSubscriptions(listOf(SUB_1, SUB_2)) + + // THEN the flow updates + assertThat(latest).isEqualTo(true) + } companion object { private val SUB_1 = diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModelKairosTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModelKairosTest.kt index ce35d9d8610f..75f5cbb041c4 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModelKairosTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModelKairosTest.kt @@ -21,81 +21,83 @@ import android.telephony.SubscriptionManager.PROFILE_CLASS_UNSET import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase -import com.android.systemui.common.shared.model.Icon +import com.android.systemui.flags.Flags +import com.android.systemui.flags.fake +import com.android.systemui.flags.featureFlagsClassic +import com.android.systemui.kairos.ExperimentalKairosApi +import com.android.systemui.kairos.KairosTestScope +import com.android.systemui.kairos.runKairosTest import com.android.systemui.kosmos.Kosmos -import com.android.systemui.kosmos.Kosmos.Fixture -import com.android.systemui.kosmos.runTest -import com.android.systemui.kosmos.testScope import com.android.systemui.kosmos.useUnconfinedTestDispatcher -import com.android.systemui.lifecycle.activateIn import com.android.systemui.statusbar.core.NewStatusBarIcons import com.android.systemui.statusbar.core.StatusBarRootModernization import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel -import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.fakeMobileIconsInteractor -import com.android.systemui.statusbar.pipeline.mobile.domain.model.SignalIconModel +import com.android.systemui.statusbar.pipeline.mobile.data.repository.fake +import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobileConnectionsRepositoryKairos +import com.android.systemui.statusbar.pipeline.mobile.data.repository.mobileConnectionsRepositoryKairos import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat -import org.junit.Before import org.junit.Test import org.junit.runner.RunWith +@OptIn(ExperimentalKairosApi::class) @SmallTest @RunWith(AndroidJUnit4::class) class StackedMobileIconViewModelKairosTest : SysuiTestCase() { - private val kosmos = testKosmos().useUnconfinedTestDispatcher() - private val testScope = kosmos.testScope - - private val Kosmos.underTest: StackedMobileIconViewModelKairos by Fixture { - stackedMobileIconViewModelKairos - } + private val kosmos = + testKosmos().useUnconfinedTestDispatcher().apply { + mobileConnectionsRepositoryKairos = fakeMobileConnectionsRepositoryKairos + featureFlagsClassic.fake.apply { + setDefault(Flags.FILTER_PROVISIONING_NETWORK_SUBSCRIPTIONS) + } + } - @Before - fun setUp() { - kosmos.underTest.activateIn(testScope) - } + private val Kosmos.underTest + get() = stackedMobileIconViewModelKairos @Test @EnableFlags(NewStatusBarIcons.FLAG_NAME, StatusBarRootModernization.FLAG_NAME) fun dualSim_filtersOutNonDualConnections() = - kosmos.runTest { - fakeMobileIconsInteractor.filteredSubscriptions.value = listOf() + kosmos.runKairosTest { + mobileConnectionsRepositoryKairos.fake.subscriptions.setValue(listOf()) assertThat(underTest.dualSim).isNull() - fakeMobileIconsInteractor.filteredSubscriptions.value = listOf(SUB_1) + mobileConnectionsRepositoryKairos.fake.subscriptions.setValue(listOf(SUB_1)) assertThat(underTest.dualSim).isNull() - fakeMobileIconsInteractor.filteredSubscriptions.value = listOf(SUB_1, SUB_2, SUB_3) + mobileConnectionsRepositoryKairos.fake.subscriptions.setValue( + listOf(SUB_1, SUB_2, SUB_3) + ) assertThat(underTest.dualSim).isNull() - fakeMobileIconsInteractor.filteredSubscriptions.value = listOf(SUB_1, SUB_2) + mobileConnectionsRepositoryKairos.fake.subscriptions.setValue(listOf(SUB_1, SUB_2)) assertThat(underTest.dualSim).isNotNull() } @Test @EnableFlags(NewStatusBarIcons.FLAG_NAME, StatusBarRootModernization.FLAG_NAME) fun dualSim_filtersOutNonCellularIcons() = - kosmos.runTest { - fakeMobileIconsInteractor.filteredSubscriptions.value = listOf(SUB_1) + kosmos.runKairosTest { + mobileConnectionsRepositoryKairos.fake.subscriptions.setValue(listOf(SUB_1)) assertThat(underTest.dualSim).isNull() - fakeMobileIconsInteractor - .getInteractorForSubId(SUB_1.subscriptionId)!! - .signalLevelIcon - .value = - SignalIconModel.Satellite( - level = 0, - icon = Icon.Resource(res = 0, contentDescription = null), - ) - fakeMobileIconsInteractor.filteredSubscriptions.value = listOf(SUB_1, SUB_2) + mobileConnectionsRepositoryKairos.fake.mobileConnectionsBySubId + .sample()[SUB_1.subscriptionId]!! + .apply { + isNonTerrestrial.setValue(true) + satelliteLevel.setValue(0) + } + + mobileConnectionsRepositoryKairos.fake.subscriptions.setValue(listOf(SUB_1, SUB_2)) assertThat(underTest.dualSim).isNull() } @Test @EnableFlags(NewStatusBarIcons.FLAG_NAME, StatusBarRootModernization.FLAG_NAME) fun dualSim_tracksActiveSubId() = - kosmos.runTest { + kosmos.runKairosTest { // Active sub id is null, order is unchanged - fakeMobileIconsInteractor.filteredSubscriptions.value = listOf(SUB_1, SUB_2) + mobileConnectionsRepositoryKairos.fake.subscriptions.setValue(listOf(SUB_1, SUB_2)) setIconLevel(SUB_1.subscriptionId, 1) setIconLevel(SUB_2.subscriptionId, 2) @@ -103,16 +105,21 @@ class StackedMobileIconViewModelKairosTest : SysuiTestCase() { assertThat(underTest.dualSim!!.secondary.level).isEqualTo(2) // Active sub is 2, order is swapped - fakeMobileIconsInteractor.activeMobileDataSubscriptionId.value = SUB_2.subscriptionId + mobileConnectionsRepositoryKairos.fake.setActiveMobileDataSubscriptionId( + SUB_2.subscriptionId + ) assertThat(underTest.dualSim!!.primary.level).isEqualTo(2) assertThat(underTest.dualSim!!.secondary.level).isEqualTo(1) } - private fun setIconLevel(subId: Int, level: Int) { - with(kosmos.fakeMobileIconsInteractor.getInteractorForSubId(subId)!!) { - signalLevelIcon.value = - (signalLevelIcon.value as SignalIconModel.Cellular).copy(level = level) + private suspend fun KairosTestScope.setIconLevel(subId: Int, level: Int) { + mobileConnectionsRepositoryKairos.fake.mobileConnectionsBySubId.sample()[subId]!!.apply { + isNonTerrestrial.setValue(false) + isInService.setValue(true) + inflateSignalStrength.setValue(false) + isGsm.setValue(true) + primaryLevel.setValue(level) } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModelTest.kt index d7bcf88f6941..8a7b698623f8 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModelTest.kt @@ -45,8 +45,8 @@ class StackedMobileIconViewModelTest : SysuiTestCase() { private val kosmos = testKosmos().useUnconfinedTestDispatcher() private val testScope = kosmos.testScope - private val Kosmos.underTest: StackedMobileIconViewModel by Fixture { - stackedMobileIconViewModel + private val Kosmos.underTest: StackedMobileIconViewModelImpl by Fixture { + stackedMobileIconViewModelImpl } @Before diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt index 18a124cf362e..033503f9ad8e 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt @@ -52,6 +52,7 @@ import com.android.systemui.unfold.DisplaySwitchLatencyTracker.Companion.FOLDABL import com.android.systemui.unfold.DisplaySwitchLatencyTracker.Companion.FOLDABLE_DEVICE_STATE_HALF_OPEN import com.android.systemui.unfold.DisplaySwitchLatencyTracker.Companion.SCREEN_EVENT_TIMEOUT import com.android.systemui.unfold.DisplaySwitchLatencyTracker.DisplaySwitchLatencyEvent +import com.android.systemui.unfold.data.repository.ScreenTimeoutPolicyRepository import com.android.systemui.unfold.data.repository.UnfoldTransitionRepositoryImpl import com.android.systemui.unfold.domain.interactor.UnfoldTransitionInteractor import com.android.systemui.unfoldedDeviceState @@ -97,6 +98,8 @@ class DisplaySwitchLatencyTrackerTest : SysuiTestCase() { private val animationStatusRepository = kosmos.fakeAnimationStatusRepository private val keyguardInteractor = mock<KeyguardInteractor>() private val displaySwitchLatencyLogger = mock<DisplaySwitchLatencyLogger>() + private val screenTimeoutPolicyRepository = mock<ScreenTimeoutPolicyRepository>() + private val screenTimeoutActive = MutableStateFlow(true) private val latencyTracker = mock<LatencyTracker>() private val deviceStateManager = kosmos.deviceStateManager @@ -136,6 +139,7 @@ class DisplaySwitchLatencyTrackerTest : SysuiTestCase() { whenever(resources.getIntArray(R.array.config_foldedDeviceStates)) .thenReturn(nonEmptyClosedDeviceStatesArray) whenever(keyguardInteractor.isAodAvailable).thenReturn(isAodAvailable) + whenever(screenTimeoutPolicyRepository.screenTimeoutActive).thenReturn(screenTimeoutActive) animationStatusRepository.onAnimationStatusChanged(true) powerInteractor.setAwakeForTest() powerInteractor.setScreenPowerState(SCREEN_ON) @@ -144,6 +148,7 @@ class DisplaySwitchLatencyTrackerTest : SysuiTestCase() { mockContext, foldStateRepository, powerInteractor, + screenTimeoutPolicyRepository, unfoldTransitionInteractor, animationStatusRepository, keyguardInteractor, @@ -196,6 +201,7 @@ class DisplaySwitchLatencyTrackerTest : SysuiTestCase() { mockContext, foldStateRepository, powerInteractor, + screenTimeoutPolicyRepository, unfoldTransitionInteractorWithEmptyProgressProvider, animationStatusRepository, keyguardInteractor, @@ -625,6 +631,44 @@ class DisplaySwitchLatencyTrackerTest : SysuiTestCase() { } } + @Test + fun displaySwitch_screenTimeoutActive_logsNoScreenWakelocks() { + testScope.runTest { + startInFoldedState(displaySwitchLatencyTracker) + screenTimeoutActive.value = true + + startUnfolding() + advanceTimeBy(100.milliseconds) + finishUnfolding() + + val event = capturedLogEvent() + assertThat(event.screenWakelockStatus) + .isEqualTo( + SysUiStatsLog + .DISPLAY_SWITCH_LATENCY_TRACKED__SCREEN_WAKELOCK_STATUS__SCREEN_WAKELOCK_STATUS_NO_WAKELOCKS + ) + } + } + + @Test + fun displaySwitch_screenTimeoutNotActive_logsHasScreenWakelocks() { + testScope.runTest { + startInFoldedState(displaySwitchLatencyTracker) + screenTimeoutActive.value = false + + startUnfolding() + advanceTimeBy(100.milliseconds) + finishUnfolding() + + val event = capturedLogEvent() + assertThat(event.screenWakelockStatus) + .isEqualTo( + SysUiStatsLog + .DISPLAY_SWITCH_LATENCY_TRACKED__SCREEN_WAKELOCK_STATUS__SCREEN_WAKELOCK_STATUS_HAS_SCREEN_WAKELOCKS + ) + } + } + private fun capturedLogEvent(): DisplaySwitchLatencyEvent { verify(displaySwitchLatencyLogger).log(capture(loggerArgumentCaptor)) return loggerArgumentCaptor.value @@ -662,6 +706,9 @@ class DisplaySwitchLatencyTrackerTest : SysuiTestCase() { fromFoldableDeviceState = fromFoldableDeviceState, toFoldableDeviceState = toFoldableDeviceState, toState = toState, + screenWakelockStatus = + SysUiStatsLog + .DISPLAY_SWITCH_LATENCY_TRACKED__SCREEN_WAKELOCK_STATUS__SCREEN_WAKELOCK_STATUS_NO_WAKELOCKS, trackingResult = SysUiStatsLog.DISPLAY_SWITCH_LATENCY_TRACKED__TRACKING_RESULT__SUCCESS, ) } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorTest.kt index 3eada258f616..07706414393b 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorTest.kt @@ -27,6 +27,8 @@ import android.graphics.drawable.Drawable import android.os.Process import android.os.UserHandle import android.os.UserManager +import android.platform.test.annotations.DisableFlags +import android.platform.test.annotations.EnableFlags import android.provider.Settings import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest @@ -34,6 +36,7 @@ import com.android.internal.logging.UiEventLogger import com.android.keyguard.KeyguardUpdateMonitor import com.android.keyguard.KeyguardUpdateMonitorCallback import com.android.systemui.Flags as AConfigFlags +import com.android.systemui.Flags.FLAG_USER_SWITCHER_ADD_SIGN_OUT_OPTION import com.android.systemui.GuestResetOrExitSessionReceiver import com.android.systemui.GuestResumeSessionReceiver import com.android.systemui.SysuiTestCase @@ -68,6 +71,7 @@ import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat import junit.framework.Assert.assertNotNull +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.runCurrent @@ -101,10 +105,13 @@ class UserSwitcherInteractorTest : SysuiTestCase() { @Mock private lateinit var resumeSessionReceiver: GuestResumeSessionReceiver @Mock private lateinit var resetOrExitSessionReceiver: GuestResetOrExitSessionReceiver @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor + @Mock private lateinit var userLogoutInteractor: UserLogoutInteractor private val kosmos = testKosmos() + private val logoutEnabledStateFlow = MutableStateFlow<Boolean>(false) private val testScope = kosmos.testScope private lateinit var spyContext: Context + private lateinit var userRepository: FakeUserRepository private lateinit var keyguardReply: KeyguardInteractorFactory.WithDependencies private lateinit var keyguardRepository: FakeKeyguardRepository @@ -118,6 +125,8 @@ class UserSwitcherInteractorTest : SysuiTestCase() { whenever(manager.getUserIcon(anyInt())).thenReturn(ICON) whenever(manager.canAddMoreUsers(any())).thenReturn(true) + whenever(userLogoutInteractor.isLogoutEnabled).thenReturn(logoutEnabledStateFlow) + overrideResource(com.android.settingslib.R.drawable.ic_account_circle, GUEST_ICON) overrideResource(R.dimen.max_avatar_size, 10) overrideResource( @@ -493,6 +502,42 @@ class UserSwitcherInteractorTest : SysuiTestCase() { } @Test + @DisableFlags(FLAG_USER_SWITCHER_ADD_SIGN_OUT_OPTION) + fun actions_logoutEnabled_flagDisabled_signOutIsNotShown() { + createUserInteractor() + testScope.runTest { + val userInfos = createUserInfos(count = 1, includeGuest = false) + userRepository.setUserInfos(userInfos) + userRepository.setSelectedUserInfo(userInfos[0]) + userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = false)) + keyguardRepository.setKeyguardShowing(true) + logoutEnabledStateFlow.value = true + + val value = collectLastValue(underTest.actions) + + assertThat(value()).isEqualTo(emptyList<UserActionModel>()) + } + } + + @Test + @EnableFlags(FLAG_USER_SWITCHER_ADD_SIGN_OUT_OPTION) + fun actions_logoutEnabled_flagEnabled_signOutIsShown() { + createUserInteractor() + testScope.runTest { + val userInfos = createUserInfos(count = 1, includeGuest = false) + userRepository.setUserInfos(userInfos) + userRepository.setSelectedUserInfo(userInfos[0]) + userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = false)) + keyguardRepository.setKeyguardShowing(true) + logoutEnabledStateFlow.value = true + + val value = collectLastValue(underTest.actions) + + assertThat(value()).isEqualTo(listOf(UserActionModel.SIGN_OUT)) + } + } + + @Test fun executeAction_addUser_dismissesDialogAndStartsActivity() { createUserInteractor() testScope.runTest { @@ -569,14 +614,23 @@ class UserSwitcherInteractorTest : SysuiTestCase() { verify(uiEventLogger, times(1)) .log(MultiUserActionsEvent.CREATE_GUEST_FROM_USER_SWITCHER) assertThat(dialogRequests) - .contains( - ShowDialogRequestModel.ShowUserCreationDialog(isGuest = true), - ) + .contains(ShowDialogRequestModel.ShowUserCreationDialog(isGuest = true)) verify(activityManager).switchUser(guestUserInfo.id) } } @Test + fun executeAction_signOut() { + createUserInteractor() + testScope.runTest { + underTest.executeAction(UserActionModel.SIGN_OUT) + runCurrent() + + verify(userLogoutInteractor).logOut() + } + } + + @Test fun selectUser_alreadySelectedGuestReSelected_exitGuestDialog() { createUserInteractor() testScope.runTest { @@ -739,7 +793,7 @@ class UserSwitcherInteractorTest : SysuiTestCase() { fakeBroadcastDispatcher.sendIntentToMatchingReceiversOnly( spyContext, - Intent(Intent.ACTION_LOCALE_CHANGED) + Intent(Intent.ACTION_LOCALE_CHANGED), ) runCurrent() @@ -972,7 +1026,7 @@ class UserSwitcherInteractorTest : SysuiTestCase() { 50, "Work Profile", /* iconPath= */ "", - /* flags= */ UserInfo.FLAG_MANAGED_PROFILE + /* flags= */ UserInfo.FLAG_MANAGED_PROFILE, ) ) userRepository.setUserInfos(userInfos) @@ -1010,7 +1064,7 @@ class UserSwitcherInteractorTest : SysuiTestCase() { userRepository.setSettings( UserSwitcherSettingsModel( isUserSwitcherEnabled = true, - isAddUsersFromLockscreen = true + isAddUsersFromLockscreen = true, ) ) @@ -1034,7 +1088,7 @@ class UserSwitcherInteractorTest : SysuiTestCase() { userRepository.setSettings( UserSwitcherSettingsModel( isUserSwitcherEnabled = true, - isAddUsersFromLockscreen = true + isAddUsersFromLockscreen = true, ) ) @@ -1068,7 +1122,7 @@ class UserSwitcherInteractorTest : SysuiTestCase() { whenever( manager.hasUserRestrictionForUser( UserManager.DISALLOW_ADD_USER, - UserHandle.of(id) + UserHandle.of(id), ) ) .thenReturn(true) @@ -1170,7 +1224,7 @@ class UserSwitcherInteractorTest : SysuiTestCase() { whenever( manager.hasUserRestrictionForUser( UserManager.DISALLOW_ADD_USER, - UserHandle.of(0) + UserHandle.of(0), ) ) .thenReturn(true) @@ -1195,7 +1249,7 @@ class UserSwitcherInteractorTest : SysuiTestCase() { model = model, id = index, isSelected = index == selectedIndex, - isGuest = includeGuest && index == count - 1 + isGuest = includeGuest && index == count - 1, ) } } @@ -1263,14 +1317,12 @@ class UserSwitcherInteractorTest : SysuiTestCase() { assertThat(record.isSwitchToEnabled).isEqualTo(isSwitchToEnabled) } - private fun assertRecordForAction( - record: UserRecord, - type: UserActionModel, - ) { + private fun assertRecordForAction(record: UserRecord, type: UserActionModel) { assertThat(record.isGuest).isEqualTo(type == UserActionModel.ENTER_GUEST_MODE) assertThat(record.isAddUser).isEqualTo(type == UserActionModel.ADD_USER) assertThat(record.isAddSupervisedUser) .isEqualTo(type == UserActionModel.ADD_SUPERVISED_USER) + assertThat(record.isSignOut).isEqualTo(type === UserActionModel.SIGN_OUT) } private fun createUserInteractor(startAsProcessUser: Boolean = true) { @@ -1317,13 +1369,11 @@ class UserSwitcherInteractorTest : SysuiTestCase() { featureFlags = kosmos.fakeFeatureFlagsClassic, userRestrictionChecker = mock(), processWrapper = kosmos.processWrapper, + userLogoutInteractor = userLogoutInteractor, ) } - private fun createUserInfos( - count: Int, - includeGuest: Boolean, - ): List<UserInfo> { + private fun createUserInfos(count: Int, includeGuest: Boolean): List<UserInfo> { return (0 until count).map { index -> val isGuest = includeGuest && index == count - 1 createUserInfo( diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt index 5d51c6d16c5a..d51e66d6f3b0 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/user/ui/viewmodel/StatusBarUserChipViewModelTest.kt @@ -44,9 +44,12 @@ import com.android.systemui.user.data.repository.FakeUserRepository import com.android.systemui.user.domain.interactor.GuestUserInteractor import com.android.systemui.user.domain.interactor.HeadlessSystemUserMode import com.android.systemui.user.domain.interactor.RefreshUsersScheduler +import com.android.systemui.user.domain.interactor.UserLogoutInteractor import com.android.systemui.user.domain.interactor.UserSwitcherInteractor import com.android.systemui.util.mockito.mock +import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.toList @@ -78,13 +81,13 @@ class StatusBarUserChipViewModelTest : SysuiTestCase() { @Mock private lateinit var resumeSessionReceiver: GuestResumeSessionReceiver @Mock private lateinit var resetOrExitSessionReceiver: GuestResetOrExitSessionReceiver @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor + @Mock private lateinit var userLogoutInteractor: UserLogoutInteractor private lateinit var underTest: StatusBarUserChipViewModel private val userRepository = FakeUserRepository() private lateinit var guestUserInteractor: GuestUserInteractor private lateinit var refreshUsersScheduler: RefreshUsersScheduler - private val testDispatcher = UnconfinedTestDispatcher() private val testScope = TestScope(testDispatcher) @@ -92,6 +95,9 @@ class StatusBarUserChipViewModelTest : SysuiTestCase() { fun setUp() { MockitoAnnotations.initMocks(this) + val logoutEnabledStateFlow = MutableStateFlow<Boolean>(false) + whenever(userLogoutInteractor.isLogoutEnabled).thenReturn(logoutEnabledStateFlow) + doAnswer { invocation -> val userId = invocation.arguments[0] as Int when (userId) { @@ -251,9 +257,7 @@ class StatusBarUserChipViewModelTest : SysuiTestCase() { headlessSystemUserMode = headlessSystemUserMode, applicationScope = testScope.backgroundScope, telephonyInteractor = - TelephonyInteractor( - repository = FakeTelephonyRepository(), - ), + TelephonyInteractor(repository = FakeTelephonyRepository()), broadcastDispatcher = fakeBroadcastDispatcher, keyguardUpdateMonitor = keyguardUpdateMonitor, backgroundDispatcher = testDispatcher, @@ -263,7 +267,8 @@ class StatusBarUserChipViewModelTest : SysuiTestCase() { guestUserInteractor = guestUserInteractor, uiEventLogger = uiEventLogger, userRestrictionChecker = mock(), - processWrapper = ProcessWrapperFake(activityManager) + processWrapper = ProcessWrapperFake(activityManager), + userLogoutInteractor = userLogoutInteractor, ) ) } @@ -293,7 +298,7 @@ class StatusBarUserChipViewModelTest : SysuiTestCase() { USER_NAME_0.text!!, /* iconPath */ "", /* flags */ UserInfo.FLAG_FULL, - /* userType */ UserManager.USER_TYPE_FULL_SYSTEM + /* userType */ UserManager.USER_TYPE_FULL_SYSTEM, ) private val USER_1 = @@ -302,7 +307,7 @@ class StatusBarUserChipViewModelTest : SysuiTestCase() { USER_NAME_1.text!!, /* iconPath */ "", /* flags */ UserInfo.FLAG_FULL, - /* userType */ UserManager.USER_TYPE_FULL_SYSTEM + /* userType */ UserManager.USER_TYPE_FULL_SYSTEM, ) private val USER_2 = @@ -311,7 +316,7 @@ class StatusBarUserChipViewModelTest : SysuiTestCase() { USER_NAME_2.text!!, /* iconPath */ "", /* flags */ UserInfo.FLAG_FULL, - /* userType */ UserManager.USER_TYPE_FULL_SYSTEM + /* userType */ UserManager.USER_TYPE_FULL_SYSTEM, ) } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt index 8ff088f5d29b..087ccb83afe5 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt @@ -44,6 +44,7 @@ import com.android.systemui.user.data.repository.FakeUserRepository import com.android.systemui.user.domain.interactor.GuestUserInteractor import com.android.systemui.user.domain.interactor.HeadlessSystemUserMode import com.android.systemui.user.domain.interactor.RefreshUsersScheduler +import com.android.systemui.user.domain.interactor.UserLogoutInteractor import com.android.systemui.user.domain.interactor.UserSwitcherInteractor import com.android.systemui.user.legacyhelper.ui.LegacyUserUiHelper import com.android.systemui.user.shared.model.UserActionModel @@ -51,6 +52,7 @@ import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.toList import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking @@ -79,6 +81,7 @@ class UserSwitcherViewModelTest : SysuiTestCase() { @Mock private lateinit var resumeSessionReceiver: GuestResumeSessionReceiver @Mock private lateinit var resetOrExitSessionReceiver: GuestResetOrExitSessionReceiver @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor + @Mock private lateinit var userLogoutInteractor: UserLogoutInteractor private lateinit var underTest: UserSwitcherViewModel @@ -94,6 +97,10 @@ class UserSwitcherViewModelTest : SysuiTestCase() { whenever(manager.canAddMoreUsers(any())).thenReturn(true) whenever(manager.getUserSwitchability(any())) .thenReturn(UserManager.SWITCHABILITY_STATUS_OK) + + val logoutEnabledStateFlow = MutableStateFlow<Boolean>(false) + whenever(userLogoutInteractor.isLogoutEnabled).thenReturn(logoutEnabledStateFlow) + overrideResource( com.android.internal.R.string.config_supervisedUserCreationPackage, SUPERVISED_USER_CREATION_PACKAGE, @@ -113,15 +120,11 @@ class UserSwitcherViewModelTest : SysuiTestCase() { UserInfo.FLAG_ADMIN or UserInfo.FLAG_FULL, UserManager.USER_TYPE_FULL_SYSTEM, - ), + ) ) userRepository.setUserInfos(userInfos) userRepository.setSelectedUserInfo(userInfos[0]) - userRepository.setSettings( - UserSwitcherSettingsModel( - isUserSwitcherEnabled = true, - ) - ) + userRepository.setSettings(UserSwitcherSettingsModel(isUserSwitcherEnabled = true)) } val refreshUsersScheduler = @@ -163,9 +166,7 @@ class UserSwitcherViewModelTest : SysuiTestCase() { headlessSystemUserMode = headlessSystemUserMode, applicationScope = testScope.backgroundScope, telephonyInteractor = - TelephonyInteractor( - repository = FakeTelephonyRepository(), - ), + TelephonyInteractor(repository = FakeTelephonyRepository()), broadcastDispatcher = fakeBroadcastDispatcher, keyguardUpdateMonitor = keyguardUpdateMonitor, backgroundDispatcher = testDispatcher, @@ -175,7 +176,8 @@ class UserSwitcherViewModelTest : SysuiTestCase() { guestUserInteractor = guestUserInteractor, uiEventLogger = uiEventLogger, userRestrictionChecker = mock(), - processWrapper = ProcessWrapperFake(activityManager) + processWrapper = ProcessWrapperFake(activityManager), + userLogoutInteractor = userLogoutInteractor, ), guestUserInteractor = guestUserInteractor, ) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/window/ui/viewmodel/WindowRootViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/window/ui/viewmodel/WindowRootViewModelTest.kt index 3da4f29a6fcb..dc344aa1a66f 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/window/ui/viewmodel/WindowRootViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/window/ui/viewmodel/WindowRootViewModelTest.kt @@ -73,7 +73,7 @@ class WindowRootViewModelTest : SysuiTestCase() { assertThat(blurRadius).isEqualTo(0f) - kosmos.windowRootViewBlurRepository.blurRadius.value = 60 + kosmos.windowRootViewBlurRepository.blurRequestedByShade.value = 60 runCurrent() assertThat(blurRadius).isEqualTo(0f) diff --git a/packages/SystemUI/res-keyguard/values-bn/strings.xml b/packages/SystemUI/res-keyguard/values-bn/strings.xml index fe6636bfa489..15471a69497d 100644 --- a/packages/SystemUI/res-keyguard/values-bn/strings.xml +++ b/packages/SystemUI/res-keyguard/values-bn/strings.xml @@ -63,8 +63,8 @@ <string name="kg_bio_try_again_or_password" msgid="1473132729225398039">"আবার চেষ্টা করুন বা পাসওয়ার্ড লিখুন"</string> <string name="kg_bio_try_again_or_pattern" msgid="4867893307468801501">"আবার চেষ্টা করুন বা প্যাটার্ন দিন"</string> <string name="kg_bio_too_many_attempts_pin" msgid="5850845723433047605">"অনেক বেশিবার চেষ্টা করার পরে পিন দিতে হবে"</string> - <string name="kg_bio_too_many_attempts_password" msgid="5551690347827728042">"অনেক বেশিবার চেষ্টা করার পরে পাসওয়ার্ড দিতে হবে"</string> - <string name="kg_bio_too_many_attempts_pattern" msgid="736884689355181602">"অনেক বেশিবার চেষ্টা করার পরে প্যাটার্ন দিতে হবে"</string> + <string name="kg_bio_too_many_attempts_password" msgid="5551690347827728042">"অনেকবার চেষ্টা করার পর পাসওয়ার্ড দিতে হয়"</string> + <string name="kg_bio_too_many_attempts_pattern" msgid="736884689355181602">"অনেকবার চেষ্টা করার পর প্যাটার্ন দিতে হয়"</string> <string name="kg_unlock_with_pin_or_fp" msgid="5635161174698729890">"পিন বা ফিঙ্গারপ্রিন্টের সাহায্যে আনলক করুন"</string> <string name="kg_unlock_with_password_or_fp" msgid="2251295907826814237">"পাসওয়ার্ড বা ফিঙ্গারপ্রিন্টের সাহায্যে আনলক করুন"</string> <string name="kg_unlock_with_pattern_or_fp" msgid="2391870539909135046">"প্যাটার্ন বা ফিঙ্গারপ্রিন্টের সাহায্যে আনলক করুন"</string> diff --git a/packages/SystemUI/res/drawable/unpin_icon.xml b/packages/SystemUI/res/drawable/unpin_icon.xml new file mode 100644 index 000000000000..4e2e15893884 --- /dev/null +++ b/packages/SystemUI/res/drawable/unpin_icon.xml @@ -0,0 +1,10 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="24dp" + android:height="24dp" + android:viewportWidth="960" + android:viewportHeight="960" + android:tint="?attr/colorControlNormal"> + <path + android:fillColor="@android:color/white" + android:pathData="M680,120L680,200L640,200L640,527L560,447L560,200L400,200L400,287L313,200L280,167L280,167L280,120L680,120ZM480,920L440,880L440,640L240,640L240,560L320,480L320,434L56,168L112,112L848,848L790,904L526,640L520,640L520,880L480,920ZM354,560L446,560L402,516L400,514L354,560ZM480,367L480,367L480,367L480,367ZM402,516L402,516L402,516L402,516Z"/> +</vector> diff --git a/packages/SystemUI/res/drawable/vector_drawable_progress_indeterminate_horizontal_trimmed.xml b/packages/SystemUI/res/drawable/vector_drawable_progress_indeterminate_horizontal_trimmed.xml index aec204f45aa7..7f6dc49505bb 100644 --- a/packages/SystemUI/res/drawable/vector_drawable_progress_indeterminate_horizontal_trimmed.xml +++ b/packages/SystemUI/res/drawable/vector_drawable_progress_indeterminate_horizontal_trimmed.xml @@ -38,7 +38,7 @@ <path android:name="rect" android:pathData="M -144.0,-5.0 l 288.0,0 l 0,10.0 l -288.0,0 Z" - android:fillColor="?androidprv:attr/colorAccentPrimaryVariant" /> + android:fillColor="@androidprv:color/materialColorPrimary" /> </group> </group> </vector>
\ No newline at end of file diff --git a/packages/SystemUI/res/layout/activity_rear_display_enabled.xml b/packages/SystemUI/res/layout/activity_rear_display_enabled.xml index f900626b4da6..6b633e03f1f2 100644 --- a/packages/SystemUI/res/layout/activity_rear_display_enabled.xml +++ b/packages/SystemUI/res/layout/activity_rear_display_enabled.xml @@ -56,6 +56,7 @@ android:gravity="center_horizontal" /> <TextView + android:id="@+id/seekbar_instructions" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/rear_display_unfolded_front_screen_on_slide_to_cancel" @@ -73,4 +74,13 @@ android:background="@null" android:gravity="center_horizontal" /> + <Button + android:id="@+id/cancel_button" + android:text="@string/cancel" + android:layout_width="@dimen/rear_display_animation_width_opened" + android:layout_height="wrap_content" + android:gravity="center_horizontal" + android:visibility="gone" + style="@style/Widget.Dialog.Button.BorderButton"/> + </LinearLayout> diff --git a/packages/SystemUI/res/layout/promoted_permission_guts.xml b/packages/SystemUI/res/layout/promoted_permission_guts.xml new file mode 100644 index 000000000000..50e5ae3c05ed --- /dev/null +++ b/packages/SystemUI/res/layout/promoted_permission_guts.xml @@ -0,0 +1,73 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright 2017, The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<com.android.systemui.statusbar.notification.row.PromotedPermissionGutsContent + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:androidprv="http://schemas.android.com/apk/prv/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + android:paddingTop="2dp" + android:paddingBottom="2dp" + android:background="@androidprv:color/materialColorSurfaceContainerHigh" + android:theme="@style/Theme.SystemUI" + > + + <RelativeLayout + android:id="@+id/promoted_guts" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:minHeight="@dimen/notification_2025_min_height"> + + <ImageView + android:id="@+id/unpin_icon" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:src="@drawable/unpin_icon" + android:layout_alignParentTop="true" + android:layout_centerHorizontal="true" + android:padding="@dimen/notification_importance_button_padding" + /> + + <TextView + android:id="@+id/demote_explain" + android:layout_width="400dp" + android:layout_height="wrap_content" + android:layout_alignParentLeft="true" + android:layout_below="@id/unpin_icon" + android:layout_toLeftOf="@id/undo" + android:padding="@*android:dimen/notification_content_margin_end" + android:textColor="@androidprv:color/materialColorOnSurface" + android:minWidth="@dimen/min_clickable_item_size" + android:minHeight="@dimen/min_clickable_item_size" + style="@style/TextAppearance.NotificationInfo.Button" /> + + <TextView + android:id="@+id/undo" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_below="@id/unpin_icon" + android:layout_alignParentRight="true" + android:padding="@*android:dimen/notification_content_margin_end" + android:textColor="@androidprv:color/materialColorOnSurface" + android:minWidth="@dimen/min_clickable_item_size" + android:minHeight="@dimen/min_clickable_item_size" + android:text="@string/snooze_undo" + style="@style/TextAppearance.NotificationInfo.Button" /> + </RelativeLayout> + +</com.android.systemui.statusbar.notification.row.PromotedPermissionGutsContent> diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml index bc08a3e2c628..06192813672a 100644 --- a/packages/SystemUI/res/values-af/strings.xml +++ b/packages/SystemUI/res/values-af/strings.xml @@ -419,6 +419,10 @@ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Klik om nuwe toestel saam te bind"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Kon nie voorafstelling opdateer nie"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Voorafstelling"</string> + <!-- no translation found for hearing_devices_input_routing_label (730396728151232306) --> + <skip /> + <!-- no translation found for hearing_device_input_routing_options:0 (4582190415045337003) --> + <!-- no translation found for hearing_device_input_routing_options:1 (8501466270452446450) --> <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Gekies"</string> <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Omgewing"</string> <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Links"</string> @@ -582,6 +586,8 @@ <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Wanneer jy ’n app deel, is enigiets wat in die app wys of speel, sigbaar aan <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Wees dus versigtig met dinge soos wagwoorde, betalingbesonderhede, boodskappe, foto’s, en oudio en video."</string> <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Deel skerm"</string> <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> het hierdie opsie gedeaktiveer"</string> + <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) --> + <skip /> <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Kies app om te deel"</string> <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Saai jou skerm uit?"</string> <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Saai een app uit"</string> @@ -980,6 +986,8 @@ <string name="drag_to_remove_tiles" msgid="4682194717573850385">"Sleep hierheen om te verwyder"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"Jy moet minstens <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> teëls hê"</string> <string name="qs_edit" msgid="5583565172803472437">"Wysig"</string> + <!-- no translation found for qs_edit_tiles (2105215324060865035) --> + <skip /> <string name="tuner_time" msgid="2450785840990529997">"Tyd"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"Wys ure, minute en sekondes"</item> diff --git a/packages/SystemUI/res/values-af/tiles_states_strings.xml b/packages/SystemUI/res/values-af/tiles_states_strings.xml index 64912d4ff9c1..36830c9178c2 100644 --- a/packages/SystemUI/res/values-af/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-af/tiles_states_strings.xml @@ -56,9 +56,11 @@ <item msgid="5376619709702103243">"Af"</item> <item msgid="4875147066469902392">"Aan"</item> </string-array> - <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) --> - <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) --> - <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) --> + <string-array name="tile_states_modes_dnd"> + <item msgid="6509540227356524582">"Onbeskikbaar"</item> + <item msgid="8589336868985358191">"Af"</item> + <item msgid="726072717827778234">"Aan"</item> + </string-array> <string-array name="tile_states_flashlight"> <item msgid="3465257127433353857">"Onbeskikbaar"</item> <item msgid="5044688398303285224">"Af"</item> diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml index 1c158b388d74..3e4ca1c25453 100644 --- a/packages/SystemUI/res/values-am/strings.xml +++ b/packages/SystemUI/res/values-am/strings.xml @@ -419,6 +419,10 @@ <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> + <!-- no translation found for hearing_devices_input_routing_label (730396728151232306) --> + <skip /> + <!-- no translation found for hearing_device_input_routing_options:0 (4582190415045337003) --> + <!-- no translation found for hearing_device_input_routing_options:1 (8501466270452446450) --> <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"ተመርጧል"</string> <string name="hearing_devices_ambient_label" msgid="629440938614895797">"በዙሪያ ያሉ"</string> <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"ግራ"</string> @@ -582,6 +586,8 @@ <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"መተግበሪያን ሲያጋሩ በዚያ መተግበሪያ ውስጥ የሚታይ ወይም የሚጫወት ማንኛውም ነገር ለ<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ይታያል። ስለዚህ እንደ የይለፍ ቃላት፣ የክፍያ ዝርዝሮች፣ መልዕክቶች፣ ፎቶዎች እና ኦዲዮ እና ቪድዮ ላሉ ነገሮች ጥንቃቄ ያድርጉ።"</string> <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"ማያ ገፅ አጋራ"</string> <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> ይህን አማራጭ አሰናክሏል"</string> + <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) --> + <skip /> <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"ለማጋራት መተግበሪያ ይምረጡ"</string> <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"ማያ ገፅዎ cast ይደረግ?"</string> <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"አንድ መተግበሪያ cast ያድርጉ"</string> @@ -980,6 +986,8 @@ <string name="drag_to_remove_tiles" msgid="4682194717573850385">"ለማስወገድ ወደዚህ ይጎትቱ"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"ቢያንስ <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> ሰቆች ያስፈልገዎታል"</string> <string name="qs_edit" msgid="5583565172803472437">"አርትዕ"</string> + <!-- no translation found for qs_edit_tiles (2105215324060865035) --> + <skip /> <string name="tuner_time" msgid="2450785840990529997">"ሰዓት"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"ሰዓቶችን፣ ደቂቃዎችን፣ ሴኮንዶችን አሳይ"</item> diff --git a/packages/SystemUI/res/values-am/tiles_states_strings.xml b/packages/SystemUI/res/values-am/tiles_states_strings.xml index 24666e7c12f7..bd240651c07c 100644 --- a/packages/SystemUI/res/values-am/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-am/tiles_states_strings.xml @@ -56,9 +56,11 @@ <item msgid="5376619709702103243">"ጠፍቷል"</item> <item msgid="4875147066469902392">"በርቷል"</item> </string-array> - <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) --> - <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) --> - <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) --> + <string-array name="tile_states_modes_dnd"> + <item msgid="6509540227356524582">"አይገኝም"</item> + <item msgid="8589336868985358191">"ጠፍቷል"</item> + <item msgid="726072717827778234">"በርቷል"</item> + </string-array> <string-array name="tile_states_flashlight"> <item msgid="3465257127433353857">"አይገኝም"</item> <item msgid="5044688398303285224">"ጠፍቷል"</item> diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml index b494f4ec5fad..f9660a340b46 100644 --- a/packages/SystemUI/res/values-ar/strings.xml +++ b/packages/SystemUI/res/values-ar/strings.xml @@ -419,6 +419,10 @@ <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> + <!-- no translation found for hearing_devices_input_routing_label (730396728151232306) --> + <skip /> + <!-- no translation found for hearing_device_input_routing_options:0 (4582190415045337003) --> + <!-- no translation found for hearing_device_input_routing_options:1 (8501466270452446450) --> <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"تمّ اختياره"</string> <string name="hearing_devices_ambient_label" msgid="629440938614895797">"الأصوات المحيطة"</string> <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"اليسرى"</string> @@ -582,6 +586,8 @@ <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"ستتم مشاركة كل المحتوى المعروض أو المشغَّل على شاشة هذا التطبيق مع تطبيق \"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>\"، لذا يُرجى توخي الحذر بشأن المعلومات الظاهرة على الشاشة، مثل كلمات المرور وتفاصيل الدفع والرسائل والصور والمقاطع الصوتية والفيديوهات."</string> <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"مشاركة الشاشة"</string> <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"تم إيقاف هذا الخيار من خلال تطبيق <xliff:g id="APP_NAME">%1$s</xliff:g>."</string> + <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) --> + <skip /> <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"يُرجى اختيار تطبيق لمشاركة محتواه"</string> <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"هل تريد بث محتوى الشاشة؟"</string> <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"بث محتوى تطبيق واحد"</string> @@ -980,6 +986,8 @@ <string name="drag_to_remove_tiles" msgid="4682194717573850385">"اسحب هنا للإزالة"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"الحدّ الأدنى من عدد المربعات الذي تحتاج إليه هو <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g>"</string> <string name="qs_edit" msgid="5583565172803472437">"تعديل"</string> + <!-- no translation found for qs_edit_tiles (2105215324060865035) --> + <skip /> <string name="tuner_time" msgid="2450785840990529997">"الوقت"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"عرض الساعات والدقائق والثواني"</item> diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml index d3ec875711f5..595432e06666 100644 --- a/packages/SystemUI/res/values-as/strings.xml +++ b/packages/SystemUI/res/values-as/strings.xml @@ -419,6 +419,10 @@ <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> + <!-- no translation found for hearing_devices_input_routing_label (730396728151232306) --> + <skip /> + <!-- no translation found for hearing_device_input_routing_options:0 (4582190415045337003) --> + <!-- no translation found for hearing_device_input_routing_options:1 (8501466270452446450) --> <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"বাছনি কৰা হৈছে"</string> <string name="hearing_devices_ambient_label" msgid="629440938614895797">"আশ-পাশ"</string> <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"বাওঁফাল"</string> @@ -582,6 +586,8 @@ <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"আপুনি কোনো এপ্ শ্বেয়াৰ কৰি থাকোঁতে সেই এপ্টোত দেখুওৱা বা প্লে’ কৰা যিকোনো বস্তু <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>ত দৃশ্যমান হয়। সেয়ে পাছৱৰ্ড, পৰিশোধৰ সবিশেষ, বাৰ্তা, ফট’ আৰু অডিঅ’ আৰু ভিডিঅ’ৰ ক্ষেত্ৰত সাৱধান হওক।"</string> <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"স্ক্ৰীন শ্বেয়াৰ কৰক"</string> <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g>এ এই বিকল্পটো অক্ষম কৰিছে"</string> + <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) --> + <skip /> <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"শ্বেয়াৰ কৰিবলৈ এপ্ বাছনি কৰক"</string> <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"আপোনাৰ স্ক্ৰীনখন কাষ্ট কৰিবনে?"</string> <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"এটা এপ্ কাষ্ট কৰক"</string> @@ -980,6 +986,8 @@ <string name="drag_to_remove_tiles" msgid="4682194717573850385">"আঁতৰাবৰ বাবে টানি আনি ইয়াত এৰি দিয়ক"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"আপোনাক অতিকমেও <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g>খন টাইল লাগিব"</string> <string name="qs_edit" msgid="5583565172803472437">"সম্পাদনা কৰক"</string> + <!-- no translation found for qs_edit_tiles (2105215324060865035) --> + <skip /> <string name="tuner_time" msgid="2450785840990529997">"সময়"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"ঘন্টা, মিনিট আৰু ছেকেণ্ড দেখুৱাওক"</item> diff --git a/packages/SystemUI/res/values-as/tiles_states_strings.xml b/packages/SystemUI/res/values-as/tiles_states_strings.xml index 39bd63e5d27a..d07f11b205cc 100644 --- a/packages/SystemUI/res/values-as/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-as/tiles_states_strings.xml @@ -56,9 +56,11 @@ <item msgid="5376619709702103243">"অফ আছে"</item> <item msgid="4875147066469902392">"অন কৰা আছে"</item> </string-array> - <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) --> - <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) --> - <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) --> + <string-array name="tile_states_modes_dnd"> + <item msgid="6509540227356524582">"উপলব্ধ নহয়"</item> + <item msgid="8589336868985358191">"অফ আছে"</item> + <item msgid="726072717827778234">"অন আছে"</item> + </string-array> <string-array name="tile_states_flashlight"> <item msgid="3465257127433353857">"উপলব্ধ নহয়"</item> <item msgid="5044688398303285224">"অফ আছে"</item> diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml index 9655ab75e61b..3cf675d3ab12 100644 --- a/packages/SystemUI/res/values-az/strings.xml +++ b/packages/SystemUI/res/values-az/strings.xml @@ -419,6 +419,10 @@ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Yeni cihaz birləşdirmək üçün klikləyin"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Hazır ayar güncəllənmədi"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Hazır Ayar"</string> + <!-- no translation found for hearing_devices_input_routing_label (730396728151232306) --> + <skip /> + <!-- no translation found for hearing_device_input_routing_options:0 (4582190415045337003) --> + <!-- no translation found for hearing_device_input_routing_options:1 (8501466270452446450) --> <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Seçilib"</string> <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Ətraf mühit"</string> <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Sol"</string> @@ -582,6 +586,8 @@ <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Tətbiq paylaşdığınız zaman həmin tətbiqdə göstərilən və ya işə salınan hər şey <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> üçün görünən olacaq. Parol, ödəniş məlumatı, mesaj, foto, habelə audio və video kimi məlumatlarla bağlı diqqətli olun."</string> <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Ekranı paylaşın"</string> <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> bu seçimi deaktiv edib"</string> + <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) --> + <skip /> <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Paylaşmaq üçün tətbiq seçin"</string> <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Ekran yayımlansın?"</string> <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Bir tətbiqi yayımlayın"</string> @@ -980,6 +986,8 @@ <string name="drag_to_remove_tiles" msgid="4682194717573850385">"Silmək üçün bura sürüşdürün"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"Minimum <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> mozaika lazımdır"</string> <string name="qs_edit" msgid="5583565172803472437">"Redaktə edin"</string> + <!-- no translation found for qs_edit_tiles (2105215324060865035) --> + <skip /> <string name="tuner_time" msgid="2450785840990529997">"Vaxt"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"Saat, dəqiqə və saniyəni göstərin"</item> diff --git a/packages/SystemUI/res/values-az/tiles_states_strings.xml b/packages/SystemUI/res/values-az/tiles_states_strings.xml index a78e67268f82..9fdca5deed05 100644 --- a/packages/SystemUI/res/values-az/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-az/tiles_states_strings.xml @@ -56,9 +56,11 @@ <item msgid="5376619709702103243">"Deaktiv"</item> <item msgid="4875147066469902392">"Aktiv"</item> </string-array> - <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) --> - <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) --> - <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) --> + <string-array name="tile_states_modes_dnd"> + <item msgid="6509540227356524582">"Əlçatan deyil"</item> + <item msgid="8589336868985358191">"Deaktiv"</item> + <item msgid="726072717827778234">"Aktiv"</item> + </string-array> <string-array name="tile_states_flashlight"> <item msgid="3465257127433353857">"Əlçatan deyil"</item> <item msgid="5044688398303285224">"Deaktiv"</item> diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml index f46399659530..b74094e54d36 100644 --- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml +++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml @@ -419,6 +419,10 @@ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kliknite da biste uparili nov uređaj"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Ažuriranje zadatih podešavanja nije uspelo"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Unapred određena podešavanja"</string> + <!-- no translation found for hearing_devices_input_routing_label (730396728151232306) --> + <skip /> + <!-- no translation found for hearing_device_input_routing_options:0 (4582190415045337003) --> + <!-- no translation found for hearing_device_input_routing_options:1 (8501466270452446450) --> <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Izabrano"</string> <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Okruženje"</string> <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Levo"</string> @@ -582,6 +586,8 @@ <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Kada delite aplikaciju, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> vidi sav sadržaj koji se prikazuje ili pušta u njoj. Zato pazite na lozinke, informacije o plaćanju, poruke, slike, audio i video sadržaj."</string> <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Deli ekran"</string> <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> je onemogućila ovu opciju"</string> + <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) --> + <skip /> <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Odaberite aplikaciju za deljenje"</string> <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Želite da prebacite ekran?"</string> <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Prebaci jednu aplikaciju"</string> @@ -980,6 +986,8 @@ <string name="drag_to_remove_tiles" msgid="4682194717573850385">"Prevucite ovde da biste uklonili"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"Minimalan broj pločica je <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g>"</string> <string name="qs_edit" msgid="5583565172803472437">"Izmeni"</string> + <!-- no translation found for qs_edit_tiles (2105215324060865035) --> + <skip /> <string name="tuner_time" msgid="2450785840990529997">"Vreme"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"Prikaži sate, minute i sekunde"</item> diff --git a/packages/SystemUI/res/values-b+sr+Latn/tiles_states_strings.xml b/packages/SystemUI/res/values-b+sr+Latn/tiles_states_strings.xml index bbfcf840e53f..5784d4735ede 100644 --- a/packages/SystemUI/res/values-b+sr+Latn/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-b+sr+Latn/tiles_states_strings.xml @@ -56,9 +56,11 @@ <item msgid="5376619709702103243">"Isključeno"</item> <item msgid="4875147066469902392">"Uključeno"</item> </string-array> - <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) --> - <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) --> - <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) --> + <string-array name="tile_states_modes_dnd"> + <item msgid="6509540227356524582">"Nedostupno"</item> + <item msgid="8589336868985358191">"Isključeno"</item> + <item msgid="726072717827778234">"Uključeno"</item> + </string-array> <string-array name="tile_states_flashlight"> <item msgid="3465257127433353857">"Nedostupno"</item> <item msgid="5044688398303285224">"Isključeno"</item> diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml index 2ece7bd3900e..11c2715fb2ff 100644 --- a/packages/SystemUI/res/values-be/strings.xml +++ b/packages/SystemUI/res/values-be/strings.xml @@ -419,6 +419,10 @@ <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> + <!-- no translation found for hearing_devices_input_routing_label (730396728151232306) --> + <skip /> + <!-- no translation found for hearing_device_input_routing_options:0 (4582190415045337003) --> + <!-- no translation found for hearing_device_input_routing_options:1 (8501466270452446450) --> <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Выбрана"</string> <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Навакольныя гукі"</string> <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Левы бок"</string> @@ -582,6 +586,8 @@ <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Калі вы абагульваеце праграму, праграма \"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>\" можа бачыць усё, што паказваецца ці прайграецца ў гэтай праграме. Таму прадухіліце паказ пароляў, плацежных рэквізітаў, паведамленняў, фота, відэа і аўдыя."</string> <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Абагуліць экран"</string> <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"Праграма \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" адключыла гэты параметр"</string> + <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) --> + <skip /> <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Выберыце праграму для абагульвання"</string> <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Уключыць трансляцыю экрана?"</string> <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Трансліраваць адну праграму"</string> @@ -980,6 +986,8 @@ <string name="drag_to_remove_tiles" msgid="4682194717573850385">"Перацягніце сюды, каб выдаліць"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"Мінімальная колькасць плітак: <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g>"</string> <string name="qs_edit" msgid="5583565172803472437">"Рэдагаваць"</string> + <!-- no translation found for qs_edit_tiles (2105215324060865035) --> + <skip /> <string name="tuner_time" msgid="2450785840990529997">"Час"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"Паказваць гадзіны, хвіліны і секунды"</item> diff --git a/packages/SystemUI/res/values-be/tiles_states_strings.xml b/packages/SystemUI/res/values-be/tiles_states_strings.xml index e6b35439ce44..67468b4a983e 100644 --- a/packages/SystemUI/res/values-be/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-be/tiles_states_strings.xml @@ -56,9 +56,11 @@ <item msgid="5376619709702103243">"Выключана"</item> <item msgid="4875147066469902392">"Уключана"</item> </string-array> - <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) --> - <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) --> - <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) --> + <string-array name="tile_states_modes_dnd"> + <item msgid="6509540227356524582">"Недаступна"</item> + <item msgid="8589336868985358191">"Выключана"</item> + <item msgid="726072717827778234">"Уключана"</item> + </string-array> <string-array name="tile_states_flashlight"> <item msgid="3465257127433353857">"Недаступна"</item> <item msgid="5044688398303285224">"Выключана"</item> diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml index 3ab943a658e6..a085b1545867 100644 --- a/packages/SystemUI/res/values-bg/strings.xml +++ b/packages/SystemUI/res/values-bg/strings.xml @@ -419,6 +419,10 @@ <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> + <!-- no translation found for hearing_devices_input_routing_label (730396728151232306) --> + <skip /> + <!-- no translation found for hearing_device_input_routing_options:0 (4582190415045337003) --> + <!-- no translation found for hearing_device_input_routing_options:1 (8501466270452446450) --> <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Избрано"</string> <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Околни звуци"</string> <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Ляво"</string> @@ -582,6 +586,8 @@ <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Когато споделяте приложение, всичко, което се показва или възпроизвежда в него, е видимо за <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Затова бъдете внимателни с неща като пароли, подробности за начини на плащане, съобщения, снимки, аудио и видео."</string> <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Споделяне на екрана"</string> <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> деактивира тази опция"</string> + <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) --> + <skip /> <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Изберете приложение за споделяне"</string> <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Искате ли да предавате екрана си?"</string> <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Предаване на едно приложение"</string> @@ -980,6 +986,8 @@ <string name="drag_to_remove_tiles" msgid="4682194717573850385">"Преместете тук с плъзгане за премахване"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"Трябва да останат поне <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> плочки"</string> <string name="qs_edit" msgid="5583565172803472437">"Редактиране"</string> + <!-- no translation found for qs_edit_tiles (2105215324060865035) --> + <skip /> <string name="tuner_time" msgid="2450785840990529997">"Час"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"Показване на часовете, минутите и секундите"</item> diff --git a/packages/SystemUI/res/values-bg/tiles_states_strings.xml b/packages/SystemUI/res/values-bg/tiles_states_strings.xml index b46d4dffb5fd..535ddcdcc7b6 100644 --- a/packages/SystemUI/res/values-bg/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-bg/tiles_states_strings.xml @@ -56,9 +56,11 @@ <item msgid="5376619709702103243">"Изкл."</item> <item msgid="4875147066469902392">"Вкл."</item> </string-array> - <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) --> - <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) --> - <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) --> + <string-array name="tile_states_modes_dnd"> + <item msgid="6509540227356524582">"Не е налице"</item> + <item msgid="8589336868985358191">"Изкл."</item> + <item msgid="726072717827778234">"Вкл."</item> + </string-array> <string-array name="tile_states_flashlight"> <item msgid="3465257127433353857">"Не е налице"</item> <item msgid="5044688398303285224">"Изкл."</item> diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml index 4ab650edc56d..18d632e95502 100644 --- a/packages/SystemUI/res/values-bn/strings.xml +++ b/packages/SystemUI/res/values-bn/strings.xml @@ -419,6 +419,10 @@ <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> + <!-- no translation found for hearing_devices_input_routing_label (730396728151232306) --> + <skip /> + <!-- no translation found for hearing_device_input_routing_options:0 (4582190415045337003) --> + <!-- no translation found for hearing_device_input_routing_options:1 (8501466270452446450) --> <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"বেছে নেওয়া হয়েছে"</string> <string name="hearing_devices_ambient_label" msgid="629440938614895797">"সারাউন্ডিং"</string> <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"বাঁদিক"</string> @@ -582,6 +586,8 @@ <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"কোনও অ্যাপ শেয়ার করার সময়, সেই অ্যাপে দেখা ও চালানো হয় এমন সব কিছু <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> দেখতে পাবে। তাই পাসওয়ার্ড, পেমেন্টের বিবরণ, মেসেজ, ফটো এবং অডিও ও ভিডিওর মতো বিষয়ের ক্ষেত্রে সতর্ক থাকুন।"</string> <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"স্ক্রিন শেয়ার করুন"</string> <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> এই বিকল্পটি বন্ধ করে দিয়েছে"</string> + <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) --> + <skip /> <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"শেয়ার করার জন্য অ্যাপ বেছে নিন"</string> <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"আপনার স্ক্রিন কাস্ট করতে চান?"</string> <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"একটি অ্যাপ কাস্ট করুন"</string> @@ -980,6 +986,8 @@ <string name="drag_to_remove_tiles" msgid="4682194717573850385">"সরানোর জন্য এখানে টেনে আনুন"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"আপনাকে কমপক্ষে <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g>টি টাইল রাখতে হবে"</string> <string name="qs_edit" msgid="5583565172803472437">"এডিট করুন"</string> + <!-- no translation found for qs_edit_tiles (2105215324060865035) --> + <skip /> <string name="tuner_time" msgid="2450785840990529997">"সময়"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"ঘণ্টা, মিনিট, এবং সেকেন্ড দেখান"</item> diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml index afa877ee5c0a..c18af99aafc0 100644 --- a/packages/SystemUI/res/values-bs/strings.xml +++ b/packages/SystemUI/res/values-bs/strings.xml @@ -419,6 +419,10 @@ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kliknite da uparite novi uređaj"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Ažuriranje zadane postavke nije uspjelo"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Zadana postavka"</string> + <!-- no translation found for hearing_devices_input_routing_label (730396728151232306) --> + <skip /> + <!-- no translation found for hearing_device_input_routing_options:0 (4582190415045337003) --> + <!-- no translation found for hearing_device_input_routing_options:1 (8501466270452446450) --> <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Odabrano"</string> <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Okruženje"</string> <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Lijevo"</string> @@ -582,6 +586,8 @@ <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Kada dijelite aplikaciju, sve što se prikazuje ili reproducira u toj aplikaciji će biti vidljivo aplikaciji <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Stoga budite oprezni s informacijama kao što su lozinke, podaci o plaćanju, poruke, fotografije, audio i videozapisi."</string> <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Dijeli ekran"</string> <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> je onemogućila tu opciju"</string> + <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) --> + <skip /> <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Odaberite aplikaciju koju želite dijeliti"</string> <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Emitirati ekran?"</string> <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Emitiraj jednu aplikaciju"</string> @@ -980,6 +986,8 @@ <string name="drag_to_remove_tiles" msgid="4682194717573850385">"Prevucite ovdje za uklanjanje"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"Broj polja mora biti najmanje <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g>"</string> <string name="qs_edit" msgid="5583565172803472437">"Uredite"</string> + <!-- no translation found for qs_edit_tiles (2105215324060865035) --> + <skip /> <string name="tuner_time" msgid="2450785840990529997">"Vrijeme"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"Prikaži sate, minute i sekunde"</item> diff --git a/packages/SystemUI/res/values-bs/tiles_states_strings.xml b/packages/SystemUI/res/values-bs/tiles_states_strings.xml index bbfcf840e53f..5784d4735ede 100644 --- a/packages/SystemUI/res/values-bs/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-bs/tiles_states_strings.xml @@ -56,9 +56,11 @@ <item msgid="5376619709702103243">"Isključeno"</item> <item msgid="4875147066469902392">"Uključeno"</item> </string-array> - <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) --> - <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) --> - <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) --> + <string-array name="tile_states_modes_dnd"> + <item msgid="6509540227356524582">"Nedostupno"</item> + <item msgid="8589336868985358191">"Isključeno"</item> + <item msgid="726072717827778234">"Uključeno"</item> + </string-array> <string-array name="tile_states_flashlight"> <item msgid="3465257127433353857">"Nedostupno"</item> <item msgid="5044688398303285224">"Isključeno"</item> diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml index 3e43c7924678..0404bc70b75e 100644 --- a/packages/SystemUI/res/values-ca/strings.xml +++ b/packages/SystemUI/res/values-ca/strings.xml @@ -419,6 +419,10 @@ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Fes clic per vincular un dispositiu nou"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"No s\'ha pogut actualitzar el valor predefinit"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Valors predefinits"</string> + <!-- no translation found for hearing_devices_input_routing_label (730396728151232306) --> + <skip /> + <!-- no translation found for hearing_device_input_routing_options:0 (4582190415045337003) --> + <!-- no translation found for hearing_device_input_routing_options:1 (8501466270452446450) --> <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Seleccionat"</string> <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Entorn"</string> <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Esquerra"</string> @@ -582,6 +586,8 @@ <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Quan comparteixes una aplicació, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> pot veure qualsevol cosa que s\'hi mostra o que s\'hi reprodueix. Per aquest motiu, ves amb compte amb les contrasenyes, les dades de pagament, els missatges, les fotos i l\'àudio i el vídeo."</string> <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Comparteix la pantalla"</string> <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> ha desactivat aquesta opció"</string> + <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) --> + <skip /> <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Tria una aplicació per compartir"</string> <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Vols emetre la pantalla?"</string> <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Emet una aplicació"</string> @@ -980,6 +986,8 @@ <string name="drag_to_remove_tiles" msgid="4682194717573850385">"Arrossega aquí per suprimir"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"Necessites com a mínim <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> mosaics"</string> <string name="qs_edit" msgid="5583565172803472437">"Edita"</string> + <!-- no translation found for qs_edit_tiles (2105215324060865035) --> + <skip /> <string name="tuner_time" msgid="2450785840990529997">"Hora"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"Mostra les hores, els minuts i els segons"</item> diff --git a/packages/SystemUI/res/values-ca/tiles_states_strings.xml b/packages/SystemUI/res/values-ca/tiles_states_strings.xml index 5094d57a0ecf..7afc00f8c149 100644 --- a/packages/SystemUI/res/values-ca/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-ca/tiles_states_strings.xml @@ -56,9 +56,11 @@ <item msgid="5376619709702103243">"Desactivat"</item> <item msgid="4875147066469902392">"Activat"</item> </string-array> - <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) --> - <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) --> - <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) --> + <string-array name="tile_states_modes_dnd"> + <item msgid="6509540227356524582">"No disponible"</item> + <item msgid="8589336868985358191">"Desactivat"</item> + <item msgid="726072717827778234">"Activat"</item> + </string-array> <string-array name="tile_states_flashlight"> <item msgid="3465257127433353857">"No disponible"</item> <item msgid="5044688398303285224">"Desactivat"</item> diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml index f374913a56cf..e438d6332dd0 100644 --- a/packages/SystemUI/res/values-cs/strings.xml +++ b/packages/SystemUI/res/values-cs/strings.xml @@ -419,6 +419,10 @@ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kliknutím spárujete nové zařízení"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Předvolbu nelze aktualizovat"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Předvolba"</string> + <!-- no translation found for hearing_devices_input_routing_label (730396728151232306) --> + <skip /> + <!-- no translation found for hearing_device_input_routing_options:0 (4582190415045337003) --> + <!-- no translation found for hearing_device_input_routing_options:1 (8501466270452446450) --> <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Vybráno"</string> <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Okolí"</string> <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Vlevo"</string> @@ -582,6 +586,8 @@ <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Při sdílení aplikace vidí <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> vše, co se ve sdílené aplikaci nachází nebo děje. Buďte proto opatrní, když jde o hesla, platební údaje, zprávy, fotografie, zvuk a video."</string> <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Sdílet obrazovku"</string> <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"Aplikace <xliff:g id="APP_NAME">%1$s</xliff:g> tuto možnost zakázala"</string> + <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) --> + <skip /> <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Vyberte aplikaci ke sdílení"</string> <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Odeslat obrazovku?"</string> <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Odeslat jednu aplikaci"</string> @@ -980,6 +986,8 @@ <string name="drag_to_remove_tiles" msgid="4682194717573850385">"Přetažením sem dlaždice odstraníte"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"Potřebujete alespoň tento počet dlaždic: <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g>"</string> <string name="qs_edit" msgid="5583565172803472437">"Upravit"</string> + <!-- no translation found for qs_edit_tiles (2105215324060865035) --> + <skip /> <string name="tuner_time" msgid="2450785840990529997">"Čas"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"Zobrazovat hodiny, minuty a sekundy"</item> diff --git a/packages/SystemUI/res/values-cs/tiles_states_strings.xml b/packages/SystemUI/res/values-cs/tiles_states_strings.xml index 0c7db6763eeb..46ef4ead74f0 100644 --- a/packages/SystemUI/res/values-cs/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-cs/tiles_states_strings.xml @@ -56,9 +56,11 @@ <item msgid="5376619709702103243">"Vypnuto"</item> <item msgid="4875147066469902392">"Zapnuto"</item> </string-array> - <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) --> - <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) --> - <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) --> + <string-array name="tile_states_modes_dnd"> + <item msgid="6509540227356524582">"Nedostupné"</item> + <item msgid="8589336868985358191">"Vypnuto"</item> + <item msgid="726072717827778234">"Zapnuto"</item> + </string-array> <string-array name="tile_states_flashlight"> <item msgid="3465257127433353857">"Nedostupné"</item> <item msgid="5044688398303285224">"Vypnuto"</item> diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml index 0875aad0986e..52abc4a2fda5 100644 --- a/packages/SystemUI/res/values-da/strings.xml +++ b/packages/SystemUI/res/values-da/strings.xml @@ -419,6 +419,10 @@ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Klik for at parre en ny enhed"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Forindstillingen kunne ikke opdateres"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Forindstilling"</string> + <!-- no translation found for hearing_devices_input_routing_label (730396728151232306) --> + <skip /> + <!-- no translation found for hearing_device_input_routing_options:0 (4582190415045337003) --> + <!-- no translation found for hearing_device_input_routing_options:1 (8501466270452446450) --> <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Valgt"</string> <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Omgivelser"</string> <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Venstre"</string> @@ -582,6 +586,8 @@ <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Når du deler en app, er alt, der vises eller afspilles i den pågældende app, synligt for <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Vær derfor forsigtig med f.eks. adgangskoder, betalingsoplysninger, beskeder, billeder, lyd og video."</string> <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Del skærm"</string> <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> har deaktiveret denne valgmulighed"</string> + <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) --> + <skip /> <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Vælg den app, du vil dele fra"</string> <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Vil du caste din skærm?"</string> <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Cast én app"</string> @@ -980,6 +986,8 @@ <string name="drag_to_remove_tiles" msgid="4682194717573850385">"Træk herhen for at fjerne"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"Du skal bruge mindst <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> felter"</string> <string name="qs_edit" msgid="5583565172803472437">"Rediger"</string> + <!-- no translation found for qs_edit_tiles (2105215324060865035) --> + <skip /> <string name="tuner_time" msgid="2450785840990529997">"Tid"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"Vis timer, minutter og sekunder"</item> diff --git a/packages/SystemUI/res/values-da/tiles_states_strings.xml b/packages/SystemUI/res/values-da/tiles_states_strings.xml index 5558b0b5433f..b4a3eaffda8d 100644 --- a/packages/SystemUI/res/values-da/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-da/tiles_states_strings.xml @@ -56,9 +56,11 @@ <item msgid="5376619709702103243">"Fra"</item> <item msgid="4875147066469902392">"Til"</item> </string-array> - <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) --> - <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) --> - <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) --> + <string-array name="tile_states_modes_dnd"> + <item msgid="6509540227356524582">"Ikke tilgængelig"</item> + <item msgid="8589336868985358191">"Fra"</item> + <item msgid="726072717827778234">"Til"</item> + </string-array> <string-array name="tile_states_flashlight"> <item msgid="3465257127433353857">"Ikke tilgængelig"</item> <item msgid="5044688398303285224">"Fra"</item> diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml index 8b783fdcc161..612c52f5c127 100644 --- a/packages/SystemUI/res/values-de/strings.xml +++ b/packages/SystemUI/res/values-de/strings.xml @@ -419,6 +419,10 @@ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Klicken, um neues Gerät zu koppeln"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Voreinstellung konnte nicht aktualisiert werden"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Voreinstellung"</string> + <!-- no translation found for hearing_devices_input_routing_label (730396728151232306) --> + <skip /> + <!-- no translation found for hearing_device_input_routing_options:0 (4582190415045337003) --> + <!-- no translation found for hearing_device_input_routing_options:1 (8501466270452446450) --> <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Ausgewählt"</string> <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Umgebungsgeräusche"</string> <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Links"</string> @@ -582,6 +586,8 @@ <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Wenn du eine App streamst, ist für <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> alles sichtbar, was in dieser App angezeigt oder abgespielt wird. Sei also vorsichtig mit Informationen wie Passwörtern, Zahlungsdetails, Nachrichten, Fotos sowie Audio- und Videoinhalten."</string> <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Bildschirm teilen"</string> <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> hat diese Option deaktiviert"</string> + <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) --> + <skip /> <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"App zum Teilen auswählen"</string> <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Bildschirm streamen?"</string> <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Eine App streamen"</string> @@ -980,6 +986,8 @@ <string name="drag_to_remove_tiles" msgid="4682194717573850385">"Zum Entfernen hierher ziehen"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"Du brauchst mindestens <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> Kacheln"</string> <string name="qs_edit" msgid="5583565172803472437">"Bearbeiten"</string> + <!-- no translation found for qs_edit_tiles (2105215324060865035) --> + <skip /> <string name="tuner_time" msgid="2450785840990529997">"Uhrzeit"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"Stunden, Minuten und Sekunden anzeigen"</item> diff --git a/packages/SystemUI/res/values-de/tiles_states_strings.xml b/packages/SystemUI/res/values-de/tiles_states_strings.xml index 2d4d1b6eb3da..ecae8dc4e0dd 100644 --- a/packages/SystemUI/res/values-de/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-de/tiles_states_strings.xml @@ -56,9 +56,11 @@ <item msgid="5376619709702103243">"Aus"</item> <item msgid="4875147066469902392">"An"</item> </string-array> - <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) --> - <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) --> - <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) --> + <string-array name="tile_states_modes_dnd"> + <item msgid="6509540227356524582">"Nicht verfügbar"</item> + <item msgid="8589336868985358191">"Aus"</item> + <item msgid="726072717827778234">"An"</item> + </string-array> <string-array name="tile_states_flashlight"> <item msgid="3465257127433353857">"Nicht verfügbar"</item> <item msgid="5044688398303285224">"Aus"</item> diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml index c1a9a3a4c19d..2c17a394b022 100644 --- a/packages/SystemUI/res/values-el/strings.xml +++ b/packages/SystemUI/res/values-el/strings.xml @@ -419,6 +419,10 @@ <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> + <!-- no translation found for hearing_devices_input_routing_label (730396728151232306) --> + <skip /> + <!-- no translation found for hearing_device_input_routing_options:0 (4582190415045337003) --> + <!-- no translation found for hearing_device_input_routing_options:1 (8501466270452446450) --> <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Έχει επιλεγεί"</string> <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Ήχοι περιβάλλοντος"</string> <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Αριστερά"</string> @@ -582,6 +586,8 @@ <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Όταν μοιράζεστε μια εφαρμογή, οτιδήποτε εμφανίζεται ή αναπαράγεται σε αυτή την εφαρμογή, είναι ορατό στην εφαρμογή <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Επομένως, να είστε προσεκτικοί με τους κωδικούς πρόσβασης, τα στοιχεία πληρωμής, τα μηνύματα, τις φωτογραφίες, τον ήχο και το βίντεο."</string> <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Κοινή χρήση οθόνης"</string> <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"Η εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g> απενεργοποίησε αυτή την επιλογή"</string> + <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) --> + <skip /> <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Επιλογή εφαρμογής για κοινή χρήση"</string> <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Να γίνει μετάδοση της οθόνης σας;"</string> <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Μετάδοση μίας εφαρμογής"</string> @@ -980,6 +986,8 @@ <string name="drag_to_remove_tiles" msgid="4682194717573850385">"Σύρετε εδώ για κατάργηση"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"Χρειάζεστε τουλάχιστον <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> πλακίδια"</string> <string name="qs_edit" msgid="5583565172803472437">"Επεξεργασία"</string> + <!-- no translation found for qs_edit_tiles (2105215324060865035) --> + <skip /> <string name="tuner_time" msgid="2450785840990529997">"Ώρα"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"Να εμφανίζονται ώρες, λεπτά και δευτερόλεπτα"</item> diff --git a/packages/SystemUI/res/values-el/tiles_states_strings.xml b/packages/SystemUI/res/values-el/tiles_states_strings.xml index 76e70719d11f..d8910b3955b9 100644 --- a/packages/SystemUI/res/values-el/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-el/tiles_states_strings.xml @@ -56,9 +56,11 @@ <item msgid="5376619709702103243">"Ανενεργό"</item> <item msgid="4875147066469902392">"Ενεργό"</item> </string-array> - <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) --> - <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) --> - <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) --> + <string-array name="tile_states_modes_dnd"> + <item msgid="6509540227356524582">"Μη διαθέσιμο"</item> + <item msgid="8589336868985358191">"Ανενεργό"</item> + <item msgid="726072717827778234">"Ενεργό"</item> + </string-array> <string-array name="tile_states_flashlight"> <item msgid="3465257127433353857">"Μη διαθέσιμο"</item> <item msgid="5044688398303285224">"Ανενεργός"</item> diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml index 1fee916f670e..7b8f98237c7a 100644 --- a/packages/SystemUI/res/values-en-rAU/strings.xml +++ b/packages/SystemUI/res/values-en-rAU/strings.xml @@ -419,6 +419,10 @@ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Click to pair new device"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Couldn\'t update preset"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Preset"</string> + <!-- no translation found for hearing_devices_input_routing_label (730396728151232306) --> + <skip /> + <!-- no translation found for hearing_device_input_routing_options:0 (4582190415045337003) --> + <!-- no translation found for hearing_device_input_routing_options:1 (8501466270452446450) --> <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Selected"</string> <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Surroundings"</string> <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Left"</string> @@ -582,6 +586,8 @@ <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"When you\'re sharing an app, anything shown or played in that app is visible to <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. So be careful with things like passwords, payment details, messages, photos, audio and video."</string> <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Share screen"</string> <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> has disabled this option"</string> + <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) --> + <skip /> <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Choose app to share"</string> <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Cast your screen?"</string> <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Cast one app"</string> @@ -980,6 +986,8 @@ <string name="drag_to_remove_tiles" msgid="4682194717573850385">"Drag here to remove"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"You need at least <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> tiles"</string> <string name="qs_edit" msgid="5583565172803472437">"Edit"</string> + <!-- no translation found for qs_edit_tiles (2105215324060865035) --> + <skip /> <string name="tuner_time" msgid="2450785840990529997">"Time"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"Show hours, minutes and seconds"</item> diff --git a/packages/SystemUI/res/values-en-rAU/tiles_states_strings.xml b/packages/SystemUI/res/values-en-rAU/tiles_states_strings.xml index 34580ebc9b99..3014e6207e7d 100644 --- a/packages/SystemUI/res/values-en-rAU/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-en-rAU/tiles_states_strings.xml @@ -56,9 +56,11 @@ <item msgid="5376619709702103243">"Off"</item> <item msgid="4875147066469902392">"On"</item> </string-array> - <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) --> - <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) --> - <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) --> + <string-array name="tile_states_modes_dnd"> + <item msgid="6509540227356524582">"Unavailable"</item> + <item msgid="8589336868985358191">"Off"</item> + <item msgid="726072717827778234">"On"</item> + </string-array> <string-array name="tile_states_flashlight"> <item msgid="3465257127433353857">"Unavailable"</item> <item msgid="5044688398303285224">"Off"</item> diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml index 318f0b43332e..15056c6e9a08 100644 --- a/packages/SystemUI/res/values-en-rCA/strings.xml +++ b/packages/SystemUI/res/values-en-rCA/strings.xml @@ -419,6 +419,11 @@ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Click to pair new device"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Couldn\'t update preset"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Preset"</string> + <string name="hearing_devices_input_routing_label" msgid="730396728151232306">"Default microphone for calls"</string> + <string-array name="hearing_device_input_routing_options"> + <item msgid="4582190415045337003">"Hearing aid microphone"</item> + <item msgid="8501466270452446450">"This phone\'s microphone"</item> + </string-array> <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Selected"</string> <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Surroundings"</string> <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Left"</string> @@ -582,6 +587,7 @@ <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"When you’re sharing an app, anything shown or played in that app is visible to <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. So be careful with things like passwords, payment details, messages, photos, and audio and video."</string> <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Share screen"</string> <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> has disabled this option"</string> + <string name="media_projection_entry_app_permission_dialog_single_app_not_supported" msgid="4860247304058870233">"Not supported by the app"</string> <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Choose app to share"</string> <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Cast your screen?"</string> <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Cast one app"</string> @@ -979,6 +985,8 @@ <string name="drag_to_remove_tiles" msgid="4682194717573850385">"Drag here to remove"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"You need at least <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> tiles"</string> <string name="qs_edit" msgid="5583565172803472437">"Edit"</string> + <!-- no translation found for qs_edit_tiles (2105215324060865035) --> + <skip /> <string name="tuner_time" msgid="2450785840990529997">"Time"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"Show hours, minutes, and seconds"</item> diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml index 1fee916f670e..7b8f98237c7a 100644 --- a/packages/SystemUI/res/values-en-rGB/strings.xml +++ b/packages/SystemUI/res/values-en-rGB/strings.xml @@ -419,6 +419,10 @@ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Click to pair new device"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Couldn\'t update preset"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Preset"</string> + <!-- no translation found for hearing_devices_input_routing_label (730396728151232306) --> + <skip /> + <!-- no translation found for hearing_device_input_routing_options:0 (4582190415045337003) --> + <!-- no translation found for hearing_device_input_routing_options:1 (8501466270452446450) --> <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Selected"</string> <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Surroundings"</string> <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Left"</string> @@ -582,6 +586,8 @@ <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"When you\'re sharing an app, anything shown or played in that app is visible to <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. So be careful with things like passwords, payment details, messages, photos, audio and video."</string> <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Share screen"</string> <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> has disabled this option"</string> + <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) --> + <skip /> <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Choose app to share"</string> <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Cast your screen?"</string> <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Cast one app"</string> @@ -980,6 +986,8 @@ <string name="drag_to_remove_tiles" msgid="4682194717573850385">"Drag here to remove"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"You need at least <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> tiles"</string> <string name="qs_edit" msgid="5583565172803472437">"Edit"</string> + <!-- no translation found for qs_edit_tiles (2105215324060865035) --> + <skip /> <string name="tuner_time" msgid="2450785840990529997">"Time"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"Show hours, minutes and seconds"</item> diff --git a/packages/SystemUI/res/values-en-rGB/tiles_states_strings.xml b/packages/SystemUI/res/values-en-rGB/tiles_states_strings.xml index 34580ebc9b99..3014e6207e7d 100644 --- a/packages/SystemUI/res/values-en-rGB/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-en-rGB/tiles_states_strings.xml @@ -56,9 +56,11 @@ <item msgid="5376619709702103243">"Off"</item> <item msgid="4875147066469902392">"On"</item> </string-array> - <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) --> - <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) --> - <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) --> + <string-array name="tile_states_modes_dnd"> + <item msgid="6509540227356524582">"Unavailable"</item> + <item msgid="8589336868985358191">"Off"</item> + <item msgid="726072717827778234">"On"</item> + </string-array> <string-array name="tile_states_flashlight"> <item msgid="3465257127433353857">"Unavailable"</item> <item msgid="5044688398303285224">"Off"</item> diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml index 1fee916f670e..7b8f98237c7a 100644 --- a/packages/SystemUI/res/values-en-rIN/strings.xml +++ b/packages/SystemUI/res/values-en-rIN/strings.xml @@ -419,6 +419,10 @@ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Click to pair new device"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Couldn\'t update preset"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Preset"</string> + <!-- no translation found for hearing_devices_input_routing_label (730396728151232306) --> + <skip /> + <!-- no translation found for hearing_device_input_routing_options:0 (4582190415045337003) --> + <!-- no translation found for hearing_device_input_routing_options:1 (8501466270452446450) --> <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Selected"</string> <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Surroundings"</string> <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Left"</string> @@ -582,6 +586,8 @@ <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"When you\'re sharing an app, anything shown or played in that app is visible to <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. So be careful with things like passwords, payment details, messages, photos, audio and video."</string> <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Share screen"</string> <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> has disabled this option"</string> + <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) --> + <skip /> <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Choose app to share"</string> <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Cast your screen?"</string> <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Cast one app"</string> @@ -980,6 +986,8 @@ <string name="drag_to_remove_tiles" msgid="4682194717573850385">"Drag here to remove"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"You need at least <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> tiles"</string> <string name="qs_edit" msgid="5583565172803472437">"Edit"</string> + <!-- no translation found for qs_edit_tiles (2105215324060865035) --> + <skip /> <string name="tuner_time" msgid="2450785840990529997">"Time"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"Show hours, minutes and seconds"</item> diff --git a/packages/SystemUI/res/values-en-rIN/tiles_states_strings.xml b/packages/SystemUI/res/values-en-rIN/tiles_states_strings.xml index 34580ebc9b99..3014e6207e7d 100644 --- a/packages/SystemUI/res/values-en-rIN/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-en-rIN/tiles_states_strings.xml @@ -56,9 +56,11 @@ <item msgid="5376619709702103243">"Off"</item> <item msgid="4875147066469902392">"On"</item> </string-array> - <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) --> - <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) --> - <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) --> + <string-array name="tile_states_modes_dnd"> + <item msgid="6509540227356524582">"Unavailable"</item> + <item msgid="8589336868985358191">"Off"</item> + <item msgid="726072717827778234">"On"</item> + </string-array> <string-array name="tile_states_flashlight"> <item msgid="3465257127433353857">"Unavailable"</item> <item msgid="5044688398303285224">"Off"</item> diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml index 486b43a2e867..1b15b1b4fe40 100644 --- a/packages/SystemUI/res/values-es-rUS/strings.xml +++ b/packages/SystemUI/res/values-es-rUS/strings.xml @@ -419,6 +419,10 @@ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Haz clic para vincular un dispositivo nuevo"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"No se pudo actualizar el ajuste predeterminado"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Ajuste predeterminado"</string> + <!-- no translation found for hearing_devices_input_routing_label (730396728151232306) --> + <skip /> + <!-- no translation found for hearing_device_input_routing_options:0 (4582190415045337003) --> + <!-- no translation found for hearing_device_input_routing_options:1 (8501466270452446450) --> <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Seleccionado"</string> <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Sonido envolvente"</string> <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Izquierda"</string> @@ -582,6 +586,8 @@ <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Cuando compartes una app, todo lo que se muestre o reproduzca en ella será visible en <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Por lo tanto, debes tener cuidado con contraseñas, detalles de pagos, mensajes, fotos, audios y videos."</string> <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Compartir pantalla"</string> <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> inhabilitó esta opción"</string> + <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) --> + <skip /> <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Elige la app para compartir"</string> <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"¿Quieres transmitir la pantalla?"</string> <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Transmitir una app"</string> @@ -980,6 +986,8 @@ <string name="drag_to_remove_tiles" msgid="4682194717573850385">"Arrastra aquí para quitar"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"Necesitas al menos <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> tarjetas"</string> <string name="qs_edit" msgid="5583565172803472437">"Editar"</string> + <!-- no translation found for qs_edit_tiles (2105215324060865035) --> + <skip /> <string name="tuner_time" msgid="2450785840990529997">"Hora"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"Mostrar horas, minutos y segundos"</item> diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml index ecd3fdab13aa..2311b48b6570 100644 --- a/packages/SystemUI/res/values-es/strings.xml +++ b/packages/SystemUI/res/values-es/strings.xml @@ -419,6 +419,10 @@ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Haz clic para emparejar un nuevo dispositivo"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"No se ha podido actualizar el preajuste"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Preajuste"</string> + <!-- no translation found for hearing_devices_input_routing_label (730396728151232306) --> + <skip /> + <!-- no translation found for hearing_device_input_routing_options:0 (4582190415045337003) --> + <!-- no translation found for hearing_device_input_routing_options:1 (8501466270452446450) --> <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Seleccionado"</string> <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Alrededores"</string> <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Izquierda"</string> @@ -582,6 +586,8 @@ <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Cuando compartes una aplicación, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> puede ver todo lo que se muestra o reproduce en ella. Debes tener cuidado con elementos como contraseñas, detalles de pagos, mensajes, fotos, audio y vídeo."</string> <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Compartir pantalla"</string> <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> ha inhabilitado esta opción"</string> + <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) --> + <skip /> <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Elige la aplicación que quieres compartir"</string> <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"¿Enviar tu pantalla?"</string> <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Enviar una aplicación"</string> @@ -980,6 +986,8 @@ <string name="drag_to_remove_tiles" msgid="4682194717573850385">"Arrastra aquí para quitar una función"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"Necesitas al menos <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> recuadros"</string> <string name="qs_edit" msgid="5583565172803472437">"Editar"</string> + <!-- no translation found for qs_edit_tiles (2105215324060865035) --> + <skip /> <string name="tuner_time" msgid="2450785840990529997">"Hora"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"Mostrar horas, minutos y segundos"</item> diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml index 2928c96b9689..9d221715da7e 100644 --- a/packages/SystemUI/res/values-et/strings.xml +++ b/packages/SystemUI/res/values-et/strings.xml @@ -419,6 +419,10 @@ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Uue seadme sidumiseks klõpsake"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Eelseadistust ei saanud värskendada"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Eelseadistus"</string> + <!-- no translation found for hearing_devices_input_routing_label (730396728151232306) --> + <skip /> + <!-- no translation found for hearing_device_input_routing_options:0 (4582190415045337003) --> + <!-- no translation found for hearing_device_input_routing_options:1 (8501466270452446450) --> <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Valitud"</string> <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Ümbritsevad helid"</string> <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Vasakule"</string> @@ -582,6 +586,8 @@ <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Rakenduse jagamisel on kogu rakenduses kuvatav või esitatav sisu nähtav rakendusele <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Seega olge ettevaatlik selliste andmetega nagu paroolid, makseteave, sõnumid, fotod ning heli ja video."</string> <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Jaga ekraani"</string> <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> on selle valiku keelanud"</string> + <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) --> + <skip /> <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Vali jagamiseks rakendus"</string> <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Kas kanda ekraanikuva üle?"</string> <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Ühe rakenduse ülekandmine"</string> @@ -980,6 +986,8 @@ <string name="drag_to_remove_tiles" msgid="4682194717573850385">"Lohistage eemaldamiseks siia"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"Teil on vaja vähemalt <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> paani"</string> <string name="qs_edit" msgid="5583565172803472437">"Muutmine"</string> + <!-- no translation found for qs_edit_tiles (2105215324060865035) --> + <skip /> <string name="tuner_time" msgid="2450785840990529997">"Kellaaeg"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"Kuva tunnid, minutid ja sekundid"</item> diff --git a/packages/SystemUI/res/values-et/tiles_states_strings.xml b/packages/SystemUI/res/values-et/tiles_states_strings.xml index 1defe925cf75..819e9761225c 100644 --- a/packages/SystemUI/res/values-et/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-et/tiles_states_strings.xml @@ -56,9 +56,11 @@ <item msgid="5376619709702103243">"Väljas"</item> <item msgid="4875147066469902392">"Sees"</item> </string-array> - <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) --> - <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) --> - <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) --> + <string-array name="tile_states_modes_dnd"> + <item msgid="6509540227356524582">"Pole saadaval"</item> + <item msgid="8589336868985358191">"Väljas"</item> + <item msgid="726072717827778234">"Sees"</item> + </string-array> <string-array name="tile_states_flashlight"> <item msgid="3465257127433353857">"Pole saadaval"</item> <item msgid="5044688398303285224">"Väljas"</item> diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml index 99dec017c0f4..443632bb543a 100644 --- a/packages/SystemUI/res/values-eu/strings.xml +++ b/packages/SystemUI/res/values-eu/strings.xml @@ -419,6 +419,10 @@ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Egin klik beste gailu bat parekatzeko"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Ezin izan da eguneratu aurrezarpena"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Aurrezarpena"</string> + <!-- no translation found for hearing_devices_input_routing_label (730396728151232306) --> + <skip /> + <!-- no translation found for hearing_device_input_routing_options:0 (4582190415045337003) --> + <!-- no translation found for hearing_device_input_routing_options:1 (8501466270452446450) --> <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Hautatuta"</string> <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Ingurunea"</string> <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Ezkerrekoa"</string> @@ -582,6 +586,8 @@ <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Aplikazio bat partekatzen ari zarenean, aplikazio horretan agertzen den edo bertan erreproduzitzen ari den guztia ikusi dezake <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> aplikazioak. Beraz, kontuz ibili pasahitzekin, ordainketen xehetasunekin, mezuekin, argazkiekin, audioekin eta bideoekin, besteak beste."</string> <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Partekatu pantaila"</string> <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> aplikazioak aukera desgaitu du"</string> + <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) --> + <skip /> <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Aukeratu zein aplikazio partekatu nahi duzun"</string> <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Pantaila igorri nahi duzu?"</string> <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Igorri aplikazio bat"</string> @@ -980,6 +986,8 @@ <string name="drag_to_remove_tiles" msgid="4682194717573850385">"Kentzeko, arrastatu hona"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"<xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> lauza behar dituzu gutxienez"</string> <string name="qs_edit" msgid="5583565172803472437">"Editatu"</string> + <!-- no translation found for qs_edit_tiles (2105215324060865035) --> + <skip /> <string name="tuner_time" msgid="2450785840990529997">"Ordua"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"Erakutsi orduak, minutuak eta segundoak"</item> diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml index db76009629c4..58f35d1d8511 100644 --- a/packages/SystemUI/res/values-fa/strings.xml +++ b/packages/SystemUI/res/values-fa/strings.xml @@ -419,6 +419,10 @@ <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> + <!-- no translation found for hearing_devices_input_routing_label (730396728151232306) --> + <skip /> + <!-- no translation found for hearing_device_input_routing_options:0 (4582190415045337003) --> + <!-- no translation found for hearing_device_input_routing_options:1 (8501466270452446450) --> <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"انتخابشده"</string> <string name="hearing_devices_ambient_label" msgid="629440938614895797">"پیرامون"</string> <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"چپ"</string> @@ -582,6 +586,8 @@ <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"وقتی برنامهای را همرسانی میکنید، هر چیزی که در آن برنامه نمایش داده شود یا پخش شود برای <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> قابلمشاهده خواهد بود. درنتیجه مراقب چیزهایی مثل گذرواژهها، جزئیات پرداخت، پیامها، عکسها، و صدا و تصویر باشید."</string> <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"همرسانی صفحهنمایش"</string> <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g>این گزینه را غیرفعال کرده است"</string> + <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) --> + <skip /> <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"برنامهای را برای همرسانی انتخاب کنید"</string> <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"محتوای صفحهنمایش شما پخش شود؟"</string> <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"پخش کردن محتوای یک برنامه"</string> @@ -980,6 +986,8 @@ <string name="drag_to_remove_tiles" msgid="4682194717573850385">"برای حذف، به اینجا بکشید"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"حداقل به <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> کاشی نیاز دارید"</string> <string name="qs_edit" msgid="5583565172803472437">"ویرایش"</string> + <!-- no translation found for qs_edit_tiles (2105215324060865035) --> + <skip /> <string name="tuner_time" msgid="2450785840990529997">"زمان"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"ساعت، دقیقه و ثانیه نشان داده شود"</item> diff --git a/packages/SystemUI/res/values-fa/tiles_states_strings.xml b/packages/SystemUI/res/values-fa/tiles_states_strings.xml index ee9429402779..68ca663eadc1 100644 --- a/packages/SystemUI/res/values-fa/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-fa/tiles_states_strings.xml @@ -56,9 +56,11 @@ <item msgid="5376619709702103243">"خاموش"</item> <item msgid="4875147066469902392">"روشن"</item> </string-array> - <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) --> - <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) --> - <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) --> + <string-array name="tile_states_modes_dnd"> + <item msgid="6509540227356524582">"دردسترس نیست"</item> + <item msgid="8589336868985358191">"خاموش"</item> + <item msgid="726072717827778234">"روشن"</item> + </string-array> <string-array name="tile_states_flashlight"> <item msgid="3465257127433353857">"دردسترس نیست"</item> <item msgid="5044688398303285224">"خاموش"</item> diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml index 8df9aeb04f92..1b767db0ca57 100644 --- a/packages/SystemUI/res/values-fi/strings.xml +++ b/packages/SystemUI/res/values-fi/strings.xml @@ -421,6 +421,10 @@ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Muodosta uusi laitepari klikkaamalla"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Esiasetusta ei voitu muuttaa"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Esiasetus"</string> + <!-- no translation found for hearing_devices_input_routing_label (730396728151232306) --> + <skip /> + <!-- no translation found for hearing_device_input_routing_options:0 (4582190415045337003) --> + <!-- no translation found for hearing_device_input_routing_options:1 (8501466270452446450) --> <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Valittu"</string> <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Ympäristö"</string> <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Vasen"</string> @@ -584,6 +588,8 @@ <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Kun jaat sovelluksen, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> näkee kaiken sovelluksessa näkyvän tai toistetun sisällön. Ole siis varovainen, kun lisäät salasanoja, maksutietoja, viestejä, kuvia, audiota tai videoita."</string> <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Jaa näyttö"</string> <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> on poistanut vaihtoehdon käytöstä"</string> + <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) --> + <skip /> <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Valitse jaettava sovellus"</string> <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Striimataanko näyttö?"</string> <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Striimaa yksi sovellus"</string> @@ -982,6 +988,8 @@ <string name="drag_to_remove_tiles" msgid="4682194717573850385">"Poista vetämällä tähän."</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"<xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> kiekkoa on vähimmäismäärä"</string> <string name="qs_edit" msgid="5583565172803472437">"Muokkaa"</string> + <!-- no translation found for qs_edit_tiles (2105215324060865035) --> + <skip /> <string name="tuner_time" msgid="2450785840990529997">"Aika"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"Näytä tunnit, minuutit ja sekunnit"</item> diff --git a/packages/SystemUI/res/values-fi/tiles_states_strings.xml b/packages/SystemUI/res/values-fi/tiles_states_strings.xml index 76a77ad058ef..12487bd4b818 100644 --- a/packages/SystemUI/res/values-fi/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-fi/tiles_states_strings.xml @@ -56,9 +56,11 @@ <item msgid="5376619709702103243">"Poissa päältä"</item> <item msgid="4875147066469902392">"Päällä"</item> </string-array> - <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) --> - <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) --> - <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) --> + <string-array name="tile_states_modes_dnd"> + <item msgid="6509540227356524582">"Ei saatavilla"</item> + <item msgid="8589336868985358191">"Pois päältä"</item> + <item msgid="726072717827778234">"Päällä"</item> + </string-array> <string-array name="tile_states_flashlight"> <item msgid="3465257127433353857">"Ei saatavilla"</item> <item msgid="5044688398303285224">"Poissa päältä"</item> diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml index 9bd16d975d74..2a315e453cae 100644 --- a/packages/SystemUI/res/values-fr-rCA/strings.xml +++ b/packages/SystemUI/res/values-fr-rCA/strings.xml @@ -419,6 +419,10 @@ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Cliquez ici pour associer un nouvel appareil"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Impossible de mettre à jour le préréglage"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Préréglage"</string> + <!-- no translation found for hearing_devices_input_routing_label (730396728151232306) --> + <skip /> + <!-- no translation found for hearing_device_input_routing_options:0 (4582190415045337003) --> + <!-- no translation found for hearing_device_input_routing_options:1 (8501466270452446450) --> <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Sélectionné"</string> <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Environnement"</string> <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Gauche"</string> @@ -582,6 +586,8 @@ <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Lorsque vous partagez une appli, tout ce qui s\'y affiche ou s\'y joue est visible par <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Par conséquent, soyez prudent avec les mots de passe, les détails du mode de paiement, les messages, les photos et les contenus audio et vidéo."</string> <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Partager l\'écran"</string> <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> a désactivé cette option"</string> + <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) --> + <skip /> <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Choisir l\'appli à partager"</string> <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Diffuser votre écran?"</string> <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Diffuser une appli"</string> @@ -980,6 +986,8 @@ <string name="drag_to_remove_tiles" msgid="4682194717573850385">"Faites glisser les tuiles ici pour les retirer"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"Vous avez besoin d\'au moins <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> tuiles"</string> <string name="qs_edit" msgid="5583565172803472437">"Modifier"</string> + <!-- no translation found for qs_edit_tiles (2105215324060865035) --> + <skip /> <string name="tuner_time" msgid="2450785840990529997">"Heure"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"Afficher les heures, les minutes et les secondes"</item> diff --git a/packages/SystemUI/res/values-fr-rCA/tiles_states_strings.xml b/packages/SystemUI/res/values-fr-rCA/tiles_states_strings.xml index 01f33917a979..6b94691d729f 100644 --- a/packages/SystemUI/res/values-fr-rCA/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-fr-rCA/tiles_states_strings.xml @@ -56,9 +56,11 @@ <item msgid="5376619709702103243">"Désactivée"</item> <item msgid="4875147066469902392">"Activé"</item> </string-array> - <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) --> - <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) --> - <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) --> + <string-array name="tile_states_modes_dnd"> + <item msgid="6509540227356524582">"Non accessible"</item> + <item msgid="8589336868985358191">"Désactivé"</item> + <item msgid="726072717827778234">"Activé"</item> + </string-array> <string-array name="tile_states_flashlight"> <item msgid="3465257127433353857">"Non disponible"</item> <item msgid="5044688398303285224">"Désactivée"</item> diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml index 63ac0cab4b17..d7e9d446bec1 100644 --- a/packages/SystemUI/res/values-fr/strings.xml +++ b/packages/SystemUI/res/values-fr/strings.xml @@ -419,6 +419,10 @@ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Cliquer pour associer un nouvel appareil"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Impossible de mettre à jour les préréglages"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Préréglage"</string> + <!-- no translation found for hearing_devices_input_routing_label (730396728151232306) --> + <skip /> + <!-- no translation found for hearing_device_input_routing_options:0 (4582190415045337003) --> + <!-- no translation found for hearing_device_input_routing_options:1 (8501466270452446450) --> <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Sélectionné"</string> <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Sons environnants"</string> <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Gauche"</string> @@ -582,6 +586,8 @@ <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Lorsque vous partagez une appli, tout ce qui est affiché ou lu dans celle-ci est visible par <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Faites donc attention aux éléments tels que les mots de passe, les détails du mode de paiement, les messages, les photos et les contenus audio et vidéo."</string> <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Partager l\'écran"</string> <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> a désactivé cette option"</string> + <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) --> + <skip /> <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Choisir l\'appli à partager"</string> <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Caster votre écran ?"</string> <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Caster une appli"</string> @@ -980,6 +986,8 @@ <string name="drag_to_remove_tiles" msgid="4682194717573850385">"Faites glisser les blocs ici pour les supprimer"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"Au minimum <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> tuiles sont nécessaires"</string> <string name="qs_edit" msgid="5583565172803472437">"Modifier"</string> + <!-- no translation found for qs_edit_tiles (2105215324060865035) --> + <skip /> <string name="tuner_time" msgid="2450785840990529997">"Heure"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"Afficher les heures, les minutes et les secondes"</item> diff --git a/packages/SystemUI/res/values-fr/tiles_states_strings.xml b/packages/SystemUI/res/values-fr/tiles_states_strings.xml index 620e46c88cd5..ea91d2f7e016 100644 --- a/packages/SystemUI/res/values-fr/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-fr/tiles_states_strings.xml @@ -56,9 +56,11 @@ <item msgid="5376619709702103243">"Désactivé"</item> <item msgid="4875147066469902392">"Activé"</item> </string-array> - <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) --> - <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) --> - <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) --> + <string-array name="tile_states_modes_dnd"> + <item msgid="6509540227356524582">"Indisponible"</item> + <item msgid="8589336868985358191">"Désactivé"</item> + <item msgid="726072717827778234">"Activé"</item> + </string-array> <string-array name="tile_states_flashlight"> <item msgid="3465257127433353857">"Indisponible"</item> <item msgid="5044688398303285224">"Désactivé"</item> diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml index 80da559ba1c5..f4a4ceb526dc 100644 --- a/packages/SystemUI/res/values-gl/strings.xml +++ b/packages/SystemUI/res/values-gl/strings.xml @@ -419,6 +419,10 @@ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Fai clic para vincular un novo dispositivo"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Non se puido actualizar a configuración predeterminada"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Configuración predeterminada"</string> + <!-- no translation found for hearing_devices_input_routing_label (730396728151232306) --> + <skip /> + <!-- no translation found for hearing_device_input_routing_options:0 (4582190415045337003) --> + <!-- no translation found for hearing_device_input_routing_options:1 (8501466270452446450) --> <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Elemento seleccionado"</string> <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Ambiente"</string> <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Esquerdo"</string> @@ -582,6 +586,8 @@ <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Se compartes toda a pantalla, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> poderá ver todo o contido que apareza ou se reproduza nesa aplicación. Ten coidado con determinada información, como os contrasinais, os detalles de pago, as mensaxes e as fotos, así como co contido de audio e de vídeo."</string> <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Compartir pantalla"</string> <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> desactivou esta opción"</string> + <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) --> + <skip /> <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Escoller unha aplicación para compartir"</string> <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Queres emitir a túa pantalla?"</string> <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Emitir unha aplicación"</string> @@ -980,6 +986,8 @@ <string name="drag_to_remove_tiles" msgid="4682194717573850385">"Arrastra o elemento ata aquí para quitalo"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"Como mínimo ten que haber <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> mosaicos"</string> <string name="qs_edit" msgid="5583565172803472437">"Editar"</string> + <!-- no translation found for qs_edit_tiles (2105215324060865035) --> + <skip /> <string name="tuner_time" msgid="2450785840990529997">"Hora"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"Mostrar horas, minutos e segundos"</item> diff --git a/packages/SystemUI/res/values-gl/tiles_states_strings.xml b/packages/SystemUI/res/values-gl/tiles_states_strings.xml index ca19e0ecd24d..9b05a8705adc 100644 --- a/packages/SystemUI/res/values-gl/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-gl/tiles_states_strings.xml @@ -56,9 +56,11 @@ <item msgid="5376619709702103243">"Non"</item> <item msgid="4875147066469902392">"Si"</item> </string-array> - <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) --> - <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) --> - <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) --> + <string-array name="tile_states_modes_dnd"> + <item msgid="6509540227356524582">"Non dispoñible"</item> + <item msgid="8589336868985358191">"Desactivado"</item> + <item msgid="726072717827778234">"Activado"</item> + </string-array> <string-array name="tile_states_flashlight"> <item msgid="3465257127433353857">"Non dispoñible"</item> <item msgid="5044688398303285224">"Non"</item> diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml index 17bcdf091f9e..2a8fe315647e 100644 --- a/packages/SystemUI/res/values-gu/strings.xml +++ b/packages/SystemUI/res/values-gu/strings.xml @@ -252,10 +252,8 @@ <string name="accessibility_bluetooth_name" msgid="7300973230214067678">"<xliff:g id="BLUETOOTH">%s</xliff:g> થી કનેક્ટ થયાં."</string> <string name="accessibility_cast_name" msgid="7344437925388773685">"<xliff:g id="CAST">%s</xliff:g> થી કનેક્ટ કરેલ."</string> <string name="accessibility_expand_group" msgid="521237935987978624">"ગ્રૂપને મોટું કરો."</string> - <!-- no translation found for accessibility_add_device_to_group (5446422960697860806) --> - <skip /> - <!-- no translation found for accessibility_remove_device_from_group (3114694270949142228) --> - <skip /> + <string name="accessibility_add_device_to_group" msgid="5446422960697860806">"ડિવાઇસને ગ્રૂપમાં ઉમેરો."</string> + <string name="accessibility_remove_device_from_group" msgid="3114694270949142228">"ડિવાઇસને ગ્રૂપમાંથી કાઢી નાખો."</string> <string name="accessibility_open_application" msgid="1749126077501259712">"ઍપ્લિકેશન ખોલો."</string> <string name="accessibility_not_connected" msgid="4061305616351042142">"કનેક્ટ થયેલું નથી."</string> <string name="data_connection_roaming" msgid="375650836665414797">"રોમિંગ"</string> @@ -333,8 +331,7 @@ <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"ઇનપુટ"</string> <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"સાંભળવામાં મદદ આપતા યંત્રો"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"ચાલુ કરી રહ્યાં છીએ…"</string> - <!-- no translation found for quick_settings_brightness_unable_adjust_msg (4124028416057617517) --> - <skip /> + <string name="quick_settings_brightness_unable_adjust_msg" msgid="4124028416057617517">"બ્રાઇટનેસ ગોઠવી શકાતી નથી કારણ કે તે લોકપ્રિય ઍપ દ્વારા નિયંત્રિત કરવામાં આવી રહી છે"</string> <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"ઑટો રોટેટ"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"ઑટો રોટેટ સ્ક્રીન"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"લોકેશન"</string> @@ -422,6 +419,10 @@ <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> + <!-- no translation found for hearing_devices_input_routing_label (730396728151232306) --> + <skip /> + <!-- no translation found for hearing_device_input_routing_options:0 (4582190415045337003) --> + <!-- no translation found for hearing_device_input_routing_options:1 (8501466270452446450) --> <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"પસંદ કરી છે"</string> <string name="hearing_devices_ambient_label" msgid="629440938614895797">"આસપાસના અવાજો"</string> <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"ડાબે"</string> @@ -585,6 +586,8 @@ <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"જ્યારે તમે કોઈ ઍપને શેર કરી રહ્યાં હો, ત્યારે તે ઍપ પર બતાવવામાં કે ચલાવવામાં આવતી હોય તેવી બધી વસ્તુ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>ને દેખાય છે. તેથી પાસવર્ડ, ચુકવણીની વિગતો, મેસેજ, ફોટા અને ડિવાઇસ પર વાગી રહેલા ઑડિયો તથા વીડિયો જેવી બાબતોને લઈને સાવચેત રહો."</string> <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"સ્ક્રીન શેર કરો"</string> <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> દ્વારા આ વિકલ્પ બંધ કરવામાં આવ્યો છે"</string> + <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) --> + <skip /> <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"શેર કરવા માટે ઍપ પસંદ કરો"</string> <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"તમારી સ્ક્રીનને કાસ્ટ કરીએ?"</string> <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"એક ઍપને કાસ્ટ કરો"</string> @@ -983,6 +986,8 @@ <string name="drag_to_remove_tiles" msgid="4682194717573850385">"દૂર કરવા માટે અહીં ખેંચો"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"તમને ઓછામાં ઓછી <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> ટાઇલની જરૂર છે"</string> <string name="qs_edit" msgid="5583565172803472437">"ફેરફાર કરો"</string> + <!-- no translation found for qs_edit_tiles (2105215324060865035) --> + <skip /> <string name="tuner_time" msgid="2450785840990529997">"સમય"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"કલાક, મિનિટ અને સેકન્ડ બતાવો"</item> @@ -1344,7 +1349,7 @@ <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"ટાઇલ ઉમેરો"</string> <string name="qs_tile_request_dialog_not_add" msgid="4168716573114067296">"ટાઇલ ઉમેરશો નહીં"</string> <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"વપરાશકર્તા પસંદ કરો"</string> - <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# ઍપ સક્રિય છે}one{# ઍપ સક્રિય છે}other{# ઍપ સક્રિય છે}}"</string> + <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> diff --git a/packages/SystemUI/res/values-gu/tiles_states_strings.xml b/packages/SystemUI/res/values-gu/tiles_states_strings.xml index 759e43664379..c3fb94129883 100644 --- a/packages/SystemUI/res/values-gu/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-gu/tiles_states_strings.xml @@ -56,9 +56,11 @@ <item msgid="5376619709702103243">"બંધ છે"</item> <item msgid="4875147066469902392">"ચાલુ છે"</item> </string-array> - <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) --> - <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) --> - <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) --> + <string-array name="tile_states_modes_dnd"> + <item msgid="6509540227356524582">"અનુપલબ્ધ"</item> + <item msgid="8589336868985358191">"બંધ"</item> + <item msgid="726072717827778234">"ચાલુ"</item> + </string-array> <string-array name="tile_states_flashlight"> <item msgid="3465257127433353857">"ઉપલબ્ધ નથી"</item> <item msgid="5044688398303285224">"બંધ છે"</item> diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml index f4c05ba2f8c5..922794daa24b 100644 --- a/packages/SystemUI/res/values-hi/strings.xml +++ b/packages/SystemUI/res/values-hi/strings.xml @@ -419,6 +419,10 @@ <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> + <!-- no translation found for hearing_devices_input_routing_label (730396728151232306) --> + <skip /> + <!-- no translation found for hearing_device_input_routing_options:0 (4582190415045337003) --> + <!-- no translation found for hearing_device_input_routing_options:1 (8501466270452446450) --> <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"चुना गया"</string> <string name="hearing_devices_ambient_label" msgid="629440938614895797">"आस-पास का वॉल्यूम"</string> <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"बाईं ओर के वॉल्यूम के लिए"</string> @@ -582,6 +586,8 @@ <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"जब कोई ऐप्लिकेशन शेयर किया जाता है, तो उस ऐप्लिकेशन में दिख रहा या चलाया जा रहा पूरा कॉन्टेंट <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> पर दिखता है. इसलिए, पासवर्ड, पेमेंट के तरीके की जानकारी, मैसेज, फ़ोटो, ऑडियो, और वीडियो को लेकर सावधानी बरतें."</string> <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"स्क्रीन शेयर करें"</string> <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> ने इस विकल्प को बंद कर दिया है"</string> + <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) --> + <skip /> <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"शेयर करने के लिए ऐप्लिकेशन चुनें"</string> <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"क्या स्क्रीन को कास्ट करना है?"</string> <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"एक ऐप्लिकेशन को कास्ट करें"</string> @@ -980,6 +986,8 @@ <string name="drag_to_remove_tiles" msgid="4682194717573850385">"हटाने के लिए यहां खींचें और छोड़ें"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"आपके पास कम से कम <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> टाइलें होनी चाहिए"</string> <string name="qs_edit" msgid="5583565172803472437">"बदलाव करें"</string> + <!-- no translation found for qs_edit_tiles (2105215324060865035) --> + <skip /> <string name="tuner_time" msgid="2450785840990529997">"समय"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"घंटे, मिनट और सेकंड दिखाएं"</item> diff --git a/packages/SystemUI/res/values-hi/tiles_states_strings.xml b/packages/SystemUI/res/values-hi/tiles_states_strings.xml index 410f25db0900..d331e3a8a5db 100644 --- a/packages/SystemUI/res/values-hi/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-hi/tiles_states_strings.xml @@ -56,9 +56,11 @@ <item msgid="5376619709702103243">"बंद है"</item> <item msgid="4875147066469902392">"चालू है"</item> </string-array> - <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) --> - <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) --> - <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) --> + <string-array name="tile_states_modes_dnd"> + <item msgid="6509540227356524582">"उपलब्ध नहीं है"</item> + <item msgid="8589336868985358191">"बंद है"</item> + <item msgid="726072717827778234">"चालू है"</item> + </string-array> <string-array name="tile_states_flashlight"> <item msgid="3465257127433353857">"उपलब्ध नहीं है"</item> <item msgid="5044688398303285224">"बंद है"</item> diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml index 9d2509404a8c..3213d59dadf9 100644 --- a/packages/SystemUI/res/values-hr/strings.xml +++ b/packages/SystemUI/res/values-hr/strings.xml @@ -419,6 +419,10 @@ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kliknite da biste uparili novi uređaj"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Ažuriranje unaprijed definiranih postavki nije uspjelo"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Unaprijed definirana postavka"</string> + <!-- no translation found for hearing_devices_input_routing_label (730396728151232306) --> + <skip /> + <!-- no translation found for hearing_device_input_routing_options:0 (4582190415045337003) --> + <!-- no translation found for hearing_device_input_routing_options:1 (8501466270452446450) --> <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Odabrano"</string> <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Okruženje"</string> <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Lijevo"</string> @@ -582,6 +586,8 @@ <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Kada dijelite aplikaciju, sve što se prikazuje ili reproducira u toj aplikaciji bit će vidljivo aplikaciji <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Stoga pazite na stvari kao što su zaporke, podaci o plaćanju, poruke, fotografije te audio i videozapisi."</string> <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Dijeljenje zaslona"</string> <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> onemogućila je ovu opciju"</string> + <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) --> + <skip /> <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Odaberite aplikaciju za dijeljenje"</string> <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Želite li emitirati zaslon?"</string> <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Emitiranje jedne aplikacije"</string> @@ -980,6 +986,8 @@ <string name="drag_to_remove_tiles" msgid="4682194717573850385">"Povucite ovdje za uklanjanje"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"Potrebno je barem <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> pločica"</string> <string name="qs_edit" msgid="5583565172803472437">"Uređivanje"</string> + <!-- no translation found for qs_edit_tiles (2105215324060865035) --> + <skip /> <string name="tuner_time" msgid="2450785840990529997">"Vrijeme"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"Prikaži sate, minute i sekunde"</item> diff --git a/packages/SystemUI/res/values-hr/tiles_states_strings.xml b/packages/SystemUI/res/values-hr/tiles_states_strings.xml index bbfcf840e53f..5784d4735ede 100644 --- a/packages/SystemUI/res/values-hr/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-hr/tiles_states_strings.xml @@ -56,9 +56,11 @@ <item msgid="5376619709702103243">"Isključeno"</item> <item msgid="4875147066469902392">"Uključeno"</item> </string-array> - <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) --> - <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) --> - <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) --> + <string-array name="tile_states_modes_dnd"> + <item msgid="6509540227356524582">"Nedostupno"</item> + <item msgid="8589336868985358191">"Isključeno"</item> + <item msgid="726072717827778234">"Uključeno"</item> + </string-array> <string-array name="tile_states_flashlight"> <item msgid="3465257127433353857">"Nedostupno"</item> <item msgid="5044688398303285224">"Isključeno"</item> diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml index bdeececfd9ae..4327594260fc 100644 --- a/packages/SystemUI/res/values-hu/strings.xml +++ b/packages/SystemUI/res/values-hu/strings.xml @@ -419,6 +419,10 @@ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kattintson új eszköz párosításához"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Nem sikerült frissíteni a beállításkészletet"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Beállításkészlet"</string> + <!-- no translation found for hearing_devices_input_routing_label (730396728151232306) --> + <skip /> + <!-- no translation found for hearing_device_input_routing_options:0 (4582190415045337003) --> + <!-- no translation found for hearing_device_input_routing_options:1 (8501466270452446450) --> <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Kiválasztva"</string> <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Környezet"</string> <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Bal"</string> @@ -582,6 +586,8 @@ <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Alkalmazás megosztása közben az adott appban megjelenített vagy lejátszott minden tartalom látható a(z) <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> számára. Ezért legyen elővigyázatos a jelszavakkal, a fizetési adatokkal, az üzenetekkel, a fotókkal, valamint a hang- és videófelvételekkel."</string> <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Képernyő megosztása"</string> <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> letiltotta ezt a beállítást"</string> + <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) --> + <skip /> <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Válassza ki a megosztani kívánt alkalmazást"</string> <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Átküldi a képernyőt?"</string> <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Egyetlen app átküldése"</string> @@ -794,8 +800,7 @@ <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Be: Arcalapú"</string> <string name="inline_done_button" msgid="6043094985588909584">"Kész"</string> <string name="inline_ok_button" msgid="603075490581280343">"Alkalmaz"</string> - <!-- no translation found for inline_turn_off_notifications (2653064779176881329) --> - <skip /> + <string name="inline_turn_off_notifications" msgid="2653064779176881329">"Kikapcsolás"</string> <string name="notification_silence_title" msgid="8608090968400832335">"Néma"</string> <string name="notification_alert_title" msgid="3656229781017543655">"Alapértelmezett"</string> <string name="notification_automatic_title" msgid="3745465364578762652">"Automatikus"</string> @@ -980,6 +985,8 @@ <string name="drag_to_remove_tiles" msgid="4682194717573850385">"Húzza ide az eltávolításhoz"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"Legalább <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> kártya szükséges"</string> <string name="qs_edit" msgid="5583565172803472437">"Szerkesztés"</string> + <!-- no translation found for qs_edit_tiles (2105215324060865035) --> + <skip /> <string name="tuner_time" msgid="2450785840990529997">"Idő"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"Óra, perc és másodperc megjelenítése"</item> diff --git a/packages/SystemUI/res/values-hu/tiles_states_strings.xml b/packages/SystemUI/res/values-hu/tiles_states_strings.xml index 8bd57214a28d..168cd0686e2d 100644 --- a/packages/SystemUI/res/values-hu/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-hu/tiles_states_strings.xml @@ -56,9 +56,11 @@ <item msgid="5376619709702103243">"Ki"</item> <item msgid="4875147066469902392">"Be"</item> </string-array> - <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) --> - <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) --> - <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) --> + <string-array name="tile_states_modes_dnd"> + <item msgid="6509540227356524582">"Nem áll rendelkezésre"</item> + <item msgid="8589336868985358191">"Ki"</item> + <item msgid="726072717827778234">"Be"</item> + </string-array> <string-array name="tile_states_flashlight"> <item msgid="3465257127433353857">"Nem áll rendelkezésre"</item> <item msgid="5044688398303285224">"Ki"</item> diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml index d448486faf64..eaff82423bd8 100644 --- a/packages/SystemUI/res/values-hy/strings.xml +++ b/packages/SystemUI/res/values-hy/strings.xml @@ -419,6 +419,10 @@ <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> + <!-- no translation found for hearing_devices_input_routing_label (730396728151232306) --> + <skip /> + <!-- no translation found for hearing_device_input_routing_options:0 (4582190415045337003) --> + <!-- no translation found for hearing_device_input_routing_options:1 (8501466270452446450) --> <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Ընտրված է"</string> <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Շրջակայք"</string> <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Ձախ"</string> @@ -582,6 +586,8 @@ <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Երբ դուք որևէ հավելված եք հեռարձակում, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> հավելվածին տեսանելի կլինի այն ամենը, ինչ ցուցադրվում կամ նվագարկվում է այդ հավելվածում։ Ուստի ուշադիր եղեք այնպիսի բաների հետ, ինչպիսիք են գաղտնաբառերը, վճարային տվյալները, հաղորդագրությունները, լուսանկարները, աուդիո և վիդեո բովանդակությունը։"</string> <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Ցուցադրել էկրանը"</string> <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g>-ն անջատել է այս ընտրանքը"</string> + <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) --> + <skip /> <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Հավելվածի ընտրություն՝ կիսվելու համար"</string> <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Հեռարձակե՞լ ձեր էկրանը"</string> <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Հեռարձակել մեկ հավելված"</string> @@ -980,6 +986,8 @@ <string name="drag_to_remove_tiles" msgid="4682194717573850385">"Քաշեք այստեղ՝ հեռացնելու համար"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"Հարկավոր է առնվազն <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> սալիկ"</string> <string name="qs_edit" msgid="5583565172803472437">"Փոփոխել"</string> + <!-- no translation found for qs_edit_tiles (2105215324060865035) --> + <skip /> <string name="tuner_time" msgid="2450785840990529997">"Ժամ"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"Ցույց տալ ժամերը, րոպեները և վայրկյանները"</item> diff --git a/packages/SystemUI/res/values-hy/tiles_states_strings.xml b/packages/SystemUI/res/values-hy/tiles_states_strings.xml index 3497c404600b..c1b2d71aed3b 100644 --- a/packages/SystemUI/res/values-hy/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-hy/tiles_states_strings.xml @@ -56,9 +56,11 @@ <item msgid="5376619709702103243">"Անջատված է"</item> <item msgid="4875147066469902392">"Միացված է"</item> </string-array> - <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) --> - <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) --> - <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) --> + <string-array name="tile_states_modes_dnd"> + <item msgid="6509540227356524582">"Հասանելի չէ"</item> + <item msgid="8589336868985358191">"Անջատված է"</item> + <item msgid="726072717827778234">"Միացված է"</item> + </string-array> <string-array name="tile_states_flashlight"> <item msgid="3465257127433353857">"Հասանելի չէ"</item> <item msgid="5044688398303285224">"Անջատված է"</item> diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml index 5867bf7c5cc6..763f76754b74 100644 --- a/packages/SystemUI/res/values-in/strings.xml +++ b/packages/SystemUI/res/values-in/strings.xml @@ -419,6 +419,10 @@ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Klik untuk menyambungkan perangkat baru"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Tidak dapat memperbarui preset"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Preset"</string> + <!-- no translation found for hearing_devices_input_routing_label (730396728151232306) --> + <skip /> + <!-- no translation found for hearing_device_input_routing_options:0 (4582190415045337003) --> + <!-- no translation found for hearing_device_input_routing_options:1 (8501466270452446450) --> <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Dipilih"</string> <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Suara sekitar"</string> <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Kiri"</string> @@ -582,6 +586,8 @@ <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Jika Anda membagikan aplikasi, semua hal yang ditampilkan atau diputar di aplikasi tersebut akan terlihat oleh <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Jadi, berhati-hatilah saat memasukkan sandi, detail pembayaran, pesan, foto, audio, dan video."</string> <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Bagikan layar"</string> <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> telah menonaktifkan opsi ini"</string> + <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) --> + <skip /> <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Pilih aplikasi yang akan dibagikan"</string> <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Transmisikan layar?"</string> <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Transmisikan satu aplikasi"</string> @@ -980,6 +986,8 @@ <string name="drag_to_remove_tiles" msgid="4682194717573850385">"Tarik ke sini untuk menghapus"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"Anda membutuhkan setidaknya <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> kartu"</string> <string name="qs_edit" msgid="5583565172803472437">"Edit"</string> + <!-- no translation found for qs_edit_tiles (2105215324060865035) --> + <skip /> <string name="tuner_time" msgid="2450785840990529997">"Waktu"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"Tampilkan jam, menit, dan detik"</item> diff --git a/packages/SystemUI/res/values-in/tiles_states_strings.xml b/packages/SystemUI/res/values-in/tiles_states_strings.xml index 7df0b0d9f34c..a00d87d8bfd7 100644 --- a/packages/SystemUI/res/values-in/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-in/tiles_states_strings.xml @@ -56,9 +56,11 @@ <item msgid="5376619709702103243">"Nonaktif"</item> <item msgid="4875147066469902392">"Aktif"</item> </string-array> - <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) --> - <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) --> - <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) --> + <string-array name="tile_states_modes_dnd"> + <item msgid="6509540227356524582">"Tidak tersedia"</item> + <item msgid="8589336868985358191">"Nonaktif"</item> + <item msgid="726072717827778234">"Aktif"</item> + </string-array> <string-array name="tile_states_flashlight"> <item msgid="3465257127433353857">"Tidak tersedia"</item> <item msgid="5044688398303285224">"Mati"</item> diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml index f949400bf09d..970a9130135a 100644 --- a/packages/SystemUI/res/values-is/strings.xml +++ b/packages/SystemUI/res/values-is/strings.xml @@ -419,6 +419,10 @@ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Smelltu til að para nýtt tæki"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Tókst ekki að uppfæra forstillingu"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Forstilling"</string> + <!-- no translation found for hearing_devices_input_routing_label (730396728151232306) --> + <skip /> + <!-- no translation found for hearing_device_input_routing_options:0 (4582190415045337003) --> + <!-- no translation found for hearing_device_input_routing_options:1 (8501466270452446450) --> <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Valið"</string> <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Umhverfi"</string> <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Vinstri"</string> @@ -582,6 +586,8 @@ <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Þegar þú deilir forriti er allt sem sést eða er spilað í því forriti sýnilegt <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Passaðu því upp á aðgangsorð, greiðsluupplýsingar, skilaboð, myndir, hljóð og myndskeið."</string> <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Deila skjá"</string> <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> slökkti á þessum valkosti"</string> + <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) --> + <skip /> <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Velja forrit til að deila"</string> <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Varpa skjánum?"</string> <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Varpa einu forriti"</string> @@ -980,6 +986,8 @@ <string name="drag_to_remove_tiles" msgid="4682194717573850385">"Dragðu hingað til að fjarlægja"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"Flísarnar mega ekki vera færri en <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g>"</string> <string name="qs_edit" msgid="5583565172803472437">"Breyta"</string> + <!-- no translation found for qs_edit_tiles (2105215324060865035) --> + <skip /> <string name="tuner_time" msgid="2450785840990529997">"Tími"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"Sýna klukkustundir, mínútur og sekúndur"</item> diff --git a/packages/SystemUI/res/values-is/tiles_states_strings.xml b/packages/SystemUI/res/values-is/tiles_states_strings.xml index d1b04e05ad7f..7790b11b5fa4 100644 --- a/packages/SystemUI/res/values-is/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-is/tiles_states_strings.xml @@ -56,9 +56,11 @@ <item msgid="5376619709702103243">"Slökkt"</item> <item msgid="4875147066469902392">"Kveikt"</item> </string-array> - <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) --> - <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) --> - <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) --> + <string-array name="tile_states_modes_dnd"> + <item msgid="6509540227356524582">"Ekki í boði"</item> + <item msgid="8589336868985358191">"Slökkt"</item> + <item msgid="726072717827778234">"Kveikt"</item> + </string-array> <string-array name="tile_states_flashlight"> <item msgid="3465257127433353857">"Ekki í boði"</item> <item msgid="5044688398303285224">"Slökkt"</item> diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml index 708fa78bedff..7323037cb00b 100644 --- a/packages/SystemUI/res/values-it/strings.xml +++ b/packages/SystemUI/res/values-it/strings.xml @@ -419,6 +419,10 @@ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Fai clic per accoppiare un nuovo dispositivo"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Impossibile aggiornare preset"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Preset"</string> + <!-- no translation found for hearing_devices_input_routing_label (730396728151232306) --> + <skip /> + <!-- no translation found for hearing_device_input_routing_options:0 (4582190415045337003) --> + <!-- no translation found for hearing_device_input_routing_options:1 (8501466270452446450) --> <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Selezionato"</string> <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Audio ambientale"</string> <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Sinistra"</string> @@ -582,6 +586,8 @@ <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Quando condividi un\'app, tutto ciò che viene mostrato o riprodotto al suo interno è visibile a <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Presta quindi attenzione a password, dati di pagamento, messaggi, foto, audio e video."</string> <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Condividi schermo"</string> <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> ha disattivato questa opzione"</string> + <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) --> + <skip /> <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Scegli l\'app da condividere"</string> <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Trasmettere lo schermo?"</string> <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Trasmetti un\'app"</string> @@ -980,6 +986,8 @@ <string name="drag_to_remove_tiles" msgid="4682194717573850385">"Trascina qui per rimuovere"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"Occorrono almeno <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> schede"</string> <string name="qs_edit" msgid="5583565172803472437">"Modifica"</string> + <!-- no translation found for qs_edit_tiles (2105215324060865035) --> + <skip /> <string name="tuner_time" msgid="2450785840990529997">"Ora"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"Mostra ore, minuti e secondi"</item> diff --git a/packages/SystemUI/res/values-it/tiles_states_strings.xml b/packages/SystemUI/res/values-it/tiles_states_strings.xml index afbd3d9b1910..2d3f55361307 100644 --- a/packages/SystemUI/res/values-it/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-it/tiles_states_strings.xml @@ -56,9 +56,11 @@ <item msgid="5376619709702103243">"Off"</item> <item msgid="4875147066469902392">"On"</item> </string-array> - <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) --> - <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) --> - <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) --> + <string-array name="tile_states_modes_dnd"> + <item msgid="6509540227356524582">"Non disponibile"</item> + <item msgid="8589336868985358191">"Off"</item> + <item msgid="726072717827778234">"On"</item> + </string-array> <string-array name="tile_states_flashlight"> <item msgid="3465257127433353857">"Non disponibile"</item> <item msgid="5044688398303285224">"Off"</item> diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml index 35500fbc4107..3ee24a3d5a75 100644 --- a/packages/SystemUI/res/values-iw/strings.xml +++ b/packages/SystemUI/res/values-iw/strings.xml @@ -419,6 +419,10 @@ <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> + <!-- no translation found for hearing_devices_input_routing_label (730396728151232306) --> + <skip /> + <!-- no translation found for hearing_device_input_routing_options:0 (4582190415045337003) --> + <!-- no translation found for hearing_device_input_routing_options:1 (8501466270452446450) --> <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"נבחר"</string> <string name="hearing_devices_ambient_label" msgid="629440938614895797">"הרעשים בסביבה"</string> <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"שמאל"</string> @@ -582,6 +586,8 @@ <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"כשמשתפים אפליקציה, כל מה שרואים או מפעילים בה מופיע גם ב-<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. מומלץ להיזהר ולא לחשוף פרטים אישיים כמו סיסמאות, פרטי תשלום, הודעות, תמונות, אודיו וסרטונים."</string> <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"שיתוף המסך"</string> <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"האפליקציה <xliff:g id="APP_NAME">%1$s</xliff:g> השביתה את האפשרות הזו"</string> + <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) --> + <skip /> <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"בחירת אפליקציה לשיתוף"</string> <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"להפעיל Cast של המסך?"</string> <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"הפעלת Cast של אפליקציה אחת"</string> @@ -980,6 +986,8 @@ <string name="drag_to_remove_tiles" msgid="4682194717573850385">"אפשר לגרור לכאן כדי להסיר"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"יש צורך ב-<xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> אריחים לפחות"</string> <string name="qs_edit" msgid="5583565172803472437">"עריכה"</string> + <!-- no translation found for qs_edit_tiles (2105215324060865035) --> + <skip /> <string name="tuner_time" msgid="2450785840990529997">"שעה"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"הצגת שעות, דקות ושניות"</item> diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml index fc792b6f9850..1e85479ca0eb 100644 --- a/packages/SystemUI/res/values-ja/strings.xml +++ b/packages/SystemUI/res/values-ja/strings.xml @@ -419,6 +419,10 @@ <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> + <!-- no translation found for hearing_devices_input_routing_label (730396728151232306) --> + <skip /> + <!-- no translation found for hearing_device_input_routing_options:0 (4582190415045337003) --> + <!-- no translation found for hearing_device_input_routing_options:1 (8501466270452446450) --> <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"選択中"</string> <string name="hearing_devices_ambient_label" msgid="629440938614895797">"周囲の音"</string> <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"左"</string> @@ -582,6 +586,8 @@ <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"アプリを共有すると、そのアプリで表示または再生される内容が <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> にすべて公開されます。パスワード、お支払い情報、メッセージ、写真、音声、動画などの情報にご注意ください。"</string> <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"画面を共有"</string> <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> がこのオプションを無効にしています"</string> + <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) --> + <skip /> <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"共有するアプリを選択"</string> <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"画面をキャストしますか?"</string> <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"1 つのアプリをキャスト"</string> @@ -794,8 +800,7 @@ <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"ON - 顔ベース"</string> <string name="inline_done_button" msgid="6043094985588909584">"完了"</string> <string name="inline_ok_button" msgid="603075490581280343">"適用"</string> - <!-- no translation found for inline_turn_off_notifications (2653064779176881329) --> - <skip /> + <string name="inline_turn_off_notifications" msgid="2653064779176881329">"OFF にする"</string> <string name="notification_silence_title" msgid="8608090968400832335">"サイレント"</string> <string name="notification_alert_title" msgid="3656229781017543655">"デフォルト"</string> <string name="notification_automatic_title" msgid="3745465364578762652">"自動"</string> @@ -980,6 +985,8 @@ <string name="drag_to_remove_tiles" msgid="4682194717573850385">"削除するにはここにドラッグ"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"タイルは <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> 個以上必要です"</string> <string name="qs_edit" msgid="5583565172803472437">"編集"</string> + <!-- no translation found for qs_edit_tiles (2105215324060865035) --> + <skip /> <string name="tuner_time" msgid="2450785840990529997">"時間"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"時間、分、秒を表示"</item> diff --git a/packages/SystemUI/res/values-ja/tiles_states_strings.xml b/packages/SystemUI/res/values-ja/tiles_states_strings.xml index 4827ad38dfa7..4a6b210797f9 100644 --- a/packages/SystemUI/res/values-ja/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-ja/tiles_states_strings.xml @@ -56,9 +56,11 @@ <item msgid="5376619709702103243">"OFF"</item> <item msgid="4875147066469902392">"ON"</item> </string-array> - <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) --> - <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) --> - <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) --> + <string-array name="tile_states_modes_dnd"> + <item msgid="6509540227356524582">"使用不可"</item> + <item msgid="8589336868985358191">"OFF"</item> + <item msgid="726072717827778234">"ON"</item> + </string-array> <string-array name="tile_states_flashlight"> <item msgid="3465257127433353857">"使用不可"</item> <item msgid="5044688398303285224">"OFF"</item> diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml index e13a1b3a3bd8..0a25f83d1185 100644 --- a/packages/SystemUI/res/values-ka/strings.xml +++ b/packages/SystemUI/res/values-ka/strings.xml @@ -419,6 +419,10 @@ <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> + <!-- no translation found for hearing_devices_input_routing_label (730396728151232306) --> + <skip /> + <!-- no translation found for hearing_device_input_routing_options:0 (4582190415045337003) --> + <!-- no translation found for hearing_device_input_routing_options:1 (8501466270452446450) --> <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"არჩეულია"</string> <string name="hearing_devices_ambient_label" msgid="629440938614895797">"გარემოცვა"</string> <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"მარცხენა"</string> @@ -582,6 +586,8 @@ <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"აპის გაზიარებისას <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ხედავს ყველაფერს, რაც ჩანს ან უკრავს ამ აპში. ამიტომ სიფრთხილე გამოიჩინეთ ისეთ ინფორმაციასთან, როგორიცაა პაროლები, გადახდის დეტალები, შეტყობინებები, ფოტოები, აუდიო და ვიდეო."</string> <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"ეკრანის გაზიარება"</string> <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g>-მა გათიშა ეს ვარიანტი"</string> + <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) --> + <skip /> <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"გაზიარებისთვის აპის არჩევა"</string> <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"გსურთ თქვენი ეკრანის ტრანსლირება?"</string> <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"ერთი აპის ტრანსლირება"</string> @@ -980,6 +986,8 @@ <string name="drag_to_remove_tiles" msgid="4682194717573850385">"ამოსაშლელად, ჩავლებით გადმოიტანეთ აქ"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"თქვენ გჭირდებათ მოზაიკის <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> ფილა მაინც"</string> <string name="qs_edit" msgid="5583565172803472437">"რედაქტირება"</string> + <!-- no translation found for qs_edit_tiles (2105215324060865035) --> + <skip /> <string name="tuner_time" msgid="2450785840990529997">"დრო"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"საათების, წუთებისა და წამების ჩვენება"</item> @@ -1249,7 +1257,7 @@ <string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"ხელმისაწვდომი მოწყობილობები გამომავალი აუდიოსთვის."</string> <string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"ხმა"</string> <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string> - <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"დინამიკები და დისპლეები"</string> + <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"დინამიკები და ეკრანები"</string> <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"შემოთავაზებული მოწყობილობები"</string> <string name="media_input_group_title" msgid="2057057473860783021">"შემავალი"</string> <string name="media_output_group_title" msgid="6789001895863332576">"გამომავალი"</string> diff --git a/packages/SystemUI/res/values-ka/tiles_states_strings.xml b/packages/SystemUI/res/values-ka/tiles_states_strings.xml index ebf28c89587b..6440b5f74a94 100644 --- a/packages/SystemUI/res/values-ka/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-ka/tiles_states_strings.xml @@ -56,9 +56,11 @@ <item msgid="5376619709702103243">"გამორთულია"</item> <item msgid="4875147066469902392">"ჩართულია"</item> </string-array> - <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) --> - <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) --> - <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) --> + <string-array name="tile_states_modes_dnd"> + <item msgid="6509540227356524582">"მიუწვდომელი"</item> + <item msgid="8589336868985358191">"გამორთული"</item> + <item msgid="726072717827778234">"ჩართული"</item> + </string-array> <string-array name="tile_states_flashlight"> <item msgid="3465257127433353857">"მიუწვდომელია"</item> <item msgid="5044688398303285224">"გამორთულია"</item> diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml index 5fc8b47869b7..c7e8d81f4e45 100644 --- a/packages/SystemUI/res/values-kk/strings.xml +++ b/packages/SystemUI/res/values-kk/strings.xml @@ -419,6 +419,10 @@ <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> + <!-- no translation found for hearing_devices_input_routing_label (730396728151232306) --> + <skip /> + <!-- no translation found for hearing_device_input_routing_options:0 (4582190415045337003) --> + <!-- no translation found for hearing_device_input_routing_options:1 (8501466270452446450) --> <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Таңдалды"</string> <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Айнала"</string> <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Сол жақ"</string> @@ -582,6 +586,8 @@ <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Қолданбаны бөліскен кезде, онда көрінетін не ойнатылатын барлық контент <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> қолданбасында көрсетіледі. Сондықтан құпия сөздерді, төлем туралы мәліметті, хабарларды, фотосуреттерді және аудио мен бейнені ашқанда сақ болыңыз."</string> <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Экранды бөлісу"</string> <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> қолданбасы осы опцияны өшірді."</string> + <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) --> + <skip /> <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Бөлісетін қолданба экранын таңдау"</string> <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Экранды трансляциялау керек пе?"</string> <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Бір қолданба экранын трансляциялау"</string> @@ -980,6 +986,8 @@ <string name="drag_to_remove_tiles" msgid="4682194717573850385">"Керексіздерін осы жерге сүйреңіз"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"Кемінде <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> бөлшек қажет."</string> <string name="qs_edit" msgid="5583565172803472437">"Өзгерту"</string> + <!-- no translation found for qs_edit_tiles (2105215324060865035) --> + <skip /> <string name="tuner_time" msgid="2450785840990529997">"Уақыт"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"Сағаттарды, минуттарды және секундтарды көрсету"</item> diff --git a/packages/SystemUI/res/values-kk/tiles_states_strings.xml b/packages/SystemUI/res/values-kk/tiles_states_strings.xml index 45316aa1391e..a177ca2862da 100644 --- a/packages/SystemUI/res/values-kk/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-kk/tiles_states_strings.xml @@ -56,9 +56,11 @@ <item msgid="5376619709702103243">"Өшірулі"</item> <item msgid="4875147066469902392">"Қосулы"</item> </string-array> - <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) --> - <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) --> - <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) --> + <string-array name="tile_states_modes_dnd"> + <item msgid="6509540227356524582">"Қолжетімді емес"</item> + <item msgid="8589336868985358191">"Өшірулі"</item> + <item msgid="726072717827778234">"Қосулы"</item> + </string-array> <string-array name="tile_states_flashlight"> <item msgid="3465257127433353857">"Қолжетімсіз"</item> <item msgid="5044688398303285224">"Өшірулі"</item> diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml index 579c509dd6db..349e34a2c8cd 100644 --- a/packages/SystemUI/res/values-km/strings.xml +++ b/packages/SystemUI/res/values-km/strings.xml @@ -419,6 +419,10 @@ <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> + <!-- no translation found for hearing_devices_input_routing_label (730396728151232306) --> + <skip /> + <!-- no translation found for hearing_device_input_routing_options:0 (4582190415045337003) --> + <!-- no translation found for hearing_device_input_routing_options:1 (8501466270452446450) --> <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"បានជ្រើសរើស"</string> <string name="hearing_devices_ambient_label" msgid="629440938614895797">"មជ្ឈដ្ឋានជុំវិញ"</string> <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"ឆ្វេង"</string> @@ -582,6 +586,8 @@ <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"នៅពេលអ្នកបង្ហាញកម្មវិធីណាមួយ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> មើលឃើញអ្វីគ្រប់យ៉ាងដែលបង្ហាញ ឬចាក់ក្នុងកម្មវិធីនោះ។ ដូច្នេះ សូមប្រុងប្រយ័ត្នចំពោះអ្វីៗដូចជា ពាក្យសម្ងាត់ ព័ត៌មានលម្អិតអំពីការទូទាត់ប្រាក់ សារ រូបថត ព្រមទាំងសំឡេង និងវីដេអូ។"</string> <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"បង្ហាញអេក្រង់"</string> <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> បានបិទជម្រើសនេះ"</string> + <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) --> + <skip /> <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"ជ្រើសរើសកម្មវិធីដើម្បីចែករំលែក"</string> <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"បញ្ជូនអេក្រង់របស់អ្នកឬ?"</string> <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"បញ្ជូនកម្មវិធីមួយ"</string> @@ -980,6 +986,8 @@ <string name="drag_to_remove_tiles" msgid="4682194717573850385">"អូសទីនេះដើម្បីយកចេញ"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"អ្នកត្រូវការប្រអប់យ៉ាងតិច <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g>"</string> <string name="qs_edit" msgid="5583565172803472437">"កែ"</string> + <!-- no translation found for qs_edit_tiles (2105215324060865035) --> + <skip /> <string name="tuner_time" msgid="2450785840990529997">"ម៉ោង"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"បង្ហាញម៉ោង នាទី និងវិនាទី"</item> diff --git a/packages/SystemUI/res/values-km/tiles_states_strings.xml b/packages/SystemUI/res/values-km/tiles_states_strings.xml index ec120089a220..49c549f6016a 100644 --- a/packages/SystemUI/res/values-km/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-km/tiles_states_strings.xml @@ -56,9 +56,11 @@ <item msgid="5376619709702103243">"បិទ"</item> <item msgid="4875147066469902392">"បើក"</item> </string-array> - <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) --> - <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) --> - <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) --> + <string-array name="tile_states_modes_dnd"> + <item msgid="6509540227356524582">"មិនមានទេ"</item> + <item msgid="8589336868985358191">"បិទ"</item> + <item msgid="726072717827778234">"បើក"</item> + </string-array> <string-array name="tile_states_flashlight"> <item msgid="3465257127433353857">"មិនមានទេ"</item> <item msgid="5044688398303285224">"បិទ"</item> diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml index c84b33baed10..0fa4c2476151 100644 --- a/packages/SystemUI/res/values-kn/strings.xml +++ b/packages/SystemUI/res/values-kn/strings.xml @@ -419,6 +419,10 @@ <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> + <!-- no translation found for hearing_devices_input_routing_label (730396728151232306) --> + <skip /> + <!-- no translation found for hearing_device_input_routing_options:0 (4582190415045337003) --> + <!-- no translation found for hearing_device_input_routing_options:1 (8501466270452446450) --> <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"ಆಯ್ಕೆಮಾಡಲಾಗಿದೆ"</string> <string name="hearing_devices_ambient_label" msgid="629440938614895797">"ಆ್ಯಂಬಿಯೆಂಟ್ ವಾಲ್ಯೂಮ್"</string> <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"ಎಡ"</string> @@ -582,6 +586,8 @@ <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"ನೀವು ಆ್ಯಪ್ ಅನ್ನು ಹಂಚಿಕೊಳ್ಳುತ್ತಿರುವಾಗ, ಆ ಆ್ಯಪ್ನಲ್ಲಿ ತೋರಿಸಿರುವ ಅಥವಾ ಪ್ಲೇ ಮಾಡಿದ ಏನಾದರೂ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ಗೆ ಗೋಚರಿಸುತ್ತದೆ. ಆದ್ದರಿಂದ ಪಾಸ್ವರ್ಡ್ಗಳು, ಪಾವತಿ ವಿವರಗಳು, ಸಂದೇಶಗಳು, ಫೋಟೋಗಳು ಮತ್ತು ಆಡಿಯೋ ಮತ್ತು ವೀಡಿಯೊದಂತಹ ವಿಷಯಗಳ ಬಗ್ಗೆ ಜಾಗರೂಕರಾಗಿರಿ."</string> <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"ಸ್ಕ್ರೀನ್ ಹಂಚಿಕೊಳ್ಳಿ"</string> <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> ಈ ಆಯ್ಕೆಯನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಿದೆ"</string> + <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) --> + <skip /> <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"ಹಂಚಿಕೊಳ್ಳಲು ಆ್ಯಪ್ ಅನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string> <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"ನಿಮ್ಮ ಸ್ಕ್ರೀನ್ ಅನ್ನು ಬಿತ್ತರಿಸಬೇಕೇ?"</string> <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"ಒಂದು ಆ್ಯಪ್ ಅನ್ನು ಬಿತ್ತರಿಸಿ"</string> @@ -980,6 +986,8 @@ <string name="drag_to_remove_tiles" msgid="4682194717573850385">"ತೆಗೆದುಹಾಕಲು ಇಲ್ಲಿ ಡ್ರ್ಯಾಗ್ ಮಾಡಿ"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"ನಿಮಗೆ ಕನಿಷ್ಠ <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> ಟೈಲ್ಗಳ ಅಗತ್ಯವಿದೆ"</string> <string name="qs_edit" msgid="5583565172803472437">"ಎಡಿಟ್"</string> + <!-- no translation found for qs_edit_tiles (2105215324060865035) --> + <skip /> <string name="tuner_time" msgid="2450785840990529997">"ಸಮಯ"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"ಗಂಟೆಗಳು, ನಿಮಿಷಗಳು, ಸೆಕೆಂಡುಗಳನ್ನು ತೋರಿಸು"</item> diff --git a/packages/SystemUI/res/values-kn/tiles_states_strings.xml b/packages/SystemUI/res/values-kn/tiles_states_strings.xml index ad57922c9f2c..712936a39299 100644 --- a/packages/SystemUI/res/values-kn/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-kn/tiles_states_strings.xml @@ -56,9 +56,11 @@ <item msgid="5376619709702103243">"ಆಫ್"</item> <item msgid="4875147066469902392">"ಆನ್"</item> </string-array> - <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) --> - <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) --> - <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) --> + <string-array name="tile_states_modes_dnd"> + <item msgid="6509540227356524582">"ಲಭ್ಯವಿಲ್ಲ"</item> + <item msgid="8589336868985358191">"ಆಫ್ ಆಗಿದೆ"</item> + <item msgid="726072717827778234">"ಆನ್ ಆಗಿದೆ"</item> + </string-array> <string-array name="tile_states_flashlight"> <item msgid="3465257127433353857">"ಲಭ್ಯವಿಲ್ಲ"</item> <item msgid="5044688398303285224">"ಆಫ್"</item> diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml index 048050cf53df..2b9fc89af8b4 100644 --- a/packages/SystemUI/res/values-ko/strings.xml +++ b/packages/SystemUI/res/values-ko/strings.xml @@ -419,6 +419,10 @@ <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> + <!-- no translation found for hearing_devices_input_routing_label (730396728151232306) --> + <skip /> + <!-- no translation found for hearing_device_input_routing_options:0 (4582190415045337003) --> + <!-- no translation found for hearing_device_input_routing_options:1 (8501466270452446450) --> <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"선택됨"</string> <string name="hearing_devices_ambient_label" msgid="629440938614895797">"주변 소리"</string> <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"왼쪽"</string> @@ -582,6 +586,8 @@ <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"앱을 공유하면 앱에 표시되거나 앱에서 재생되는 모든 항목이 <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>에 표시됩니다. 따라서 비밀번호, 결제 세부정보, 메시지, 사진, 오디오 및 동영상 등이 노출되지 않도록 주의하세요."</string> <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"화면 공유"</string> <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g>에서 이 옵션을 사용 중지했습니다."</string> + <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) --> + <skip /> <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"공유할 앱 선택"</string> <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"화면을 전송하시겠습니까?"</string> <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"앱 1개 전송"</string> @@ -980,6 +986,8 @@ <string name="drag_to_remove_tiles" msgid="4682194717573850385">"여기로 드래그하여 삭제"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"<xliff:g id="MIN_NUM_TILES">%1$d</xliff:g>개 이상의 타일이 필요합니다."</string> <string name="qs_edit" msgid="5583565172803472437">"수정"</string> + <!-- no translation found for qs_edit_tiles (2105215324060865035) --> + <skip /> <string name="tuner_time" msgid="2450785840990529997">"시간"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"시간, 분, 초 표시"</item> diff --git a/packages/SystemUI/res/values-ko/tiles_states_strings.xml b/packages/SystemUI/res/values-ko/tiles_states_strings.xml index 49113eea79b7..f31f8106d374 100644 --- a/packages/SystemUI/res/values-ko/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-ko/tiles_states_strings.xml @@ -56,9 +56,11 @@ <item msgid="5376619709702103243">"꺼짐"</item> <item msgid="4875147066469902392">"켜짐"</item> </string-array> - <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) --> - <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) --> - <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) --> + <string-array name="tile_states_modes_dnd"> + <item msgid="6509540227356524582">"사용할 수 없음"</item> + <item msgid="8589336868985358191">"사용 안함"</item> + <item msgid="726072717827778234">"사용"</item> + </string-array> <string-array name="tile_states_flashlight"> <item msgid="3465257127433353857">"이용 불가"</item> <item msgid="5044688398303285224">"꺼짐"</item> diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml index e6b108126012..b1f6f484ed59 100644 --- a/packages/SystemUI/res/values-ky/strings.xml +++ b/packages/SystemUI/res/values-ky/strings.xml @@ -419,6 +419,10 @@ <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> + <!-- no translation found for hearing_devices_input_routing_label (730396728151232306) --> + <skip /> + <!-- no translation found for hearing_device_input_routing_options:0 (4582190415045337003) --> + <!-- no translation found for hearing_device_input_routing_options:1 (8501466270452446450) --> <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Тандалды"</string> <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Айланадагы үндөр"</string> <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Сол"</string> @@ -582,6 +586,8 @@ <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Колдонмону бөлүшкөндө, андагы нерселер <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> колдонмосуна көрүнөт. Андыктан сырсөздөр, төлөмдүн чоо-жайы, билдирүүлөр, сүрөттөр, аудио жана видеолор менен этият болуңуз."</string> <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Экранды бөлүшүү"</string> <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> бул параметрди өчүрүп койду"</string> + <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) --> + <skip /> <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Бөлүшүү үчүн колдонмо тандоо"</string> <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Экранды башка түзмөккө чыгарасызбы?"</string> <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Бир колдонмону чыгаруу"</string> @@ -980,6 +986,8 @@ <string name="drag_to_remove_tiles" msgid="4682194717573850385">"Өчүрүү үчүн бул жерге сүйрөңүз"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"Сизге жок дегенде <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> мозаика керек"</string> <string name="qs_edit" msgid="5583565172803472437">"Түзөтүү"</string> + <!-- no translation found for qs_edit_tiles (2105215324060865035) --> + <skip /> <string name="tuner_time" msgid="2450785840990529997">"Убакыт"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"Сааттар, мүнөттөр жана секунддар"</item> diff --git a/packages/SystemUI/res/values-ky/tiles_states_strings.xml b/packages/SystemUI/res/values-ky/tiles_states_strings.xml index 68f8987a02dc..fe451facb749 100644 --- a/packages/SystemUI/res/values-ky/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-ky/tiles_states_strings.xml @@ -56,9 +56,11 @@ <item msgid="5376619709702103243">"Өчүк"</item> <item msgid="4875147066469902392">"Күйүк"</item> </string-array> - <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) --> - <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) --> - <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) --> + <string-array name="tile_states_modes_dnd"> + <item msgid="6509540227356524582">"Жеткиликсиз"</item> + <item msgid="8589336868985358191">"Өчүк"</item> + <item msgid="726072717827778234">"Күйүк"</item> + </string-array> <string-array name="tile_states_flashlight"> <item msgid="3465257127433353857">"Жеткиликсиз"</item> <item msgid="5044688398303285224">"Өчүк"</item> diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml index e7229e8f3704..c2a4e8fad79b 100644 --- a/packages/SystemUI/res/values-lo/strings.xml +++ b/packages/SystemUI/res/values-lo/strings.xml @@ -419,6 +419,10 @@ <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> + <!-- no translation found for hearing_devices_input_routing_label (730396728151232306) --> + <skip /> + <!-- no translation found for hearing_device_input_routing_options:0 (4582190415045337003) --> + <!-- no translation found for hearing_device_input_routing_options:1 (8501466270452446450) --> <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"ເລືອກແລ້ວ"</string> <string name="hearing_devices_ambient_label" msgid="629440938614895797">"ສຽງແວດລ້ອມ"</string> <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"ຊ້າຍ"</string> @@ -582,6 +586,8 @@ <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"ເມື່ອທ່ານແບ່ງປັນແອັບຂອງທ່ານ, ຄົນອື່ນຈະເບິ່ງເຫັນທຸກຢ່າງທີ່ສະແດງ ຫຼື ຫຼິ້ນໃນແອັບໃນ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. ດັ່ງນັ້ນ, ໃຫ້ລະມັດລະວັງສິ່ງຕ່າງໆ ເຊັ່ນ: ລະຫັດຜ່ານ, ລາຍລະອຽດການຈ່າຍເງິນ, ຂໍ້ຄວາມ, ຮູບພາບ, ພ້ອມທັງສຽງ ແລະ ວິດີໂອ."</string> <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"ແບ່ງປັນໜ້າຈໍ"</string> <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> ປິດການນຳໃຊ້ຕົວເລືອກນີ້ແລ້ວ"</string> + <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) --> + <skip /> <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"ເລືອກແອັບທີ່ຈະແບ່ງປັນ"</string> <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"ສົ່ງສັນຍານໜ້າຈໍຂອງທ່ານບໍ?"</string> <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"ສົ່ງສັນຍານແອັບ 1 ລາຍການ"</string> @@ -980,6 +986,8 @@ <string name="drag_to_remove_tiles" msgid="4682194717573850385">"ລາກມາບ່ອນນີ້ເພື່ອລຶບອອກ"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"ທ່ານຍຕ້ອງໃຊ້ຢ່າງໜ້ອຍ <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> ຊ່ອງ"</string> <string name="qs_edit" msgid="5583565172803472437">"ແກ້ໄຂ"</string> + <!-- no translation found for qs_edit_tiles (2105215324060865035) --> + <skip /> <string name="tuner_time" msgid="2450785840990529997">"ເວລາ"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"ສະແດງຊົ່ວໂມງ, ນາທີ ແລະ ວິນາທີ"</item> diff --git a/packages/SystemUI/res/values-lo/tiles_states_strings.xml b/packages/SystemUI/res/values-lo/tiles_states_strings.xml index 90390e23162b..72c57e645879 100644 --- a/packages/SystemUI/res/values-lo/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-lo/tiles_states_strings.xml @@ -56,9 +56,11 @@ <item msgid="5376619709702103243">"ປິດ"</item> <item msgid="4875147066469902392">"ເປີດ"</item> </string-array> - <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) --> - <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) --> - <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) --> + <string-array name="tile_states_modes_dnd"> + <item msgid="6509540227356524582">"ບໍ່ພ້ອມໃຫ້ນຳໃຊ້"</item> + <item msgid="8589336868985358191">"ປິດ"</item> + <item msgid="726072717827778234">"ເປີດ"</item> + </string-array> <string-array name="tile_states_flashlight"> <item msgid="3465257127433353857">"ບໍ່ສາມາດໃຊ້ໄດ້"</item> <item msgid="5044688398303285224">"ປິດ"</item> diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml index 6563cb97f35d..48ab51b46e57 100644 --- a/packages/SystemUI/res/values-lt/strings.xml +++ b/packages/SystemUI/res/values-lt/strings.xml @@ -419,6 +419,10 @@ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Spustelėkite, kad susietumėte naują įrenginį"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Išankstinių nustatymų atnaujinti nepavyko"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Išankstiniai nustatymai"</string> + <!-- no translation found for hearing_devices_input_routing_label (730396728151232306) --> + <skip /> + <!-- no translation found for hearing_device_input_routing_options:0 (4582190415045337003) --> + <!-- no translation found for hearing_device_input_routing_options:1 (8501466270452446450) --> <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Pasirinkta"</string> <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Aplinka"</string> <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Kairė"</string> @@ -582,6 +586,8 @@ <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Kai bendrinate programą, „<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>“ matomas visas toje programoje rodomas ar leidžiamas turinys. Todėl būkite atsargūs naudodami slaptažodžius, išsamią mokėjimo metodo informaciją, pranešimus, nuotraukas ir garso bei vaizdo įrašus."</string> <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Bendrinti ekraną"</string> <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"Programoje „<xliff:g id="APP_NAME">%1$s</xliff:g>“ ši parinktis išjungta"</string> + <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) --> + <skip /> <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Norimos bendrinti programos pasirinkimas"</string> <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Perduoti ekraną?"</string> <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Perduoti vieną programą"</string> @@ -980,6 +986,8 @@ <string name="drag_to_remove_tiles" msgid="4682194717573850385">"Vilkite čia, jei norite pašalinti"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"Turi būti bent <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> išklotinės elem."</string> <string name="qs_edit" msgid="5583565172803472437">"Redaguoti"</string> + <!-- no translation found for qs_edit_tiles (2105215324060865035) --> + <skip /> <string name="tuner_time" msgid="2450785840990529997">"Laikas"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"Rodyti valandas, minutes ir sekundes"</item> diff --git a/packages/SystemUI/res/values-lt/tiles_states_strings.xml b/packages/SystemUI/res/values-lt/tiles_states_strings.xml index 3e1f3c702e61..4ed6bd95303a 100644 --- a/packages/SystemUI/res/values-lt/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-lt/tiles_states_strings.xml @@ -56,9 +56,11 @@ <item msgid="5376619709702103243">"Išjungta"</item> <item msgid="4875147066469902392">"Įjungta"</item> </string-array> - <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) --> - <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) --> - <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) --> + <string-array name="tile_states_modes_dnd"> + <item msgid="6509540227356524582">"Nepasiekiama"</item> + <item msgid="8589336868985358191">"Išjungta"</item> + <item msgid="726072717827778234">"Įjungta"</item> + </string-array> <string-array name="tile_states_flashlight"> <item msgid="3465257127433353857">"Nepasiekiama"</item> <item msgid="5044688398303285224">"Išjungta"</item> diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml index 0c030b7029f9..e5c407f30b6c 100644 --- a/packages/SystemUI/res/values-lv/strings.xml +++ b/packages/SystemUI/res/values-lv/strings.xml @@ -419,6 +419,10 @@ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Noklikšķiniet, lai savienotu pārī jaunu ierīci"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Nevarēja atjaunināt pirmsiestatījumu"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Pirmsiestatījums"</string> + <!-- no translation found for hearing_devices_input_routing_label (730396728151232306) --> + <skip /> + <!-- no translation found for hearing_device_input_routing_options:0 (4582190415045337003) --> + <!-- no translation found for hearing_device_input_routing_options:1 (8501466270452446450) --> <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Atlasīts"</string> <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Apkārtnes skaņas"</string> <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Pa kreisi"</string> @@ -582,6 +586,8 @@ <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Kopīgojot lietotni, lietotnei <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ir pieejams viss kopīgotajā lietotnē parādītais vai atskaņotais saturs. Tāpēc piesardzīgi apejieties ar parolēm, maksājumu informāciju, ziņojumiem, fotoattēliem un audio un video saturu."</string> <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Kopīgot ekrānu"</string> <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"Lietotnē <xliff:g id="APP_NAME">%1$s</xliff:g> tika atspējota šī opcija"</string> + <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) --> + <skip /> <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Lietotnes izvēlēšanās kopīgošanai"</string> <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Vai apraidīt ekrānu?"</string> <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Apraidīt vienu lietotni"</string> @@ -980,6 +986,8 @@ <string name="drag_to_remove_tiles" msgid="4682194717573850385">"Lai noņemtu vienumus, velciet tos šeit."</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"Nepieciešami vismaz <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> elementi"</string> <string name="qs_edit" msgid="5583565172803472437">"Rediģēt"</string> + <!-- no translation found for qs_edit_tiles (2105215324060865035) --> + <skip /> <string name="tuner_time" msgid="2450785840990529997">"Laiks"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"Rādīt stundas, minūtes un sekundes"</item> diff --git a/packages/SystemUI/res/values-lv/tiles_states_strings.xml b/packages/SystemUI/res/values-lv/tiles_states_strings.xml index e55babc7317b..0c59186e2165 100644 --- a/packages/SystemUI/res/values-lv/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-lv/tiles_states_strings.xml @@ -56,9 +56,11 @@ <item msgid="5376619709702103243">"Izslēgts"</item> <item msgid="4875147066469902392">"Ieslēgts"</item> </string-array> - <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) --> - <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) --> - <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) --> + <string-array name="tile_states_modes_dnd"> + <item msgid="6509540227356524582">"Nav pieejams"</item> + <item msgid="8589336868985358191">"Izslēgts"</item> + <item msgid="726072717827778234">"Ieslēgts"</item> + </string-array> <string-array name="tile_states_flashlight"> <item msgid="3465257127433353857">"Nav pieejams"</item> <item msgid="5044688398303285224">"Izslēgts"</item> diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml index b798f457ee8b..e546fb584ed3 100644 --- a/packages/SystemUI/res/values-mk/strings.xml +++ b/packages/SystemUI/res/values-mk/strings.xml @@ -419,6 +419,10 @@ <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> + <!-- no translation found for hearing_devices_input_routing_label (730396728151232306) --> + <skip /> + <!-- no translation found for hearing_device_input_routing_options:0 (4582190415045337003) --> + <!-- no translation found for hearing_device_input_routing_options:1 (8501466270452446450) --> <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Избрано"</string> <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Опкружување"</string> <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Лево"</string> @@ -582,6 +586,8 @@ <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Додека споделувате апликација, сѐ што се прикажува или пушта на таа апликација е видливо за <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Затоа, бидете внимателни со работи како лозинки, детали за плаќање, пораки, фотографии и аудио и видео."</string> <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Сподели екран"</string> <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> ја оневозможи опцијава"</string> + <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) --> + <skip /> <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Изберете апликација за споделување"</string> <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Да се емитува вашиот екран?"</string> <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Емитувајте една апликација"</string> @@ -980,6 +986,8 @@ <string name="drag_to_remove_tiles" msgid="4682194717573850385">"Повлечете тука за да се отстрани"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"Потребни ви се најмалку <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> плочки"</string> <string name="qs_edit" msgid="5583565172803472437">"Измени"</string> + <!-- no translation found for qs_edit_tiles (2105215324060865035) --> + <skip /> <string name="tuner_time" msgid="2450785840990529997">"Време"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"Прикажи часови, минути и секунди"</item> diff --git a/packages/SystemUI/res/values-mk/tiles_states_strings.xml b/packages/SystemUI/res/values-mk/tiles_states_strings.xml index 8986ca5a7c86..0f32b8e88573 100644 --- a/packages/SystemUI/res/values-mk/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-mk/tiles_states_strings.xml @@ -56,9 +56,11 @@ <item msgid="5376619709702103243">"Исклучено"</item> <item msgid="4875147066469902392">"Вклучено"</item> </string-array> - <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) --> - <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) --> - <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) --> + <string-array name="tile_states_modes_dnd"> + <item msgid="6509540227356524582">"Недостапно"</item> + <item msgid="8589336868985358191">"Исклучено"</item> + <item msgid="726072717827778234">"Вклучено"</item> + </string-array> <string-array name="tile_states_flashlight"> <item msgid="3465257127433353857">"Недостапно"</item> <item msgid="5044688398303285224">"Исклучено"</item> diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml index eb7da33b02d2..17b1c3d9d49c 100644 --- a/packages/SystemUI/res/values-ml/strings.xml +++ b/packages/SystemUI/res/values-ml/strings.xml @@ -419,6 +419,10 @@ <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> + <!-- no translation found for hearing_devices_input_routing_label (730396728151232306) --> + <skip /> + <!-- no translation found for hearing_device_input_routing_options:0 (4582190415045337003) --> + <!-- no translation found for hearing_device_input_routing_options:1 (8501466270452446450) --> <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"തിരഞ്ഞെടുത്തു"</string> <string name="hearing_devices_ambient_label" msgid="629440938614895797">"സറൗണ്ടിംഗ്സ്"</string> <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"ഇടത്"</string> @@ -582,6 +586,8 @@ <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"നിങ്ങളുടെ ആപ്പ് പങ്കിടുമ്പോൾ, ആ ആപ്പിൽ കാണിക്കുന്നതോ പ്ലേ ചെയ്യുന്നതോ ആയ എല്ലാ കാര്യങ്ങളും <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> എന്നതിന് ദൃശ്യമാകും. അതിനാൽ പാസ്വേഡുകൾ, പേയ്മെന്റ് വിശദാംശങ്ങൾ, സന്ദേശങ്ങൾ, ഫോട്ടോകൾ, ഓഡിയോ, വീഡിയോ എന്നിവ പോലുള്ള കാര്യങ്ങളിൽ ശ്രദ്ധ പുലർത്തുക."</string> <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"സ്ക്രീൻ പങ്കിടുക"</string> <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> ഈ ഓപ്ഷൻ പ്രവർത്തനരഹിതമാക്കി"</string> + <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) --> + <skip /> <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"പങ്കിടാൻ ആപ്പ് തിരഞ്ഞെടുക്കുക"</string> <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"നിങ്ങളുടെ സ്ക്രീൻ കാസ്റ്റ് ചെയ്യണോ?"</string> <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"ഒരു ആപ്പ് കാസ്റ്റ് ചെയ്യുക"</string> @@ -980,6 +986,8 @@ <string name="drag_to_remove_tiles" msgid="4682194717573850385">"നീക്കംചെയ്യുന്നതിന് ഇവിടെ വലിച്ചിടുക"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"നിങ്ങൾക്ക് ചുരുങ്ങിയത് <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> ടൈലുകളെങ്കിലും വേണം"</string> <string name="qs_edit" msgid="5583565172803472437">"എഡിറ്റ് ചെയ്യുക"</string> + <!-- no translation found for qs_edit_tiles (2105215324060865035) --> + <skip /> <string name="tuner_time" msgid="2450785840990529997">"സമയം"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"മണിക്കൂറും മിനിറ്റും സെക്കൻഡും കാണിക്കുക"</item> diff --git a/packages/SystemUI/res/values-ml/tiles_states_strings.xml b/packages/SystemUI/res/values-ml/tiles_states_strings.xml index a7098c9ef814..c6ba7f16fa22 100644 --- a/packages/SystemUI/res/values-ml/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-ml/tiles_states_strings.xml @@ -56,9 +56,11 @@ <item msgid="5376619709702103243">"ഓഫാണ്"</item> <item msgid="4875147066469902392">"ഓണാണ്"</item> </string-array> - <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) --> - <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) --> - <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) --> + <string-array name="tile_states_modes_dnd"> + <item msgid="6509540227356524582">"ലഭ്യമല്ല"</item> + <item msgid="8589336868985358191">"ഓഫാണ്"</item> + <item msgid="726072717827778234">"ഓണാണ്"</item> + </string-array> <string-array name="tile_states_flashlight"> <item msgid="3465257127433353857">"ലഭ്യമല്ല"</item> <item msgid="5044688398303285224">"ഓഫാണ്"</item> diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml index 3adddc2d27ad..ba7baf4ae5b0 100644 --- a/packages/SystemUI/res/values-mn/strings.xml +++ b/packages/SystemUI/res/values-mn/strings.xml @@ -419,6 +419,10 @@ <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> + <!-- no translation found for hearing_devices_input_routing_label (730396728151232306) --> + <skip /> + <!-- no translation found for hearing_device_input_routing_options:0 (4582190415045337003) --> + <!-- no translation found for hearing_device_input_routing_options:1 (8501466270452446450) --> <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Сонгосон"</string> <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Орчин тойрон"</string> <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Зүүн"</string> @@ -582,6 +586,8 @@ <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Таныг апп хуваалцаж байхад тухайн аппад харуулж эсвэл тоглуулж буй аливаа зүйл <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>-д харагдана. Тиймээс нууц үг, төлбөрийн дэлгэрэнгүй, мессеж, зураг, аудио, видео зэрэг зүйлд болгоомжтой хандаарай."</string> <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Дэлгэцийг хуваалцах"</string> <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> энэ сонголтыг идэвхгүй болгосон"</string> + <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) --> + <skip /> <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Хуваалцах апп сонгох"</string> <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Дэлгэцээ дамжуулах уу?"</string> <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Нэг апп дамжуулах"</string> @@ -980,6 +986,8 @@ <string name="drag_to_remove_tiles" msgid="4682194717573850385">"Устгахын тулд энд зөөнө үү"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"Танд хамгийн багадаа <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> хавтан шаардлагатай"</string> <string name="qs_edit" msgid="5583565172803472437">"Засах"</string> + <!-- no translation found for qs_edit_tiles (2105215324060865035) --> + <skip /> <string name="tuner_time" msgid="2450785840990529997">"Цаг"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"Цаг, минут, секундийг харуулах"</item> diff --git a/packages/SystemUI/res/values-mn/tiles_states_strings.xml b/packages/SystemUI/res/values-mn/tiles_states_strings.xml index 9b5a6e3c3037..394c42e5f3a7 100644 --- a/packages/SystemUI/res/values-mn/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-mn/tiles_states_strings.xml @@ -56,9 +56,11 @@ <item msgid="5376619709702103243">"Унтраалттай"</item> <item msgid="4875147066469902392">"Асаалттай"</item> </string-array> - <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) --> - <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) --> - <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) --> + <string-array name="tile_states_modes_dnd"> + <item msgid="6509540227356524582">"Боломжгүй"</item> + <item msgid="8589336868985358191">"Унтраалттай"</item> + <item msgid="726072717827778234">"Асаалттай"</item> + </string-array> <string-array name="tile_states_flashlight"> <item msgid="3465257127433353857">"Боломжгүй"</item> <item msgid="5044688398303285224">"Унтраалттай"</item> diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml index 08ef7b2e9cfc..d6d8e1cfa70d 100644 --- a/packages/SystemUI/res/values-mr/strings.xml +++ b/packages/SystemUI/res/values-mr/strings.xml @@ -419,6 +419,10 @@ <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> + <!-- no translation found for hearing_devices_input_routing_label (730396728151232306) --> + <skip /> + <!-- no translation found for hearing_device_input_routing_options:0 (4582190415045337003) --> + <!-- no translation found for hearing_device_input_routing_options:1 (8501466270452446450) --> <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"निवडला आहे"</string> <string name="hearing_devices_ambient_label" msgid="629440938614895797">"जवळपासचे"</string> <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"डावे"</string> @@ -582,6 +586,8 @@ <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"तुम्ही अॅप शेअर करता, तेव्हा त्या अॅपमध्ये दाखवल्या किंवा प्ले होणाऱ्या कोणत्याही गोष्टी <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> साठी दृश्यमान असतात. त्यामुळे पासवर्ड, पेमेंट तपशील, मेसेज, फोटो आणि ऑडिओ व व्हिडिओ यांसारख्या गोष्टींबाबत सावधगिरी बाळगा."</string> <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"स्क्रीन शेअर करा"</string> <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> हा पर्याय बंद केला आहे"</string> + <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) --> + <skip /> <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"शेअर करण्यासाठी अॅप निवडा"</string> <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"तुमची स्क्रीन कास्ट करायची आहे का?"</string> <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"एक अॅप कास्ट करा"</string> @@ -980,6 +986,8 @@ <string name="drag_to_remove_tiles" msgid="4682194717573850385">"काढण्यासाठी येथे ड्रॅग करा"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"तुम्हाला किमान <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> टाइलची गरज आहे"</string> <string name="qs_edit" msgid="5583565172803472437">"संपादित करा"</string> + <!-- no translation found for qs_edit_tiles (2105215324060865035) --> + <skip /> <string name="tuner_time" msgid="2450785840990529997">"वेळ"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"तास, मिनिटे आणि सेकंद दर्शवा"</item> diff --git a/packages/SystemUI/res/values-mr/tiles_states_strings.xml b/packages/SystemUI/res/values-mr/tiles_states_strings.xml index a5930d9ff295..4809c740406a 100644 --- a/packages/SystemUI/res/values-mr/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-mr/tiles_states_strings.xml @@ -56,9 +56,11 @@ <item msgid="5376619709702103243">"बंद आहे"</item> <item msgid="4875147066469902392">"सुरू आहे"</item> </string-array> - <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) --> - <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) --> - <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) --> + <string-array name="tile_states_modes_dnd"> + <item msgid="6509540227356524582">"उपलब्ध नाही"</item> + <item msgid="8589336868985358191">"बंद आहे"</item> + <item msgid="726072717827778234">"सुरू आहे"</item> + </string-array> <string-array name="tile_states_flashlight"> <item msgid="3465257127433353857">"उपलब्ध नाही"</item> <item msgid="5044688398303285224">"बंद आहे"</item> diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml index 5aec75608f76..2aa5068557d6 100644 --- a/packages/SystemUI/res/values-ms/strings.xml +++ b/packages/SystemUI/res/values-ms/strings.xml @@ -419,6 +419,10 @@ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Klik untuk menggandingkan peranti baharu"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Tidak dapat mengemaskinikan pratetapan"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Pratetapan"</string> + <!-- no translation found for hearing_devices_input_routing_label (730396728151232306) --> + <skip /> + <!-- no translation found for hearing_device_input_routing_options:0 (4582190415045337003) --> + <!-- no translation found for hearing_device_input_routing_options:1 (8501466270452446450) --> <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Dipilih"</string> <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Persekitaran"</string> <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Kiri"</string> @@ -582,6 +586,8 @@ <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Apabila anda berkongsi apl, apa-apa sahaja kandungan yang dipaparkan atau dimainkan dalam apl tersebut boleh dilihat oleh <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Oleh hal yang demikian, berhati-hati dengan perkara seperti kata laluan, butiran pembayaran, mesej, foto dan audio serta video."</string> <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Kongsi skrin"</string> <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> telah melumpuhkan pilihan ini"</string> + <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) --> + <skip /> <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Pilih apl untuk dikongsi"</string> <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Hantar skrin anda?"</string> <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Hantar satu apl"</string> @@ -794,8 +800,7 @@ <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Hidup - Berasaskan wajah"</string> <string name="inline_done_button" msgid="6043094985588909584">"Selesai"</string> <string name="inline_ok_button" msgid="603075490581280343">"Guna"</string> - <!-- no translation found for inline_turn_off_notifications (2653064779176881329) --> - <skip /> + <string name="inline_turn_off_notifications" msgid="2653064779176881329">"Matikan"</string> <string name="notification_silence_title" msgid="8608090968400832335">"Senyap"</string> <string name="notification_alert_title" msgid="3656229781017543655">"Lalai"</string> <string name="notification_automatic_title" msgid="3745465364578762652">"Automatik"</string> @@ -980,6 +985,8 @@ <string name="drag_to_remove_tiles" msgid="4682194717573850385">"Seret ke sini untuk mengalih keluar"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"Anda memerlukan sekurang-kurangnya <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> jubin"</string> <string name="qs_edit" msgid="5583565172803472437">"Edit"</string> + <!-- no translation found for qs_edit_tiles (2105215324060865035) --> + <skip /> <string name="tuner_time" msgid="2450785840990529997">"Masa"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"Tunjukkan jam, minit dan saat"</item> diff --git a/packages/SystemUI/res/values-ms/tiles_states_strings.xml b/packages/SystemUI/res/values-ms/tiles_states_strings.xml index eca55a34e028..88544435c076 100644 --- a/packages/SystemUI/res/values-ms/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-ms/tiles_states_strings.xml @@ -56,9 +56,11 @@ <item msgid="5376619709702103243">"Mati"</item> <item msgid="4875147066469902392">"Hidup"</item> </string-array> - <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) --> - <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) --> - <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) --> + <string-array name="tile_states_modes_dnd"> + <item msgid="6509540227356524582">"Tidak tersedia"</item> + <item msgid="8589336868985358191">"Mati"</item> + <item msgid="726072717827778234">"Hidup"</item> + </string-array> <string-array name="tile_states_flashlight"> <item msgid="3465257127433353857">"Tidak tersedia"</item> <item msgid="5044688398303285224">"Mati"</item> diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml index 98f937b377e1..70f8259df0a7 100644 --- a/packages/SystemUI/res/values-my/strings.xml +++ b/packages/SystemUI/res/values-my/strings.xml @@ -419,6 +419,10 @@ <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> + <!-- no translation found for hearing_devices_input_routing_label (730396728151232306) --> + <skip /> + <!-- no translation found for hearing_device_input_routing_options:0 (4582190415045337003) --> + <!-- no translation found for hearing_device_input_routing_options:1 (8501466270452446450) --> <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"ရွေးထားသည်"</string> <string name="hearing_devices_ambient_label" msgid="629440938614895797">"ဝန်းကျင်အသံ"</string> <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"ဘယ်"</string> @@ -582,6 +586,8 @@ <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"အက်ပ်ကို မျှဝေနေချိန်တွင် ယင်းအက်ပ်တွင် ပြထားသော (သို့) ဖွင့်ထားသော အရာအားလုံးကို <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> က မြင်နိုင်သည်။ စကားဝှက်၊ ငွေပေးချေမှု အချက်အလက်၊ မက်ဆေ့ဂျ်၊ ဓာတ်ပုံ၊ အသံနှင့် ဗီဒီယိုကဲ့သို့ အရာများကို ဂရုစိုက်ပါ။"</string> <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"စခရင် မျှဝေရန်"</string> <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> သည် ဤရွေးစရာကို ပိတ်ထားသည်"</string> + <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) --> + <skip /> <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"မျှဝေရန် အက်ပ်ရွေးခြင်း"</string> <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"သင့်ဖန်သားပြင်ကို ကာစ်လုပ်မလား။"</string> <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"အက်ပ်တစ်ခုကို ကာစ်လုပ်ရန်"</string> @@ -980,6 +986,8 @@ <string name="drag_to_remove_tiles" msgid="4682194717573850385">"ဖယ်ရှားရန် ဤနေရာသို့ဖိဆွဲပါ"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"အနည်းဆုံး <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> ကွက် ရှိရမည်"</string> <string name="qs_edit" msgid="5583565172803472437">"တည်းဖြတ်ရန်"</string> + <!-- no translation found for qs_edit_tiles (2105215324060865035) --> + <skip /> <string name="tuner_time" msgid="2450785840990529997">"အချိန်"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"နာရီ၊ မိနစ်နှင့် စက္ကန့်ကိုပြပါ"</item> diff --git a/packages/SystemUI/res/values-my/tiles_states_strings.xml b/packages/SystemUI/res/values-my/tiles_states_strings.xml index 2bd239043dea..75221239f686 100644 --- a/packages/SystemUI/res/values-my/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-my/tiles_states_strings.xml @@ -56,9 +56,11 @@ <item msgid="5376619709702103243">"ပိတ်"</item> <item msgid="4875147066469902392">"ဖွင့်"</item> </string-array> - <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) --> - <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) --> - <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) --> + <string-array name="tile_states_modes_dnd"> + <item msgid="6509540227356524582">"မရနိုင်ပါ"</item> + <item msgid="8589336868985358191">"ပိတ်"</item> + <item msgid="726072717827778234">"ဖွင့်"</item> + </string-array> <string-array name="tile_states_flashlight"> <item msgid="3465257127433353857">"မရနိုင်ပါ"</item> <item msgid="5044688398303285224">"ပိတ်"</item> diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml index 627ab44fba26..82aeb65157d3 100644 --- a/packages/SystemUI/res/values-nb/strings.xml +++ b/packages/SystemUI/res/values-nb/strings.xml @@ -419,6 +419,10 @@ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Klikk for å koble til en ny enhet"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Kunne ikke oppdatere forhåndsinnstillingen"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Forhåndsinnstilling"</string> + <!-- no translation found for hearing_devices_input_routing_label (730396728151232306) --> + <skip /> + <!-- no translation found for hearing_device_input_routing_options:0 (4582190415045337003) --> + <!-- no translation found for hearing_device_input_routing_options:1 (8501466270452446450) --> <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Valgt"</string> <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Omgivelser"</string> <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Venstre"</string> @@ -582,6 +586,8 @@ <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Når du deler en app, er alt som vises eller spilles av i appen, synlig for <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Derfor bør du være forsiktig med for eksempel passord, betalingsopplysninger, meldinger, bilder, lyd og video."</string> <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Del skjermen"</string> <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> har deaktivert dette alternativet"</string> + <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) --> + <skip /> <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Velg app å dele"</string> <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Vil du caste skjermen?"</string> <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Cast én app"</string> @@ -980,6 +986,8 @@ <string name="drag_to_remove_tiles" msgid="4682194717573850385">"Dra hit for å fjerne"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"Du trenger minst <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> infobrikker"</string> <string name="qs_edit" msgid="5583565172803472437">"Endre"</string> + <!-- no translation found for qs_edit_tiles (2105215324060865035) --> + <skip /> <string name="tuner_time" msgid="2450785840990529997">"Tid"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"Vis timer, minutter og sekunder"</item> diff --git a/packages/SystemUI/res/values-nb/tiles_states_strings.xml b/packages/SystemUI/res/values-nb/tiles_states_strings.xml index fca868e98240..975c1c1c0b78 100644 --- a/packages/SystemUI/res/values-nb/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-nb/tiles_states_strings.xml @@ -56,9 +56,11 @@ <item msgid="5376619709702103243">"Av"</item> <item msgid="4875147066469902392">"På"</item> </string-array> - <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) --> - <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) --> - <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) --> + <string-array name="tile_states_modes_dnd"> + <item msgid="6509540227356524582">"Utilgjengelig"</item> + <item msgid="8589336868985358191">"Av"</item> + <item msgid="726072717827778234">"På"</item> + </string-array> <string-array name="tile_states_flashlight"> <item msgid="3465257127433353857">"Utilgjengelig"</item> <item msgid="5044688398303285224">"Av"</item> diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml index 6090ca509036..78164a250f42 100644 --- a/packages/SystemUI/res/values-ne/strings.xml +++ b/packages/SystemUI/res/values-ne/strings.xml @@ -419,6 +419,10 @@ <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> + <!-- no translation found for hearing_devices_input_routing_label (730396728151232306) --> + <skip /> + <!-- no translation found for hearing_device_input_routing_options:0 (4582190415045337003) --> + <!-- no translation found for hearing_device_input_routing_options:1 (8501466270452446450) --> <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"चयन गरिएको छ"</string> <string name="hearing_devices_ambient_label" msgid="629440938614895797">"वरपरका आवाज"</string> <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"बायाँ"</string> @@ -582,6 +586,8 @@ <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"तपाईंले यो एप सेयर गरिरहेका बेला यो एपमा देखाइने वा प्ले गरिने सबै सामग्री <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> मा देखिन्छ। त्यसैले पासवर्ड, भुक्तानीसम्बन्धी विवरण, म्यासेज, फोटो र अडियो तथा भिडियो जस्ता कुरा हेर्दा वा प्ले गर्दा सावधानी अपनाउनुहोला।"</string> <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"स्क्रिन सेयर गर्नुहोस्"</string> <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> ले यो विकल्प अफ गर्नुभएको छ"</string> + <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) --> + <skip /> <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"सेयर गर्न छनौट गर्नुहोस्"</string> <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"स्क्रिन कास्ट गर्ने हो?"</string> <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"एउटा एप कास्ट गर्नुहोस्"</string> @@ -980,6 +986,8 @@ <string name="drag_to_remove_tiles" msgid="4682194717573850385">"हटाउनका लागि यहाँ तान्नुहोस्"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"तपाईंलाई कम्तीमा <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> वटा टाइल चाहिन्छ"</string> <string name="qs_edit" msgid="5583565172803472437">"सम्पादन गर्नुहोस्"</string> + <!-- no translation found for qs_edit_tiles (2105215324060865035) --> + <skip /> <string name="tuner_time" msgid="2450785840990529997">"समय"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"घन्टा, मिनेट, र सेकेन्ड देखाउनुहोस्"</item> diff --git a/packages/SystemUI/res/values-ne/tiles_states_strings.xml b/packages/SystemUI/res/values-ne/tiles_states_strings.xml index 71f415a3d0ef..f48daa32df9e 100644 --- a/packages/SystemUI/res/values-ne/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-ne/tiles_states_strings.xml @@ -56,9 +56,11 @@ <item msgid="5376619709702103243">"अफ छ"</item> <item msgid="4875147066469902392">"अन छ"</item> </string-array> - <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) --> - <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) --> - <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) --> + <string-array name="tile_states_modes_dnd"> + <item msgid="6509540227356524582">"उपलब्ध छैन"</item> + <item msgid="8589336868985358191">"अफ छ"</item> + <item msgid="726072717827778234">"अन छ"</item> + </string-array> <string-array name="tile_states_flashlight"> <item msgid="3465257127433353857">"उपलब्ध छैन"</item> <item msgid="5044688398303285224">"अफ छ"</item> diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml index ebe9ad475229..fe6f34814279 100644 --- a/packages/SystemUI/res/values-nl/strings.xml +++ b/packages/SystemUI/res/values-nl/strings.xml @@ -419,6 +419,10 @@ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Klik om nieuw apparaat te koppelen"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Kan voorinstelling niet updaten"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Voorinstelling"</string> + <!-- no translation found for hearing_devices_input_routing_label (730396728151232306) --> + <skip /> + <!-- no translation found for hearing_device_input_routing_options:0 (4582190415045337003) --> + <!-- no translation found for hearing_device_input_routing_options:1 (8501466270452446450) --> <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Geselecteerd"</string> <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Omgevingsgeluid"</string> <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Links"</string> @@ -582,6 +586,8 @@ <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Als je een app deelt, is alles dat wordt getoond of afgespeeld in die app zichtbaar voor <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Wees daarom voorzichtig met bijvoorbeeld wachtwoorden, betalingsgegevens, berichten, foto\'s, en audio en video."</string> <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Scherm delen"</string> <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"Voor <xliff:g id="APP_NAME">%1$s</xliff:g> staat deze optie uit"</string> + <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) --> + <skip /> <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"App kiezen om te delen"</string> <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Je scherm casten?"</string> <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Eén app casten"</string> @@ -980,6 +986,8 @@ <string name="drag_to_remove_tiles" msgid="4682194717573850385">"Sleep hier naartoe om te verwijderen"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"Je hebt minimaal <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> tegels nodig"</string> <string name="qs_edit" msgid="5583565172803472437">"Bewerken"</string> + <!-- no translation found for qs_edit_tiles (2105215324060865035) --> + <skip /> <string name="tuner_time" msgid="2450785840990529997">"Tijd"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"Uren, minuten en seconden tonen"</item> diff --git a/packages/SystemUI/res/values-nl/tiles_states_strings.xml b/packages/SystemUI/res/values-nl/tiles_states_strings.xml index 01de4cbc574f..b739fa2211b3 100644 --- a/packages/SystemUI/res/values-nl/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-nl/tiles_states_strings.xml @@ -56,9 +56,11 @@ <item msgid="5376619709702103243">"Uit"</item> <item msgid="4875147066469902392">"Aan"</item> </string-array> - <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) --> - <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) --> - <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) --> + <string-array name="tile_states_modes_dnd"> + <item msgid="6509540227356524582">"Niet beschikbaar"</item> + <item msgid="8589336868985358191">"Uit"</item> + <item msgid="726072717827778234">"Aan"</item> + </string-array> <string-array name="tile_states_flashlight"> <item msgid="3465257127433353857">"Niet beschikbaar"</item> <item msgid="5044688398303285224">"Uit"</item> diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml index 8569c7aaad2e..fa16f28f15a8 100644 --- a/packages/SystemUI/res/values-or/strings.xml +++ b/packages/SystemUI/res/values-or/strings.xml @@ -419,6 +419,10 @@ <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> + <!-- no translation found for hearing_devices_input_routing_label (730396728151232306) --> + <skip /> + <!-- no translation found for hearing_device_input_routing_options:0 (4582190415045337003) --> + <!-- no translation found for hearing_device_input_routing_options:1 (8501466270452446450) --> <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"ଚୟନ କରାଯାଇଛି"</string> <string name="hearing_devices_ambient_label" msgid="629440938614895797">"ପରିପାର୍ଶ୍ୱ"</string> <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"ବାମ"</string> @@ -582,6 +586,8 @@ <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"ଆପଣ ଏକ ଆପ ସେୟାର କରିବା ସମୟରେ, ସେହି ଆପରେ ଦେଖାଯାଉଥିବା କିମ୍ବା ପ୍ଲେ ହେଉଥିବା ସବୁକିଛି <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>କୁ ଦେଖାଯାଏ। ତେଣୁ ପାସୱାର୍ଡ, ପେମେଣ୍ଟ ବିବରଣୀ, ମେସେଜ, ଫଟୋ ଏବଂ ଅଡିଓ ଓ ଭିଡିଓ ପରି ବିଷୟଗୁଡ଼ିକ ପ୍ରତି ସତର୍କ ରୁହନ୍ତୁ।"</string> <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"ସ୍କ୍ରିନ ସେୟାର କରନ୍ତୁ"</string> <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> ଏହି ବିକଳ୍ପକୁ ଅକ୍ଷମ କରିଛି"</string> + <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) --> + <skip /> <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"ସେୟାର କରିବାକୁ ଆପ ବାଛନ୍ତୁ"</string> <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"ଆପଣଙ୍କ ସ୍କ୍ରିନକୁ କାଷ୍ଟ କରିବେ?"</string> <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"ଗୋଟିଏ ଆପକୁ କାଷ୍ଟ କରନ୍ତୁ"</string> @@ -980,6 +986,8 @@ <string name="drag_to_remove_tiles" msgid="4682194717573850385">"ବାହାର କରିବାକୁ ଏଠାକୁ ଡ୍ରାଗ୍ କରନ୍ତୁ"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"ଆପଣଙ୍କର ଅତିକମ୍ରେ <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g>ଟି ଟାଇଲ୍ ଆବଶ୍ୟକ"</string> <string name="qs_edit" msgid="5583565172803472437">"ଏଡିଟ କରନ୍ତୁ"</string> + <!-- no translation found for qs_edit_tiles (2105215324060865035) --> + <skip /> <string name="tuner_time" msgid="2450785840990529997">"ସମୟ"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"ଘଣ୍ଟା, ମିନିଟ୍ ଏବଂ ସେକେଣ୍ଡ ଦେଖାନ୍ତୁ"</item> @@ -1249,7 +1257,7 @@ <string name="media_output_dialog_accessibility_title" msgid="4681741064190167888">"ଅଡିଓ ଆଉଟପୁଟ ପାଇଁ ଉପଲବ୍ଧ ଡିଭାଇସଗୁଡ଼ିକ।"</string> <string name="media_output_dialog_accessibility_seekbar" msgid="5332843993805568978">"ଭଲ୍ୟୁମ"</string> <string name="media_output_dialog_volume_percentage" msgid="1613984910585111798">"<xliff:g id="PERCENTAGE">%1$d</xliff:g>%%"</string> - <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"ସ୍ପିକର ଏବଂ ଡିସପ୍ଲେଗୁଡ଼ିକ"</string> + <string name="media_output_group_title_speakers_and_displays" msgid="7169712332365659820">"ସ୍ପିକର ଏବଂ ଡିସପ୍ଲେ"</string> <string name="media_output_group_title_suggested_device" msgid="4157186235837903826">"ପ୍ରସ୍ତାବିତ ଡିଭାଇସଗୁଡ଼ିକ"</string> <string name="media_input_group_title" msgid="2057057473860783021">"ଇନପୁଟ"</string> <string name="media_output_group_title" msgid="6789001895863332576">"ଆଉଟପୁଟ"</string> diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml index c7735a21b25b..98d56e34a6b0 100644 --- a/packages/SystemUI/res/values-pa/strings.xml +++ b/packages/SystemUI/res/values-pa/strings.xml @@ -419,6 +419,10 @@ <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> + <!-- no translation found for hearing_devices_input_routing_label (730396728151232306) --> + <skip /> + <!-- no translation found for hearing_device_input_routing_options:0 (4582190415045337003) --> + <!-- no translation found for hearing_device_input_routing_options:1 (8501466270452446450) --> <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"ਚੁਣਿਆ ਗਿਆ"</string> <string name="hearing_devices_ambient_label" msgid="629440938614895797">"ਆਲੇ-ਦੁਆਲੇ"</string> <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"ਖੱਬੇ"</string> @@ -582,6 +586,8 @@ <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"ਕਿਸੇ ਐਪ ਨੂੰ ਸਾਂਝਾ ਕਰਨ ਦੌਰਾਨ, ਉਸ ਐਪ \'ਤੇ ਦਿਖ ਰਹੀ ਜਾਂ ਚਲਾਈ ਗਈ ਹਰੇਕ ਚੀਜ਼ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ਨੂੰ ਦਿਖਣਯੋਗ ਹੁੰਦੀ ਹੈ। ਇਸ ਲਈ ਪਾਸਵਰਡਾਂ, ਭੁਗਤਾਨ ਵੇਰਵਿਆਂ, ਸੁਨੇਹਿਆਂ, ਫ਼ੋਟੋਆਂ ਅਤੇ ਆਡੀਓ ਅਤੇ ਵੀਡੀਓ ਵਰਗੀਆਂ ਚੀਜ਼ਾਂ ਵਾਸਤੇ ਸਾਵਧਾਨ ਰਹੋ।"</string> <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"ਸਕ੍ਰੀਨ ਸਾਂਝੀ ਕਰੋ"</string> <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> ਨੇ ਇਸ ਵਿਕਲਪ ਨੂੰ ਬੰਦ ਕਰ ਦਿੱਤਾ ਹੈ"</string> + <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) --> + <skip /> <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"ਸਾਂਝਾ ਕਰਨ ਲਈ ਐਪ ਚੁਣੋ"</string> <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"ਕੀ ਸਕ੍ਰੀਨ ਨੂੰ ਕਾਸਟ ਕਰਨਾ ਹੈ?"</string> <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"ਇੱਕ ਐਪ ਨੂੰ ਕਾਸਟ ਕਰੋ"</string> @@ -980,6 +986,8 @@ <string name="drag_to_remove_tiles" msgid="4682194717573850385">"ਹਟਾਉਣ ਲਈ ਇੱਥੇ ਘਸੀਟੋ"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"ਤੁਹਾਨੂੰ ਘੱਟੋ-ਘੱਟ <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> ਟਾਇਲਾਂ ਦੀ ਲੋੜ ਪਵੇਗੀ"</string> <string name="qs_edit" msgid="5583565172803472437">"ਸੰਪਾਦਨ ਕਰੋ"</string> + <!-- no translation found for qs_edit_tiles (2105215324060865035) --> + <skip /> <string name="tuner_time" msgid="2450785840990529997">"ਸਮਾਂ"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"ਘੰਟੇ, ਮਿੰਟ, ਅਤੇ ਸਕਿੰਟ ਦਿਖਾਓ"</item> diff --git a/packages/SystemUI/res/values-pa/tiles_states_strings.xml b/packages/SystemUI/res/values-pa/tiles_states_strings.xml index 43b8734bb274..d0f5103c32a1 100644 --- a/packages/SystemUI/res/values-pa/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-pa/tiles_states_strings.xml @@ -56,9 +56,11 @@ <item msgid="5376619709702103243">"ਬੰਦ ਹੈ"</item> <item msgid="4875147066469902392">"ਚਾਲੂ ਹੈ"</item> </string-array> - <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) --> - <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) --> - <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) --> + <string-array name="tile_states_modes_dnd"> + <item msgid="6509540227356524582">"ਉਪਲਬਧ ਨਹੀਂ"</item> + <item msgid="8589336868985358191">"ਬੰਦ"</item> + <item msgid="726072717827778234">"ਚਾਲੂ"</item> + </string-array> <string-array name="tile_states_flashlight"> <item msgid="3465257127433353857">"ਅਣਉਪਲਬਧ ਹੈ"</item> <item msgid="5044688398303285224">"ਬੰਦ ਹੈ"</item> diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml index 7c490736651c..cdf30a992f49 100644 --- a/packages/SystemUI/res/values-pl/strings.xml +++ b/packages/SystemUI/res/values-pl/strings.xml @@ -419,6 +419,10 @@ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kliknij, aby sparować nowe urządzenie"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Nie udało się zaktualizować gotowego ustawienia"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Gotowe ustawienie"</string> + <!-- no translation found for hearing_devices_input_routing_label (730396728151232306) --> + <skip /> + <!-- no translation found for hearing_device_input_routing_options:0 (4582190415045337003) --> + <!-- no translation found for hearing_device_input_routing_options:1 (8501466270452446450) --> <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Wybrano"</string> <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Otoczenie"</string> <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Po lewej"</string> @@ -582,6 +586,8 @@ <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Kiedy udostępniasz obraz z aplikacji <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>, widoczne jest wszystko to, co jest w niej wyświetlane lub odtwarzane. Dlatego zachowaj ostrożność w zakresie haseł, danych do płatności, wiadomości, zdjęć, audio i filmów."</string> <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Udostępnij ekran"</string> <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> ma wyłączoną tę opcję"</string> + <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) --> + <skip /> <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Wybierz aplikację do udostępniania"</string> <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Włączyć przesyłanie treści z ekranu?"</string> <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Przesyłanie obrazu z 1 aplikacji"</string> @@ -980,6 +986,8 @@ <string name="drag_to_remove_tiles" msgid="4682194717573850385">"Przeciągnij tutaj, by usunąć"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"Minimalna liczba kafelków to <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g>"</string> <string name="qs_edit" msgid="5583565172803472437">"Edytuj"</string> + <!-- no translation found for qs_edit_tiles (2105215324060865035) --> + <skip /> <string name="tuner_time" msgid="2450785840990529997">"Godzina"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"Pokazuj godziny, minuty i sekundy"</item> diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml index 40b85cc0da14..d9bbaf064da1 100644 --- a/packages/SystemUI/res/values-pt-rBR/strings.xml +++ b/packages/SystemUI/res/values-pt-rBR/strings.xml @@ -419,6 +419,10 @@ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Clique para parear o novo dispositivo"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Não foi possível atualizar a predefinição"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Predefinição"</string> + <!-- no translation found for hearing_devices_input_routing_label (730396728151232306) --> + <skip /> + <!-- no translation found for hearing_device_input_routing_options:0 (4582190415045337003) --> + <!-- no translation found for hearing_device_input_routing_options:1 (8501466270452446450) --> <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Selecionado"</string> <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Som ambiente"</string> <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Lado esquerdo"</string> @@ -582,6 +586,8 @@ <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Quando você compartilha um aplicativo, todas as informações mostradas ou abertas nele ficam visíveis para o app <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Tenha cuidado com senhas, detalhes de pagamento, mensagens, fotos, áudios e vídeos."</string> <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Compartilhar tela"</string> <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> desativou essa opção"</string> + <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) --> + <skip /> <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Escolha um app para compartilhar"</string> <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Transmitir a tela?"</string> <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Transmitir um app"</string> @@ -980,6 +986,8 @@ <string name="drag_to_remove_tiles" msgid="4682194717573850385">"Arraste aqui para remover"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"É preciso haver pelo menos <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> blocos"</string> <string name="qs_edit" msgid="5583565172803472437">"Editar"</string> + <!-- no translation found for qs_edit_tiles (2105215324060865035) --> + <skip /> <string name="tuner_time" msgid="2450785840990529997">"Horas"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"Mostrar horas, minutos e segundos"</item> diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml index 934c7176d4a3..913f5b3be8bd 100644 --- a/packages/SystemUI/res/values-pt-rPT/strings.xml +++ b/packages/SystemUI/res/values-pt-rPT/strings.xml @@ -419,6 +419,10 @@ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Clique para sincronizar um novo dispositivo"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Não foi possível atualizar a predefinição"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Predefinição"</string> + <!-- no translation found for hearing_devices_input_routing_label (730396728151232306) --> + <skip /> + <!-- no translation found for hearing_device_input_routing_options:0 (4582190415045337003) --> + <!-- no translation found for hearing_device_input_routing_options:1 (8501466270452446450) --> <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Selecionado"</string> <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Ambiente"</string> <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Esquerda"</string> @@ -582,6 +586,8 @@ <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Quando está a partilhar uma app, tudo o que é mostrado ou reproduzido nessa app é visível para a app <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Por isso, tenha cuidado com, por exemplo, palavras-passe, detalhes de pagamento, mensagens, fotos, áudio e vídeo."</string> <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Partilhar ecrã"</string> <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"A app <xliff:g id="APP_NAME">%1$s</xliff:g> desativou esta opção"</string> + <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) --> + <skip /> <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Escolha uma app para partilhar"</string> <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Transmitir o ecrã?"</string> <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Transmitir uma app"</string> @@ -794,8 +800,7 @@ <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Ativada – Com base no rosto"</string> <string name="inline_done_button" msgid="6043094985588909584">"Concluído"</string> <string name="inline_ok_button" msgid="603075490581280343">"Aplicar"</string> - <!-- no translation found for inline_turn_off_notifications (2653064779176881329) --> - <skip /> + <string name="inline_turn_off_notifications" msgid="2653064779176881329">"Desativar"</string> <string name="notification_silence_title" msgid="8608090968400832335">"Silencioso"</string> <string name="notification_alert_title" msgid="3656229781017543655">"Predefinição"</string> <string name="notification_automatic_title" msgid="3745465364578762652">"Automática"</string> @@ -980,6 +985,8 @@ <string name="drag_to_remove_tiles" msgid="4682194717573850385">"Arrastar para aqui para remover"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"Necessita de, pelo menos, <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> cartões"</string> <string name="qs_edit" msgid="5583565172803472437">"Editar"</string> + <!-- no translation found for qs_edit_tiles (2105215324060865035) --> + <skip /> <string name="tuner_time" msgid="2450785840990529997">"Hora"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"Mostrar horas, minutos e segundos"</item> diff --git a/packages/SystemUI/res/values-pt-rPT/tiles_states_strings.xml b/packages/SystemUI/res/values-pt-rPT/tiles_states_strings.xml index 42a465ea415f..bedcab0eabc4 100644 --- a/packages/SystemUI/res/values-pt-rPT/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-pt-rPT/tiles_states_strings.xml @@ -56,9 +56,11 @@ <item msgid="5376619709702103243">"Desligado"</item> <item msgid="4875147066469902392">"Ligado"</item> </string-array> - <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) --> - <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) --> - <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) --> + <string-array name="tile_states_modes_dnd"> + <item msgid="6509540227356524582">"Indisponível"</item> + <item msgid="8589336868985358191">"Desativado"</item> + <item msgid="726072717827778234">"Ativado"</item> + </string-array> <string-array name="tile_states_flashlight"> <item msgid="3465257127433353857">"Indisponível"</item> <item msgid="5044688398303285224">"Desligada"</item> diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml index 40b85cc0da14..d9bbaf064da1 100644 --- a/packages/SystemUI/res/values-pt/strings.xml +++ b/packages/SystemUI/res/values-pt/strings.xml @@ -419,6 +419,10 @@ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Clique para parear o novo dispositivo"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Não foi possível atualizar a predefinição"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Predefinição"</string> + <!-- no translation found for hearing_devices_input_routing_label (730396728151232306) --> + <skip /> + <!-- no translation found for hearing_device_input_routing_options:0 (4582190415045337003) --> + <!-- no translation found for hearing_device_input_routing_options:1 (8501466270452446450) --> <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Selecionado"</string> <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Som ambiente"</string> <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Lado esquerdo"</string> @@ -582,6 +586,8 @@ <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Quando você compartilha um aplicativo, todas as informações mostradas ou abertas nele ficam visíveis para o app <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Tenha cuidado com senhas, detalhes de pagamento, mensagens, fotos, áudios e vídeos."</string> <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Compartilhar tela"</string> <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"O app <xliff:g id="APP_NAME">%1$s</xliff:g> desativou essa opção"</string> + <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) --> + <skip /> <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Escolha um app para compartilhar"</string> <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Transmitir a tela?"</string> <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Transmitir um app"</string> @@ -980,6 +986,8 @@ <string name="drag_to_remove_tiles" msgid="4682194717573850385">"Arraste aqui para remover"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"É preciso haver pelo menos <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> blocos"</string> <string name="qs_edit" msgid="5583565172803472437">"Editar"</string> + <!-- no translation found for qs_edit_tiles (2105215324060865035) --> + <skip /> <string name="tuner_time" msgid="2450785840990529997">"Horas"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"Mostrar horas, minutos e segundos"</item> diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml index 3f8324e3b7f3..906ee09405c3 100644 --- a/packages/SystemUI/res/values-ro/strings.xml +++ b/packages/SystemUI/res/values-ro/strings.xml @@ -419,6 +419,10 @@ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Dă clic pentru a asocia un nou dispozitiv"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Nu s-a putut actualiza presetarea"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Presetare"</string> + <!-- no translation found for hearing_devices_input_routing_label (730396728151232306) --> + <skip /> + <!-- no translation found for hearing_device_input_routing_options:0 (4582190415045337003) --> + <!-- no translation found for hearing_device_input_routing_options:1 (8501466270452446450) --> <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Selectat"</string> <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Împrejurimi"</string> <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Stânga"</string> @@ -582,6 +586,8 @@ <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Când permiți accesul la o aplicație, orice conținut se afișează sau se redă în aplicație este vizibil pentru <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Prin urmare, ai grijă cu parolele, detaliile de plată, mesajele, fotografiile și conținutul audio și video."</string> <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Permite accesul la ecran"</string> <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> a dezactivat această opțiune"</string> + <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) --> + <skip /> <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Alege aplicația de trimis"</string> <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Proiectezi ecranul?"</string> <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Proiectează o aplicație"</string> @@ -980,6 +986,8 @@ <string name="drag_to_remove_tiles" msgid="4682194717573850385">"Trage aici pentru a elimina"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"Ai nevoie de cel puțin <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> carduri"</string> <string name="qs_edit" msgid="5583565172803472437">"Editează"</string> + <!-- no translation found for qs_edit_tiles (2105215324060865035) --> + <skip /> <string name="tuner_time" msgid="2450785840990529997">"Oră"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"Afișează orele, minutele și secundele"</item> diff --git a/packages/SystemUI/res/values-ro/tiles_states_strings.xml b/packages/SystemUI/res/values-ro/tiles_states_strings.xml index 56460771381d..3f893fab4db2 100644 --- a/packages/SystemUI/res/values-ro/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-ro/tiles_states_strings.xml @@ -56,9 +56,11 @@ <item msgid="5376619709702103243">"Dezactivată"</item> <item msgid="4875147066469902392">"Activată"</item> </string-array> - <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) --> - <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) --> - <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) --> + <string-array name="tile_states_modes_dnd"> + <item msgid="6509540227356524582">"Indisponibil"</item> + <item msgid="8589336868985358191">"Dezactivat"</item> + <item msgid="726072717827778234">"Activat"</item> + </string-array> <string-array name="tile_states_flashlight"> <item msgid="3465257127433353857">"Indisponibilă"</item> <item msgid="5044688398303285224">"Dezactivată"</item> diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml index 450d048da5bf..ee9d755088ed 100644 --- a/packages/SystemUI/res/values-ru/strings.xml +++ b/packages/SystemUI/res/values-ru/strings.xml @@ -419,6 +419,10 @@ <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> + <!-- no translation found for hearing_devices_input_routing_label (730396728151232306) --> + <skip /> + <!-- no translation found for hearing_device_input_routing_options:0 (4582190415045337003) --> + <!-- no translation found for hearing_device_input_routing_options:1 (8501466270452446450) --> <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Выбрано"</string> <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Окружающие звуки"</string> <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Левый"</string> @@ -582,6 +586,8 @@ <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"При показе приложения все, что в нем происходит, будет видно в приложении \"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>\". Поэтому будьте осторожны с паролями, сведениями о способах оплаты, сообщениями, фотографиями, аудио- и видеозаписями."</string> <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Показать экран"</string> <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"Приложение \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" отключило эту возможность"</string> + <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) --> + <skip /> <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Выбор приложения для демонстрации"</string> <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Начать трансляцию экрана?"</string> <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Транслировать одно приложение"</string> @@ -980,6 +986,8 @@ <string name="drag_to_remove_tiles" msgid="4682194717573850385">"Чтобы удалить, перетащите сюда"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"Должно остаться не менее <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> элементов"</string> <string name="qs_edit" msgid="5583565172803472437">"Изменить"</string> + <!-- no translation found for qs_edit_tiles (2105215324060865035) --> + <skip /> <string name="tuner_time" msgid="2450785840990529997">"Время"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"Часы, минуты и секунды"</item> diff --git a/packages/SystemUI/res/values-ru/tiles_states_strings.xml b/packages/SystemUI/res/values-ru/tiles_states_strings.xml index 9ffa7f1f4152..d20f5e218806 100644 --- a/packages/SystemUI/res/values-ru/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-ru/tiles_states_strings.xml @@ -56,9 +56,11 @@ <item msgid="5376619709702103243">"Откл."</item> <item msgid="4875147066469902392">"Вкл."</item> </string-array> - <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) --> - <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) --> - <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) --> + <string-array name="tile_states_modes_dnd"> + <item msgid="6509540227356524582">"Недоступно"</item> + <item msgid="8589336868985358191">"Отключено"</item> + <item msgid="726072717827778234">"Включено"</item> + </string-array> <string-array name="tile_states_flashlight"> <item msgid="3465257127433353857">"Функция недоступна"</item> <item msgid="5044688398303285224">"Откл."</item> diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml index c6326f27c478..583d8842b189 100644 --- a/packages/SystemUI/res/values-si/strings.xml +++ b/packages/SystemUI/res/values-si/strings.xml @@ -419,6 +419,10 @@ <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> + <!-- no translation found for hearing_devices_input_routing_label (730396728151232306) --> + <skip /> + <!-- no translation found for hearing_device_input_routing_options:0 (4582190415045337003) --> + <!-- no translation found for hearing_device_input_routing_options:1 (8501466270452446450) --> <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"තෝරන ලදි"</string> <string name="hearing_devices_ambient_label" msgid="629440938614895797">"වටපිටාව"</string> <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"වම"</string> @@ -582,6 +586,8 @@ <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"ඔබ යෙදුමක් බෙදා ගන්නා විට, එම යෙදුමේ පෙන්වන හෝ වාදනය කරන ඕනෑම දෙයක් <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> වෙත දෘශ්යමාන වේ. ඒ නිසා මුරපද, ගෙවීම් විස්තර, පණිවිඩ, ඡායාරූප, සහ ශ්රව්ය සහ දෘශ්ය වැනි දේවල් පිළිබඳ ප්රවේශම් වන්න."</string> <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"තිරය බෙදා ගන්න"</string> <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> මෙම විකල්පය අබල කර ඇත"</string> + <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) --> + <skip /> <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"බෙදා ගැනීමට යෙදුම තෝරන්න"</string> <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"ඔබේ තිරය විකාශය කරන්න ද?"</string> <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"එක් යෙදුමක් විකාශය කරන්න"</string> @@ -980,6 +986,8 @@ <string name="drag_to_remove_tiles" msgid="4682194717573850385">"ඉවත් කිරීමට මෙතැනට අදින්න"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"ඔබ අවම වශයෙන් ටයිල් <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> ක් අවශ්ය වෙයි"</string> <string name="qs_edit" msgid="5583565172803472437">"සංස්කරණය"</string> + <!-- no translation found for qs_edit_tiles (2105215324060865035) --> + <skip /> <string name="tuner_time" msgid="2450785840990529997">"වේලාව"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"පැය, මිනිත්තු, සහ තත්පර පෙන්වන්න"</item> diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml index fe1dbbd926d1..206e10c6245d 100644 --- a/packages/SystemUI/res/values-sk/strings.xml +++ b/packages/SystemUI/res/values-sk/strings.xml @@ -419,6 +419,10 @@ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kliknutím spárujete nové zariadenie"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Predvoľbu sa nepodarilo aktualizovať"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Predvoľba"</string> + <!-- no translation found for hearing_devices_input_routing_label (730396728151232306) --> + <skip /> + <!-- no translation found for hearing_device_input_routing_options:0 (4582190415045337003) --> + <!-- no translation found for hearing_device_input_routing_options:1 (8501466270452446450) --> <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Vybrané"</string> <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Okolie"</string> <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Vľavo"</string> @@ -582,6 +586,8 @@ <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Pri zdieľaní aplikácie vidí aplikácia <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> všetko, čo sa v zdieľanej aplikácii zobrazuje alebo prehráva. Preto zvýšte pozornosť v prípade položiek, ako sú heslá, platobné údaje, správy, fotky a zvuk či video."</string> <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Zdieľať obrazovku"</string> <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"Aplikácia <xliff:g id="APP_NAME">%1$s</xliff:g> túto možnosť zakázala"</string> + <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) --> + <skip /> <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Vyberte aplikáciu, do ktorej chcete zdieľať"</string> <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Chcete prenášať obrazovku?"</string> <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Prenášať jednu aplikáciu"</string> @@ -980,6 +986,8 @@ <string name="drag_to_remove_tiles" msgid="4682194717573850385">"Presunutím sem odstránite"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"Minimálny počet vyžadovaných dlaždíc: <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g>"</string> <string name="qs_edit" msgid="5583565172803472437">"Upraviť"</string> + <!-- no translation found for qs_edit_tiles (2105215324060865035) --> + <skip /> <string name="tuner_time" msgid="2450785840990529997">"Čas"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"Zobrazovať hodiny, minúty a sekundy"</item> diff --git a/packages/SystemUI/res/values-sk/tiles_states_strings.xml b/packages/SystemUI/res/values-sk/tiles_states_strings.xml index 5a4ce99bf63e..1b82be8eab8e 100644 --- a/packages/SystemUI/res/values-sk/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-sk/tiles_states_strings.xml @@ -56,9 +56,11 @@ <item msgid="5376619709702103243">"Vypnuté"</item> <item msgid="4875147066469902392">"Zapnuté"</item> </string-array> - <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) --> - <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) --> - <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) --> + <string-array name="tile_states_modes_dnd"> + <item msgid="6509540227356524582">"Nedostupné"</item> + <item msgid="8589336868985358191">"Vypnutý"</item> + <item msgid="726072717827778234">"Zapnutý"</item> + </string-array> <string-array name="tile_states_flashlight"> <item msgid="3465257127433353857">"Nie je k dispozícii"</item> <item msgid="5044688398303285224">"Vypnuté"</item> diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml index 22d21bc47731..1c1ff03c3dcf 100644 --- a/packages/SystemUI/res/values-sl/strings.xml +++ b/packages/SystemUI/res/values-sl/strings.xml @@ -419,6 +419,10 @@ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kliknite za seznanitev nove naprave"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Prednastavljenih vrednosti ni bilo mogoče posodobiti"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Prednastavljeno"</string> + <!-- no translation found for hearing_devices_input_routing_label (730396728151232306) --> + <skip /> + <!-- no translation found for hearing_device_input_routing_options:0 (4582190415045337003) --> + <!-- no translation found for hearing_device_input_routing_options:1 (8501466270452446450) --> <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Izbrano"</string> <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Okolica"</string> <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Levo"</string> @@ -582,6 +586,8 @@ <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Pri deljenju aplikacije je aplikaciji <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> vidno vse, kar je prikazano ali predvajano v tej aplikaciji. Zato bodite previdni z gesli, podatki za plačilo, sporočili, fotografijami ter z zvokom in videom."</string> <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Deli zaslon"</string> <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"Aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> je onemogočila to možnost"</string> + <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) --> + <skip /> <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Izbira aplikacije za deljenje"</string> <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Želite predvajati vsebino zaslona?"</string> <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Predvajanje ene aplikacije"</string> @@ -794,8 +800,7 @@ <string name="rotation_lock_camera_rotation_on" msgid="789434807790534274">"Vklopljeno – na podlagi obraza"</string> <string name="inline_done_button" msgid="6043094985588909584">"Končano"</string> <string name="inline_ok_button" msgid="603075490581280343">"Uporabi"</string> - <!-- no translation found for inline_turn_off_notifications (2653064779176881329) --> - <skip /> + <string name="inline_turn_off_notifications" msgid="2653064779176881329">"Izklopi"</string> <string name="notification_silence_title" msgid="8608090968400832335">"Tiho"</string> <string name="notification_alert_title" msgid="3656229781017543655">"Privzeto"</string> <string name="notification_automatic_title" msgid="3745465364578762652">"Samodejno"</string> @@ -980,6 +985,8 @@ <string name="drag_to_remove_tiles" msgid="4682194717573850385">"Če želite odstraniti, povlecite sem"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"Imeti morate vsaj toliko ploščic: <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g>"</string> <string name="qs_edit" msgid="5583565172803472437">"Uredi"</string> + <!-- no translation found for qs_edit_tiles (2105215324060865035) --> + <skip /> <string name="tuner_time" msgid="2450785840990529997">"Ura"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"Prikaži ure, minute in sekunde"</item> diff --git a/packages/SystemUI/res/values-sl/tiles_states_strings.xml b/packages/SystemUI/res/values-sl/tiles_states_strings.xml index 8f243dedef9c..e0db4f1d262a 100644 --- a/packages/SystemUI/res/values-sl/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-sl/tiles_states_strings.xml @@ -56,9 +56,11 @@ <item msgid="5376619709702103243">"Izklopljeno"</item> <item msgid="4875147066469902392">"Vklopljeno"</item> </string-array> - <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) --> - <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) --> - <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) --> + <string-array name="tile_states_modes_dnd"> + <item msgid="6509540227356524582">"Ni na voljo"</item> + <item msgid="8589336868985358191">"Izklopljeno"</item> + <item msgid="726072717827778234">"Vklopljeno"</item> + </string-array> <string-array name="tile_states_flashlight"> <item msgid="3465257127433353857">"Ni na voljo"</item> <item msgid="5044688398303285224">"Izklopljeno"</item> diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml index 203b9faa6730..c68e7a81a02c 100644 --- a/packages/SystemUI/res/values-sq/strings.xml +++ b/packages/SystemUI/res/values-sq/strings.xml @@ -419,6 +419,10 @@ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kliko për të çiftuar një pajisje të re"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Paravendosja nuk mund të përditësohej"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Paravendosja"</string> + <!-- no translation found for hearing_devices_input_routing_label (730396728151232306) --> + <skip /> + <!-- no translation found for hearing_device_input_routing_options:0 (4582190415045337003) --> + <!-- no translation found for hearing_device_input_routing_options:1 (8501466270452446450) --> <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Zgjedhur"</string> <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Ambienti rrethues"</string> <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Majtas"</string> @@ -582,6 +586,8 @@ <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Kur ti ndan një aplikacion, çdo gjë që shfaqet ose luhet në atë aplikacion është e dukshme për <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Prandaj, ki kujdes me gjërat si fjalëkalimet, detajet e pagesave, mesazhet, fotografitë, si dhe audion dhe videon."</string> <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Ndaj ekranin"</string> <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> e ka çaktivizuar këtë opsion"</string> + <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) --> + <skip /> <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Zgjidh aplikacionin për të ndarë"</string> <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Të transmetohet ekrani yt?"</string> <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Transmeto një aplikacion"</string> @@ -980,6 +986,8 @@ <string name="drag_to_remove_tiles" msgid="4682194717573850385">"Zvarrit këtu për ta hequr"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"Të duhen të paktën <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> pllakëza"</string> <string name="qs_edit" msgid="5583565172803472437">"Redakto"</string> + <!-- no translation found for qs_edit_tiles (2105215324060865035) --> + <skip /> <string name="tuner_time" msgid="2450785840990529997">"Ora"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"Shfaq orët, minutat dhe sekondat"</item> diff --git a/packages/SystemUI/res/values-sq/tiles_states_strings.xml b/packages/SystemUI/res/values-sq/tiles_states_strings.xml index ac1409990bcd..9bbb0f941ab1 100644 --- a/packages/SystemUI/res/values-sq/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-sq/tiles_states_strings.xml @@ -56,9 +56,11 @@ <item msgid="5376619709702103243">"Joaktiv"</item> <item msgid="4875147066469902392">"Aktiv"</item> </string-array> - <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) --> - <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) --> - <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) --> + <string-array name="tile_states_modes_dnd"> + <item msgid="6509540227356524582">"Nuk ofrohet"</item> + <item msgid="8589336868985358191">"Çaktivizuar"</item> + <item msgid="726072717827778234">"Aktivizuar"</item> + </string-array> <string-array name="tile_states_flashlight"> <item msgid="3465257127433353857">"Nuk ofrohet"</item> <item msgid="5044688398303285224">"Joaktiv"</item> diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml index 97ca62a420c3..46d83528114f 100644 --- a/packages/SystemUI/res/values-sr/strings.xml +++ b/packages/SystemUI/res/values-sr/strings.xml @@ -419,6 +419,10 @@ <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> + <!-- no translation found for hearing_devices_input_routing_label (730396728151232306) --> + <skip /> + <!-- no translation found for hearing_device_input_routing_options:0 (4582190415045337003) --> + <!-- no translation found for hearing_device_input_routing_options:1 (8501466270452446450) --> <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Изабрано"</string> <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Окружење"</string> <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Лево"</string> @@ -582,6 +586,8 @@ <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Када делите апликацију, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> види сав садржај који се приказује или пушта у њој. Зато пазите на лозинке, информације о плаћању, поруке, слике, аудио и видео садржај."</string> <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Дели екран"</string> <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"Апликација <xliff:g id="APP_NAME">%1$s</xliff:g> је онемогућила ову опцију"</string> + <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) --> + <skip /> <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Одаберите апликацију за дељење"</string> <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Желите да пребаците екран?"</string> <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Пребаци једну апликацију"</string> @@ -980,6 +986,8 @@ <string name="drag_to_remove_tiles" msgid="4682194717573850385">"Превуците овде да бисте уклонили"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"Минималан број плочица је <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g>"</string> <string name="qs_edit" msgid="5583565172803472437">"Измени"</string> + <!-- no translation found for qs_edit_tiles (2105215324060865035) --> + <skip /> <string name="tuner_time" msgid="2450785840990529997">"Време"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"Прикажи сате, минуте и секунде"</item> diff --git a/packages/SystemUI/res/values-sr/tiles_states_strings.xml b/packages/SystemUI/res/values-sr/tiles_states_strings.xml index e7e34aea2bee..14a8d94d2839 100644 --- a/packages/SystemUI/res/values-sr/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-sr/tiles_states_strings.xml @@ -56,9 +56,11 @@ <item msgid="5376619709702103243">"Искључено"</item> <item msgid="4875147066469902392">"Укључено"</item> </string-array> - <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) --> - <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) --> - <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) --> + <string-array name="tile_states_modes_dnd"> + <item msgid="6509540227356524582">"Недоступно"</item> + <item msgid="8589336868985358191">"Искључено"</item> + <item msgid="726072717827778234">"Укључено"</item> + </string-array> <string-array name="tile_states_flashlight"> <item msgid="3465257127433353857">"Недоступно"</item> <item msgid="5044688398303285224">"Искључено"</item> diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml index 4cdc37d72176..ed0daf9da04b 100644 --- a/packages/SystemUI/res/values-sv/strings.xml +++ b/packages/SystemUI/res/values-sv/strings.xml @@ -419,6 +419,10 @@ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Klicka för att parkoppla en ny enhet"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Det gick inte att uppdatera förinställningen"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Förinställning"</string> + <!-- no translation found for hearing_devices_input_routing_label (730396728151232306) --> + <skip /> + <!-- no translation found for hearing_device_input_routing_options:0 (4582190415045337003) --> + <!-- no translation found for hearing_device_input_routing_options:1 (8501466270452446450) --> <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Markerad"</string> <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Omgivningsläge"</string> <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Vänster"</string> @@ -582,6 +586,8 @@ <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"När du delar en app är allt som visas eller spelas upp i appen synligt för <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Var försiktig med sådant som lösenord, betalningsuppgifter, meddelanden, foton, ljud och video."</string> <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Dela skärmen"</string> <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> har inaktiverat alternativet"</string> + <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) --> + <skip /> <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Välj en app att dela"</string> <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Vill du casta skärmen?"</string> <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Casta en app"</string> @@ -980,6 +986,8 @@ <string name="drag_to_remove_tiles" msgid="4682194717573850385">"Ta bort genom att dra här"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"Minst <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> rutor måste finnas kvar"</string> <string name="qs_edit" msgid="5583565172803472437">"Redigera"</string> + <!-- no translation found for qs_edit_tiles (2105215324060865035) --> + <skip /> <string name="tuner_time" msgid="2450785840990529997">"Tid"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"Visa timmar, minuter och sekunder"</item> diff --git a/packages/SystemUI/res/values-sv/tiles_states_strings.xml b/packages/SystemUI/res/values-sv/tiles_states_strings.xml index 7ec70eac7adc..f042697bd74d 100644 --- a/packages/SystemUI/res/values-sv/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-sv/tiles_states_strings.xml @@ -56,9 +56,11 @@ <item msgid="5376619709702103243">"Av"</item> <item msgid="4875147066469902392">"På"</item> </string-array> - <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) --> - <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) --> - <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) --> + <string-array name="tile_states_modes_dnd"> + <item msgid="6509540227356524582">"Inte tillgängligt"</item> + <item msgid="8589336868985358191">"Av"</item> + <item msgid="726072717827778234">"På"</item> + </string-array> <string-array name="tile_states_flashlight"> <item msgid="3465257127433353857">"Inte tillgängligt"</item> <item msgid="5044688398303285224">"Av"</item> diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml index 1cb61fdbab01..da390fb459ac 100644 --- a/packages/SystemUI/res/values-sw/strings.xml +++ b/packages/SystemUI/res/values-sw/strings.xml @@ -419,6 +419,10 @@ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Bofya ili uunganishe kifaa kipya"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Imeshindwa kusasisha mipangilio iliyowekwa mapema"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Mipangilio iliyowekwa mapema"</string> + <!-- no translation found for hearing_devices_input_routing_label (730396728151232306) --> + <skip /> + <!-- no translation found for hearing_device_input_routing_options:0 (4582190415045337003) --> + <!-- no translation found for hearing_device_input_routing_options:1 (8501466270452446450) --> <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Umechagua"</string> <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Mazingira"</string> <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Kushoto"</string> @@ -582,6 +586,8 @@ <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Unaporuhusu ufikiaji wa programu, chochote kinachoonyeshwa au kuchezwa katika programu hiyo kitaonekana kwa <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Kwa hivyo kuwa mwangalifu na vitu kama vile manenosiri, maelezo ya malipo, ujumbe, picha, sauti na video."</string> <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Ruhusu ufikiaji wa skrini"</string> <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> imezima chaguo hili"</string> + <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) --> + <skip /> <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Chagua programu utakayoruhusu ifikiwe"</string> <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Ungependa kutuma maudhui yaliyo katika skrini yako?"</string> <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Tuma maudhui ya programu moja"</string> @@ -980,6 +986,8 @@ <string name="drag_to_remove_tiles" msgid="4682194717573850385">"Buruta hapa ili uondoe"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"Unahitaji angalau vigae <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g>"</string> <string name="qs_edit" msgid="5583565172803472437">"Badilisha"</string> + <!-- no translation found for qs_edit_tiles (2105215324060865035) --> + <skip /> <string name="tuner_time" msgid="2450785840990529997">"Wakati"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"Onyesha saa, dakika na sekunde"</item> diff --git a/packages/SystemUI/res/values-sw/tiles_states_strings.xml b/packages/SystemUI/res/values-sw/tiles_states_strings.xml index 9e262819f560..e08ed3a107da 100644 --- a/packages/SystemUI/res/values-sw/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-sw/tiles_states_strings.xml @@ -56,9 +56,11 @@ <item msgid="5376619709702103243">"Kimezimwa"</item> <item msgid="4875147066469902392">"Kimewashwa"</item> </string-array> - <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) --> - <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) --> - <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) --> + <string-array name="tile_states_modes_dnd"> + <item msgid="6509540227356524582">"Haipatikani"</item> + <item msgid="8589336868985358191">"Imezimwa"</item> + <item msgid="726072717827778234">"Imewashwa"</item> + </string-array> <string-array name="tile_states_flashlight"> <item msgid="3465257127433353857">"Hakipatikani"</item> <item msgid="5044688398303285224">"Imezimwa"</item> diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml index 3109a13290af..0c7aa40868cb 100644 --- a/packages/SystemUI/res/values-ta/strings.xml +++ b/packages/SystemUI/res/values-ta/strings.xml @@ -419,6 +419,10 @@ <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> + <!-- no translation found for hearing_devices_input_routing_label (730396728151232306) --> + <skip /> + <!-- no translation found for hearing_device_input_routing_options:0 (4582190415045337003) --> + <!-- no translation found for hearing_device_input_routing_options:1 (8501466270452446450) --> <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"தேர்ந்தெடுக்கப்பட்டது"</string> <string name="hearing_devices_ambient_label" msgid="629440938614895797">"சுற்றுப்புறங்கள்"</string> <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"இடது"</string> @@ -582,6 +586,8 @@ <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"ஓர் ஆப்ஸைப் பகிரும்போது, அதில் காட்டப்படும்/பிளே செய்யப்படும் அனைத்தும் <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> இல் தெரியும். எனவே கடவுச்சொற்கள், பேமெண்ட் விவரங்கள், மெசேஜ்கள், படங்கள், ஆடியோ, வீடியோ போன்றவை குறித்துக் கவனத்துடன் இருங்கள்."</string> <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"திரையைப் பகிர்"</string> <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> ஆப்ஸ் இந்த விருப்பத்தை முடக்கியுள்ளது"</string> + <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) --> + <skip /> <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"பகிர ஆப்ஸைத் தேர்வுசெய்க"</string> <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"உங்கள் திரையை அலைபரப்ப வேண்டுமா?"</string> <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"ஓர் ஆப்ஸை அலைபரப்பு"</string> @@ -980,6 +986,8 @@ <string name="drag_to_remove_tiles" msgid="4682194717573850385">"அகற்ற, இங்கே இழுக்கவும்"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"குறைந்தது <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> கட்டங்கள் தேவை"</string> <string name="qs_edit" msgid="5583565172803472437">"மாற்று"</string> + <!-- no translation found for qs_edit_tiles (2105215324060865035) --> + <skip /> <string name="tuner_time" msgid="2450785840990529997">"நேரம்"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"மணிநேரம், நிமிடங்கள், வினாடிகளைக் காட்டு"</item> diff --git a/packages/SystemUI/res/values-ta/tiles_states_strings.xml b/packages/SystemUI/res/values-ta/tiles_states_strings.xml index d3773a761c37..8280da4340e9 100644 --- a/packages/SystemUI/res/values-ta/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-ta/tiles_states_strings.xml @@ -56,9 +56,11 @@ <item msgid="5376619709702103243">"முடக்கப்பட்டுள்ளது"</item> <item msgid="4875147066469902392">"இயக்கப்பட்டுள்ளது"</item> </string-array> - <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) --> - <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) --> - <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) --> + <string-array name="tile_states_modes_dnd"> + <item msgid="6509540227356524582">"கிடைக்கவில்லை"</item> + <item msgid="8589336868985358191">"ஆஃப்"</item> + <item msgid="726072717827778234">"ஆன்"</item> + </string-array> <string-array name="tile_states_flashlight"> <item msgid="3465257127433353857">"கிடைக்கவில்லை"</item> <item msgid="5044688398303285224">"முடக்கப்பட்டுள்ளது"</item> diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml index 1bf3ab2e913f..972c64ab7f26 100644 --- a/packages/SystemUI/res/values-te/strings.xml +++ b/packages/SystemUI/res/values-te/strings.xml @@ -419,6 +419,10 @@ <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> + <!-- no translation found for hearing_devices_input_routing_label (730396728151232306) --> + <skip /> + <!-- no translation found for hearing_device_input_routing_options:0 (4582190415045337003) --> + <!-- no translation found for hearing_device_input_routing_options:1 (8501466270452446450) --> <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"ఎంచుకోబడింది"</string> <string name="hearing_devices_ambient_label" msgid="629440938614895797">"పరిసరాలు"</string> <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"ఎడమ వైపునకు"</string> @@ -582,6 +586,8 @@ <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"మీరు యాప్ను షేర్ చేసేటప్పుడు, సంబంధిత యాప్లో కనిపించేవి లేదా ప్లే అయ్యేవన్నీ <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>కు కనిపిస్తాయి. కాబట్టి పాస్వర్డ్లు, పేమెంట్ వివరాలు, మెసేజ్లు, ఫోటోలు, ఆడియో, ఇంకా వీడియో వంటి విషయాల్లో జాగ్రత్త వహించండి."</string> <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"స్క్రీన్ను షేర్ చేయండి"</string> <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> ఈ ఆప్షన్ను డిజేబుల్ చేసింది"</string> + <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) --> + <skip /> <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"షేర్ చేయడానికి యాప్ను ఎంచుకోండి"</string> <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"మీ స్క్రీన్ను ప్రసారం చేయాలా?"</string> <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"ఒక యాప్ను ప్రసారం చేయండి"</string> @@ -980,6 +986,8 @@ <string name="drag_to_remove_tiles" msgid="4682194717573850385">"తీసివేయడానికి ఇక్కడికి లాగండి"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"మీ వద్ద కనీసం <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> టైల్లు ఉండాలి"</string> <string name="qs_edit" msgid="5583565172803472437">"ఎడిట్ చేయండి"</string> + <!-- no translation found for qs_edit_tiles (2105215324060865035) --> + <skip /> <string name="tuner_time" msgid="2450785840990529997">"సమయం"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"గంటలు, నిమిషాలు మరియు సెకన్లను చూపు"</item> diff --git a/packages/SystemUI/res/values-te/tiles_states_strings.xml b/packages/SystemUI/res/values-te/tiles_states_strings.xml index 72343a33a082..60a91f82ca9b 100644 --- a/packages/SystemUI/res/values-te/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-te/tiles_states_strings.xml @@ -56,9 +56,11 @@ <item msgid="5376619709702103243">"ఆఫ్లో ఉంది"</item> <item msgid="4875147066469902392">"ఆన్లో ఉంది"</item> </string-array> - <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) --> - <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) --> - <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) --> + <string-array name="tile_states_modes_dnd"> + <item msgid="6509540227356524582">"అందుబాటులో లేదు"</item> + <item msgid="8589336868985358191">"ఆఫ్లో ఉంది"</item> + <item msgid="726072717827778234">"ఆన్లో ఉంది"</item> + </string-array> <string-array name="tile_states_flashlight"> <item msgid="3465257127433353857">"అందుబాటులో లేదు"</item> <item msgid="5044688398303285224">"ఆఫ్లో ఉంది"</item> diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml index 99aca811b873..a4f14473e004 100644 --- a/packages/SystemUI/res/values-th/strings.xml +++ b/packages/SystemUI/res/values-th/strings.xml @@ -419,6 +419,10 @@ <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> + <!-- no translation found for hearing_devices_input_routing_label (730396728151232306) --> + <skip /> + <!-- no translation found for hearing_device_input_routing_options:0 (4582190415045337003) --> + <!-- no translation found for hearing_device_input_routing_options:1 (8501466270452446450) --> <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"เลือกแล้ว"</string> <string name="hearing_devices_ambient_label" msgid="629440938614895797">"เสียงแวดล้อม"</string> <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"ซ้าย"</string> @@ -582,6 +586,8 @@ <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"เมื่อกำลังแชร์แอป <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> จะมองเห็นทุกสิ่งที่แสดงหรือเล่นอยู่ในแอปดังกล่าว ดังนั้นโปรดระวังสิ่งต่างๆ อย่างเช่นรหัสผ่าน รายละเอียดการชำระเงิน ข้อความ รูปภาพ รวมถึงเสียงและวิดีโอ"</string> <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"แชร์หน้าจอ"</string> <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> ปิดใช้ตัวเลือกนี้"</string> + <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) --> + <skip /> <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"เลือกแอปที่จะแชร์"</string> <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"แคสต์หน้าจอของคุณไหม"</string> <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"แคสต์แอปเดียว"</string> @@ -980,6 +986,8 @@ <string name="drag_to_remove_tiles" msgid="4682194717573850385">"ลากมาที่นี่เพื่อนำออก"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"คุณต้องมีการ์ดอย่างน้อย <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> รายการ"</string> <string name="qs_edit" msgid="5583565172803472437">"แก้ไข"</string> + <!-- no translation found for qs_edit_tiles (2105215324060865035) --> + <skip /> <string name="tuner_time" msgid="2450785840990529997">"เวลา"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"แสดงชั่วโมง นาที และวินาที"</item> diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml index 726cf21e84e4..1f251758db94 100644 --- a/packages/SystemUI/res/values-tl/strings.xml +++ b/packages/SystemUI/res/values-tl/strings.xml @@ -419,6 +419,10 @@ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"I-click para magpares ng bagong device"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Hindi ma-update ang preset"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Preset"</string> + <!-- no translation found for hearing_devices_input_routing_label (730396728151232306) --> + <skip /> + <!-- no translation found for hearing_device_input_routing_options:0 (4582190415045337003) --> + <!-- no translation found for hearing_device_input_routing_options:1 (8501466270452446450) --> <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Napili"</string> <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Paligid"</string> <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Kaliwa"</string> @@ -582,6 +586,8 @@ <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Kapag nagshe-share ka ng app, makikita ng <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> ang kahit anong ipinapakita o pine-play sa app na iyon. Kaya mag-ingat sa mga bagay-bagay tulad ng mga password, detalye ng pagbabayad, mensahe, larawan, at audio at video."</string> <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Ibahagi ang screen"</string> <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"Na-disable ng <xliff:g id="APP_NAME">%1$s</xliff:g> ang opsyong ito"</string> + <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) --> + <skip /> <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Pumili ng app na ishe-share"</string> <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"I-cast ang iyong screen?"</string> <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Mag-cast ng isang app"</string> @@ -980,6 +986,8 @@ <string name="drag_to_remove_tiles" msgid="4682194717573850385">"I-drag dito upang alisin"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"Kailangan mo ng kahit <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> (na) tile"</string> <string name="qs_edit" msgid="5583565172803472437">"I-edit"</string> + <!-- no translation found for qs_edit_tiles (2105215324060865035) --> + <skip /> <string name="tuner_time" msgid="2450785840990529997">"Oras"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"Ipakita ang oras, minuto at segundo"</item> diff --git a/packages/SystemUI/res/values-tl/tiles_states_strings.xml b/packages/SystemUI/res/values-tl/tiles_states_strings.xml index 92a122dcf437..84213d5a51c3 100644 --- a/packages/SystemUI/res/values-tl/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-tl/tiles_states_strings.xml @@ -56,9 +56,11 @@ <item msgid="5376619709702103243">"Naka-off"</item> <item msgid="4875147066469902392">"Naka-on"</item> </string-array> - <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) --> - <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) --> - <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) --> + <string-array name="tile_states_modes_dnd"> + <item msgid="6509540227356524582">"Hindi available"</item> + <item msgid="8589336868985358191">"Naka-off"</item> + <item msgid="726072717827778234">"Naka-on"</item> + </string-array> <string-array name="tile_states_flashlight"> <item msgid="3465257127433353857">"Hindi available"</item> <item msgid="5044688398303285224">"Naka-off"</item> diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml index a68bc74df470..4748cc4dad54 100644 --- a/packages/SystemUI/res/values-tr/strings.xml +++ b/packages/SystemUI/res/values-tr/strings.xml @@ -419,6 +419,10 @@ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Yeni cihaz eşlemek için tıklayın"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Hazır ayar güncellenemedi"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Hazır Ayar"</string> + <!-- no translation found for hearing_devices_input_routing_label (730396728151232306) --> + <skip /> + <!-- no translation found for hearing_device_input_routing_options:0 (4582190415045337003) --> + <!-- no translation found for hearing_device_input_routing_options:1 (8501466270452446450) --> <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Seçili"</string> <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Çevredeki sesler"</string> <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Sol"</string> @@ -582,6 +586,8 @@ <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>, bir uygulamayı paylaştığınızda o uygulamada gösterilen veya oynatılan her şeyi görebilir. Bu nedenle şifre, ödeme ayrıntıları, mesaj, fotoğraf, ses ve video gibi öğeler konusunda dikkatli olun."</string> <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Ekranı paylaş"</string> <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> bu seçeneği devre dışı bıraktı"</string> + <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) --> + <skip /> <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Paylaşılacak uygulamayı seçin"</string> <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Ekranınız yayınlansın mı?"</string> <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"1 uygulamayı yayınla"</string> @@ -980,6 +986,8 @@ <string name="drag_to_remove_tiles" msgid="4682194717573850385">"Kaldırmak için buraya sürükleyin"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"En az <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> kutu gerekiyor"</string> <string name="qs_edit" msgid="5583565172803472437">"Düzenle"</string> + <!-- no translation found for qs_edit_tiles (2105215324060865035) --> + <skip /> <string name="tuner_time" msgid="2450785840990529997">"Saat"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"Saati, dakikayı ve saniyeyi göster"</item> diff --git a/packages/SystemUI/res/values-tr/tiles_states_strings.xml b/packages/SystemUI/res/values-tr/tiles_states_strings.xml index 986981f21237..3efa8ea96fb3 100644 --- a/packages/SystemUI/res/values-tr/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-tr/tiles_states_strings.xml @@ -56,9 +56,11 @@ <item msgid="5376619709702103243">"Kapalı"</item> <item msgid="4875147066469902392">"Açık"</item> </string-array> - <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) --> - <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) --> - <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) --> + <string-array name="tile_states_modes_dnd"> + <item msgid="6509540227356524582">"Kullanılamıyor"</item> + <item msgid="8589336868985358191">"Kapalı"</item> + <item msgid="726072717827778234">"Açık"</item> + </string-array> <string-array name="tile_states_flashlight"> <item msgid="3465257127433353857">"Kullanılamıyor"</item> <item msgid="5044688398303285224">"Kapalı"</item> diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml index 2e0229393897..d89467cf5e23 100644 --- a/packages/SystemUI/res/values-uk/strings.xml +++ b/packages/SystemUI/res/values-uk/strings.xml @@ -419,6 +419,10 @@ <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> + <!-- no translation found for hearing_devices_input_routing_label (730396728151232306) --> + <skip /> + <!-- no translation found for hearing_device_input_routing_options:0 (4582190415045337003) --> + <!-- no translation found for hearing_device_input_routing_options:1 (8501466270452446450) --> <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Вибрано"</string> <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Звуки оточення"</string> <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Ліворуч"</string> @@ -582,6 +586,8 @@ <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Коли ви показуєте вікно додатка, увесь контент, що відображається або відтворюється в ньому, стає видимим у додатку <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Тому будьте обережні з паролями, повідомленнями, фотографіями, аудіо, відео, платіжною інформацією тощо."</string> <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Показати екран"</string> <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"Додаток <xliff:g id="APP_NAME">%1$s</xliff:g> вимкнув цю опцію"</string> + <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) --> + <skip /> <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Виберіть додаток для показу"</string> <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Транслювати екран?"</string> <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Транслювати один додаток"</string> @@ -980,6 +986,8 @@ <string name="drag_to_remove_tiles" msgid="4682194717573850385">"Перетягніть сюди, щоб видалити"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"Мінімальна кількість фрагментів: <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g>"</string> <string name="qs_edit" msgid="5583565172803472437">"Редагувати"</string> + <!-- no translation found for qs_edit_tiles (2105215324060865035) --> + <skip /> <string name="tuner_time" msgid="2450785840990529997">"Час"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"Показувати години, хвилини та секунди"</item> diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml index c5a96096a86b..51950e099640 100644 --- a/packages/SystemUI/res/values-ur/strings.xml +++ b/packages/SystemUI/res/values-ur/strings.xml @@ -419,6 +419,10 @@ <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> + <!-- no translation found for hearing_devices_input_routing_label (730396728151232306) --> + <skip /> + <!-- no translation found for hearing_device_input_routing_options:0 (4582190415045337003) --> + <!-- no translation found for hearing_device_input_routing_options:1 (8501466270452446450) --> <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"منتخب کردہ"</string> <string name="hearing_devices_ambient_label" msgid="629440938614895797">"اطراف"</string> <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"دائیں"</string> @@ -582,6 +586,8 @@ <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"آپ کے کسی ایپ کا اشتراک کرنے پر اس ایپ میں دکھائی گئی یا چلائی گئی ہر چیز <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> کیلئے مرئی ہو جاتی ہے۔ لہذا، پاس ورڈز، ادائیگی کی تفصیلات، پیغامات، تصاویر، ساتھ ہی آڈیو اور ویڈیو جیسی چیزوں کے سلسلے میں محتاط رہیں۔"</string> <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"اسکرین کا اشتراک کریں"</string> <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> نے اس اختیار کو غیر فعال کر دیا ہے"</string> + <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) --> + <skip /> <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"اشتراک کرنے کیلئے ایپ منتخب کریں"</string> <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"اپنی اسکرین کاسٹ کریں؟"</string> <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"ایک ایپ کاسٹ کریں"</string> @@ -980,6 +986,8 @@ <string name="drag_to_remove_tiles" msgid="4682194717573850385">"ہٹانے کیلئے یہاں گھسیٹیں؟"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"آپ کو کم از کم <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> ٹائلز کی ضرورت ہے"</string> <string name="qs_edit" msgid="5583565172803472437">"ترمیم کریں"</string> + <!-- no translation found for qs_edit_tiles (2105215324060865035) --> + <skip /> <string name="tuner_time" msgid="2450785840990529997">"وقت"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"گھنٹے، منٹ اور سیکنڈ دکھائیں"</item> diff --git a/packages/SystemUI/res/values-ur/tiles_states_strings.xml b/packages/SystemUI/res/values-ur/tiles_states_strings.xml index ea032f1451bd..88698b5c7514 100644 --- a/packages/SystemUI/res/values-ur/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-ur/tiles_states_strings.xml @@ -56,9 +56,11 @@ <item msgid="5376619709702103243">"آف ہے"</item> <item msgid="4875147066469902392">"آن ہے"</item> </string-array> - <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) --> - <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) --> - <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) --> + <string-array name="tile_states_modes_dnd"> + <item msgid="6509540227356524582">"دستیاب نہیں ہے"</item> + <item msgid="8589336868985358191">"آف"</item> + <item msgid="726072717827778234">"آن"</item> + </string-array> <string-array name="tile_states_flashlight"> <item msgid="3465257127433353857">"دستیاب نہیں ہے"</item> <item msgid="5044688398303285224">"آف ہے"</item> diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml index fee0eb9c5473..8d750dd8ad08 100644 --- a/packages/SystemUI/res/values-uz/strings.xml +++ b/packages/SystemUI/res/values-uz/strings.xml @@ -419,6 +419,10 @@ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Yangi qurilmani ulash uchun bosing"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Andoza yangilanmadi"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Andoza"</string> + <!-- no translation found for hearing_devices_input_routing_label (730396728151232306) --> + <skip /> + <!-- no translation found for hearing_device_input_routing_options:0 (4582190415045337003) --> + <!-- no translation found for hearing_device_input_routing_options:1 (8501466270452446450) --> <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Tanlangan"</string> <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Atrof-muhit"</string> <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Chap"</string> @@ -582,6 +586,8 @@ <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Ilovani namoyish qilayotganingizda oʻsha ilova ichida koʻrsatilayotgan yoki ijro qilinayotganlar <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>ga koʻrinadi. Shu sababli parollar, toʻlov tafsilotlari, xabarlar, suratlar, audio va video chiqmasligi uchun ehtiyot boʻling."</string> <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Ekranni namoyish qilish"</string> <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> bu sozlamani faolsizlantirgan"</string> + <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) --> + <skip /> <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Ulashiladigan ilovani tanlash"</string> <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Ekraningiz uzatilsinmi?"</string> <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Bitta ilovani uzatish"</string> @@ -980,6 +986,8 @@ <string name="drag_to_remove_tiles" msgid="4682194717573850385">"O‘chirish uchun bu yerga torting"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"Kamida <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> ta katakcha lozim"</string> <string name="qs_edit" msgid="5583565172803472437">"Tahrirlash"</string> + <!-- no translation found for qs_edit_tiles (2105215324060865035) --> + <skip /> <string name="tuner_time" msgid="2450785840990529997">"Vaqt"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"Soat, daqiqa va soniyalar ko‘rsatilsin"</item> diff --git a/packages/SystemUI/res/values-uz/tiles_states_strings.xml b/packages/SystemUI/res/values-uz/tiles_states_strings.xml index 1433a9b79071..7afb9bdc8034 100644 --- a/packages/SystemUI/res/values-uz/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-uz/tiles_states_strings.xml @@ -56,9 +56,11 @@ <item msgid="5376619709702103243">"Oʻchiq"</item> <item msgid="4875147066469902392">"Yoniq"</item> </string-array> - <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) --> - <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) --> - <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) --> + <string-array name="tile_states_modes_dnd"> + <item msgid="6509540227356524582">"Mavjud emas"</item> + <item msgid="8589336868985358191">"Yoqilmagan"</item> + <item msgid="726072717827778234">"Yoniq"</item> + </string-array> <string-array name="tile_states_flashlight"> <item msgid="3465257127433353857">"Ishlamaydi"</item> <item msgid="5044688398303285224">"Oʻchiq"</item> diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml index c79eef76e6e4..300dea9bb021 100644 --- a/packages/SystemUI/res/values-vi/strings.xml +++ b/packages/SystemUI/res/values-vi/strings.xml @@ -419,6 +419,10 @@ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Nhấp để ghép nối thiết bị mới"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Không cập nhật được giá trị đặt trước"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Chế độ đặt sẵn"</string> + <!-- no translation found for hearing_devices_input_routing_label (730396728151232306) --> + <skip /> + <!-- no translation found for hearing_device_input_routing_options:0 (4582190415045337003) --> + <!-- no translation found for hearing_device_input_routing_options:1 (8501466270452446450) --> <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Đã chọn"</string> <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Âm lượng xung quanh"</string> <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Trái"</string> @@ -582,6 +586,8 @@ <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Khi bạn chia sẻ một ứng dụng, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> sẽ thấy được mọi nội dung hiển thị hoặc phát trong ứng dụng đó. Vì vậy, hãy thận trọng để không làm lộ thông tin như mật khẩu, thông tin thanh toán, tin nhắn, ảnh, âm thanh và video."</string> <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Chia sẻ màn hình"</string> <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> đã tắt lựa chọn này"</string> + <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) --> + <skip /> <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Chọn ứng dụng để chia sẻ"</string> <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Truyền màn hình?"</string> <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Truyền một ứng dụng"</string> @@ -980,6 +986,8 @@ <string name="drag_to_remove_tiles" msgid="4682194717573850385">"Kéo vào đây để xóa"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"Bạn cần ít nhất <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> ô"</string> <string name="qs_edit" msgid="5583565172803472437">"Chỉnh sửa"</string> + <!-- no translation found for qs_edit_tiles (2105215324060865035) --> + <skip /> <string name="tuner_time" msgid="2450785840990529997">"Thời gian"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"Hiển thị giờ, phút và giây"</item> diff --git a/packages/SystemUI/res/values-vi/tiles_states_strings.xml b/packages/SystemUI/res/values-vi/tiles_states_strings.xml index 02d2bb6be45f..78e39a75e117 100644 --- a/packages/SystemUI/res/values-vi/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-vi/tiles_states_strings.xml @@ -56,9 +56,11 @@ <item msgid="5376619709702103243">"Đang tắt"</item> <item msgid="4875147066469902392">"Đang bật"</item> </string-array> - <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) --> - <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) --> - <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) --> + <string-array name="tile_states_modes_dnd"> + <item msgid="6509540227356524582">"Không có"</item> + <item msgid="8589336868985358191">"Đang tắt"</item> + <item msgid="726072717827778234">"Đang bật"</item> + </string-array> <string-array name="tile_states_flashlight"> <item msgid="3465257127433353857">"Không hoạt động"</item> <item msgid="5044688398303285224">"Đang tắt"</item> diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml index f0f55a827930..dacca692fb65 100644 --- a/packages/SystemUI/res/values-zh-rCN/strings.xml +++ b/packages/SystemUI/res/values-zh-rCN/strings.xml @@ -419,6 +419,10 @@ <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> + <!-- no translation found for hearing_devices_input_routing_label (730396728151232306) --> + <skip /> + <!-- no translation found for hearing_device_input_routing_options:0 (4582190415045337003) --> + <!-- no translation found for hearing_device_input_routing_options:1 (8501466270452446450) --> <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"已选择"</string> <string name="hearing_devices_ambient_label" msgid="629440938614895797">"周围声音"</string> <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"左侧"</string> @@ -582,6 +586,8 @@ <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"当您共享一个应用时,该应用中显示或播放的所有内容均对“<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>”可见。因此,请务必小心操作,谨防泄露密码、付款信息、消息、照片、音频、视频等。"</string> <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"共享屏幕"</string> <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"“<xliff:g id="APP_NAME">%1$s</xliff:g>”已停用此选项"</string> + <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) --> + <skip /> <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"选择要分享的应用"</string> <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"投放您的屏幕?"</string> <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"投放单个应用"</string> @@ -980,6 +986,8 @@ <string name="drag_to_remove_tiles" msgid="4682194717573850385">"拖动到此处即可移除"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"您至少需要 <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> 个卡片"</string> <string name="qs_edit" msgid="5583565172803472437">"编辑"</string> + <!-- no translation found for qs_edit_tiles (2105215324060865035) --> + <skip /> <string name="tuner_time" msgid="2450785840990529997">"时间"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"显示小时、分钟和秒"</item> diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml index caac7437b4e0..afc33d9611b9 100644 --- a/packages/SystemUI/res/values-zh-rHK/strings.xml +++ b/packages/SystemUI/res/values-zh-rHK/strings.xml @@ -419,6 +419,10 @@ <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> + <!-- no translation found for hearing_devices_input_routing_label (730396728151232306) --> + <skip /> + <!-- no translation found for hearing_device_input_routing_options:0 (4582190415045337003) --> + <!-- no translation found for hearing_device_input_routing_options:1 (8501466270452446450) --> <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"揀咗"</string> <string name="hearing_devices_ambient_label" msgid="629440938614895797">"環境聲音"</string> <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"左"</string> @@ -582,6 +586,8 @@ <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"當你分享應用程式時,「<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>」可看到該應用程式中顯示或播放的任何內容。因此,請謹慎處理密碼、付款資料、訊息、相片、音訊和影片等。"</string> <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"分享螢幕畫面"</string> <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」已停用此選項"</string> + <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) --> + <skip /> <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"選擇要分享的應用程式"</string> <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"要投放螢幕嗎?"</string> <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"投放一個應用程式"</string> @@ -980,6 +986,8 @@ <string name="drag_to_remove_tiles" msgid="4682194717573850385">"拖曳這裡即可移除"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"你需要有至少 <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> 個資訊方塊"</string> <string name="qs_edit" msgid="5583565172803472437">"編輯"</string> + <!-- no translation found for qs_edit_tiles (2105215324060865035) --> + <skip /> <string name="tuner_time" msgid="2450785840990529997">"時間"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"顯示小時、分鐘和秒"</item> diff --git a/packages/SystemUI/res/values-zh-rHK/tiles_states_strings.xml b/packages/SystemUI/res/values-zh-rHK/tiles_states_strings.xml index 7369d9519b34..2dcb0cd290e1 100644 --- a/packages/SystemUI/res/values-zh-rHK/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-zh-rHK/tiles_states_strings.xml @@ -56,9 +56,11 @@ <item msgid="5376619709702103243">"已關閉"</item> <item msgid="4875147066469902392">"已開啟"</item> </string-array> - <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) --> - <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) --> - <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) --> + <string-array name="tile_states_modes_dnd"> + <item msgid="6509540227356524582">"無法使用"</item> + <item msgid="8589336868985358191">"已關閉"</item> + <item msgid="726072717827778234">"已開啟"</item> + </string-array> <string-array name="tile_states_flashlight"> <item msgid="3465257127433353857">"無法使用"</item> <item msgid="5044688398303285224">"已關閉"</item> diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml index d3a5544ac694..3ea590a17188 100644 --- a/packages/SystemUI/res/values-zh-rTW/strings.xml +++ b/packages/SystemUI/res/values-zh-rTW/strings.xml @@ -419,6 +419,10 @@ <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> + <!-- no translation found for hearing_devices_input_routing_label (730396728151232306) --> + <skip /> + <!-- no translation found for hearing_device_input_routing_options:0 (4582190415045337003) --> + <!-- no translation found for hearing_device_input_routing_options:1 (8501466270452446450) --> <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"已選取"</string> <string name="hearing_devices_ambient_label" msgid="629440938614895797">"環境"</string> <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"左"</string> @@ -582,6 +586,8 @@ <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"當你分享應用程式畫面時,「<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>」可存取該應用程式顯示或播放的所有內容。因此,請謹慎處理密碼、付款資料、訊息、相片和影音內容等資訊。"</string> <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"分享畫面"</string> <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"「<xliff:g id="APP_NAME">%1$s</xliff:g>」已停用此選項"</string> + <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) --> + <skip /> <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"選擇要分享的應用程式"</string> <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"要投放畫面嗎?"</string> <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"投放一個應用程式"</string> @@ -980,6 +986,8 @@ <string name="drag_to_remove_tiles" msgid="4682194717573850385">"拖曳到這裡即可移除"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"你至少必須要有 <xliff:g id="MIN_NUM_TILES">%1$d</xliff:g> 個設定方塊"</string> <string name="qs_edit" msgid="5583565172803472437">"編輯"</string> + <!-- no translation found for qs_edit_tiles (2105215324060865035) --> + <skip /> <string name="tuner_time" msgid="2450785840990529997">"時間"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"顯示小時、分鐘和秒"</item> diff --git a/packages/SystemUI/res/values-zh-rTW/tiles_states_strings.xml b/packages/SystemUI/res/values-zh-rTW/tiles_states_strings.xml index 18a6d12dcff9..33313ac14d53 100644 --- a/packages/SystemUI/res/values-zh-rTW/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-zh-rTW/tiles_states_strings.xml @@ -56,9 +56,11 @@ <item msgid="5376619709702103243">"已關閉"</item> <item msgid="4875147066469902392">"已開啟"</item> </string-array> - <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) --> - <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) --> - <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) --> + <string-array name="tile_states_modes_dnd"> + <item msgid="6509540227356524582">"無法使用"</item> + <item msgid="8589336868985358191">"已關閉"</item> + <item msgid="726072717827778234">"已開啟"</item> + </string-array> <string-array name="tile_states_flashlight"> <item msgid="3465257127433353857">"無法使用"</item> <item msgid="5044688398303285224">"已關閉"</item> diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml index 08657d1bfe54..767adbb25c25 100644 --- a/packages/SystemUI/res/values-zu/strings.xml +++ b/packages/SystemUI/res/values-zu/strings.xml @@ -421,6 +421,10 @@ <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Chofoza ukuze ubhangqe idivayisi entsha"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"Ayikwazanga ukubuyekeza ukusetha ngaphambilini"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"Ukusetha ngaphambilini"</string> + <!-- no translation found for hearing_devices_input_routing_label (730396728151232306) --> + <skip /> + <!-- no translation found for hearing_device_input_routing_options:0 (4582190415045337003) --> + <!-- no translation found for hearing_device_input_routing_options:1 (8501466270452446450) --> <string name="hearing_devices_spinner_item_selected" msgid="3137083889662762383">"Okukhethiwe"</string> <string name="hearing_devices_ambient_label" msgid="629440938614895797">"Izindawo ezizungezile"</string> <string name="hearing_devices_ambient_control_left" msgid="3586965448230412600">"Kwesokunxele"</string> @@ -584,6 +588,8 @@ <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="7094417930857938876">"Uma wabelana nge-app, noma yini eboniswayo noma edlalwayo kuleyo app ibonakala ku-<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g>. Ngakho-ke qaphela ngezinto ezifana namaphasiwedi, imininingwane yenkokhelo, imilayezo, izithombe, nomsindo nevidiyo."</string> <string name="media_projection_entry_app_permission_dialog_continue_entire_screen" msgid="1850848182344377579">"Yabelana ngesikrini"</string> <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> ivale le nketho"</string> + <!-- no translation found for media_projection_entry_app_permission_dialog_single_app_not_supported (4860247304058870233) --> + <skip /> <string name="media_projection_entry_share_app_selector_title" msgid="1419515119767501822">"Khetha i-app yokwabelana"</string> <string name="media_projection_entry_cast_permission_dialog_title" msgid="752756942658159416">"Sakaza isikrini sakho?"</string> <string name="media_projection_entry_cast_permission_dialog_option_text_single_app" msgid="6073353940838561981">"Sakaza i-app eyodwa"</string> @@ -982,6 +988,8 @@ <string name="drag_to_remove_tiles" msgid="4682194717573850385">"Hudulela lapha ukuze ususe"</string> <string name="drag_to_remove_disabled" msgid="933046987838658850">"Udinga okungenani amathayela angu-<xliff:g id="MIN_NUM_TILES">%1$d</xliff:g>"</string> <string name="qs_edit" msgid="5583565172803472437">"Hlela"</string> + <!-- no translation found for qs_edit_tiles (2105215324060865035) --> + <skip /> <string name="tuner_time" msgid="2450785840990529997">"Isikhathi"</string> <string-array name="clock_options"> <item msgid="3986445361435142273">"Bonisa amahora, amaminithi, namasekhondi"</item> diff --git a/packages/SystemUI/res/values-zu/tiles_states_strings.xml b/packages/SystemUI/res/values-zu/tiles_states_strings.xml index 674aeaa3b890..10b26fc31dce 100644 --- a/packages/SystemUI/res/values-zu/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-zu/tiles_states_strings.xml @@ -56,9 +56,11 @@ <item msgid="5376619709702103243">"Valiwe"</item> <item msgid="4875147066469902392">"Vuliwe"</item> </string-array> - <!-- no translation found for tile_states_modes_dnd:0 (6509540227356524582) --> - <!-- no translation found for tile_states_modes_dnd:1 (8589336868985358191) --> - <!-- no translation found for tile_states_modes_dnd:2 (726072717827778234) --> + <string-array name="tile_states_modes_dnd"> + <item msgid="6509540227356524582">"Ayitholakali"</item> + <item msgid="8589336868985358191">"Valiwe"</item> + <item msgid="726072717827778234">"Vuliwe"</item> + </string-array> <string-array name="tile_states_flashlight"> <item msgid="3465257127433353857">"Akutholakali"</item> <item msgid="5044688398303285224">"Valiwe"</item> diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index d0ae307b6919..7d983068f34e 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -989,7 +989,7 @@ <dimen name="keyguard_security_container_padding_top">20dp</dimen> - <dimen name="keyguard_translate_distance_on_swipe_up">-200dp</dimen> + <dimen name="keyguard_translate_distance_on_swipe_up">-180dp</dimen> <dimen name="keyguard_indication_margin_bottom">32dp</dimen> <dimen name="ambient_indication_margin_bottom">71dp</dimen> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index b627bdf22a6c..681bd53f1a40 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -2551,6 +2551,9 @@ <!-- Label for header of customize QS [CHAR LIMIT=60] --> <string name="drag_to_rearrange_tiles">Hold and drag to rearrange tiles</string> + <!-- Label for placing tiles in edit mode for QS [CHAR LIMIT=60] --> + <string name="tap_to_position_tile">Tap to position tile</string> + <!-- Label for area where tiles can be dragged in to [CHAR LIMIT=60] --> <string name="drag_to_remove_tiles">Drag here to remove</string> @@ -2592,6 +2595,12 @@ <!-- Accessibility description of action to remove QS tile on click. It will read as "Double-tap to remove tile" in screen readers [CHAR LIMIT=NONE] --> <string name="accessibility_qs_edit_remove_tile_action">remove tile</string> + <!-- Accessibility description of action to select the QS tile to place on click. It will read as "Double-tap to toggle placement mode" in screen readers [CHAR LIMIT=NONE] --> + <string name="accessibility_qs_edit_toggle_placement_mode">toggle placement mode</string> + + <!-- Accessibility description of action to toggle the QS tile selection. It will read as "Double-tap to toggle selection" in screen readers [CHAR LIMIT=NONE] --> + <string name="accessibility_qs_edit_toggle_selection">toggle selection</string> + <!-- Accessibility action of action to add QS tile to end. It will read as "Double-tap to add tile to the last position" in screen readers [CHAR LIMIT=NONE] --> <string name="accessibility_qs_edit_tile_add_action">add tile to the last position</string> @@ -4200,6 +4209,12 @@ All Quick Settings tiles will reset to the device’s original settings </string> + + <!-- Content of the Reset Tiles dialog in QS Edit mode. [CHAR LIMIT=NONE] --> + <string name="demote_explain_text"> + <xliff:g id="application" example= "Superfast Food Delivery">%1$s</xliff:g> will no longer show Live Updates here. You can change this any time in Settings. + </string> + <!-- Template that joins disabled message with the label for the voice over. [CHAR LIMIT=NONE] --> <string name="volume_slider_disabled_message_template"><xliff:g example="Notification" id="stream_name">%1$s</xliff:g>, <xliff:g example="Disabled because ring is muted" id="disabled_message">%2$s</xliff:g></string> </resources> diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml index 7895ff7b90f6..a479f1841ca4 100644 --- a/packages/SystemUI/res/values/styles.xml +++ b/packages/SystemUI/res/values/styles.xml @@ -19,7 +19,8 @@ <style name="TextAppearance.StatusBar.Clock" parent="@*android:style/TextAppearance.StatusBar.Icon"> <item name="android:textSize">@dimen/status_bar_clock_size</item> - <item name="android:fontFamily">@*android:string/config_headlineFontFamilyMedium</item> + <item name="android:fontFamily" android:featureFlag="!com.android.systemui.status_bar_font_updates">@*android:string/config_headlineFontFamilyMedium</item> + <item name="android:fontFamily" android:featureFlag="com.android.systemui.status_bar_font_updates">"variable-label-large-emphasized"</item> <item name="android:textColor">@color/status_bar_clock_color</item> <item name="android:fontFeatureSettings">tnum</item> </style> diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java index ea7321627322..b8cd5bec2cbe 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/PreviewPositionHelper.java @@ -208,4 +208,19 @@ public class PreviewPositionHelper { } mMatrix.postTranslate(translateX, translateY); } + + /** + * A factory that returns a new instance of the {@link PreviewPositionHelper}. + * <p>{@link PreviewPositionHelper} is a stateful helper, and hence when using it in distinct + * scenarios, prefer fetching an object using this factory</p> + * <p>Additionally, helpful for injecting mocks in tests</p> + */ + public static class PreviewPositionHelperFactory { + /** + * Returns a new {@link PreviewPositionHelper} for use in a distinct scenario. + */ + public PreviewPositionHelper create() { + return new PreviewPositionHelper(); + } + } } diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java index 892851cd7056..8a307145023d 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardViewController.java @@ -160,7 +160,7 @@ public interface KeyguardViewController { /** * Shows the primary bouncer. */ - void showPrimaryBouncer(boolean scrimmed); + void showPrimaryBouncer(boolean scrimmed, String reason); /** * When the primary bouncer is fully visible or is showing but animation didn't finish yet. diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java index 14b13d105482..24b955152093 100644 --- a/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java +++ b/packages/SystemUI/src/com/android/systemui/accessibility/hearingaid/HearingDevicesDialogDelegate.java @@ -286,7 +286,9 @@ public class HearingDevicesDialogDelegate implements SystemUIDialog.Delegate, mLaunchSourceId); final Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_DETAILS_SETTINGS) .putExtra(Intent.EXTRA_COMPONENT_NAME, - ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME.flattenToString()); + ACCESSIBILITY_HEARING_AIDS_COMPONENT_NAME.flattenToString()) + .setPackage(mQSSettingsPackageRepository.getSettingsPackageName()); + mActivityStarter.postStartActivityDismissingKeyguard(intent, /* delay= */ 0, mDialogTransitionAnimator.createActivityTransitionController( dialog)); @@ -588,9 +590,7 @@ public class HearingDevicesDialogDelegate implements SystemUIDialog.Delegate, com.android.internal.R.color.materialColorOnPrimaryContainer)); } text.setText(item.getToolName()); - Intent intent = item.getToolIntent() - .setPackage(mQSSettingsPackageRepository.getSettingsPackageName()); - + Intent intent = item.getToolIntent(); view.setOnClickListener(v -> { final String name = intent.getComponent() != null ? intent.getComponent().flattenToString() diff --git a/packages/SystemUI/src/com/android/systemui/ambient/touch/scrim/BouncerScrimController.java b/packages/SystemUI/src/com/android/systemui/ambient/touch/scrim/BouncerScrimController.java index 6f2dd799c409..633c13e9e94d 100644 --- a/packages/SystemUI/src/com/android/systemui/ambient/touch/scrim/BouncerScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/ambient/touch/scrim/BouncerScrimController.java @@ -34,7 +34,7 @@ public class BouncerScrimController implements ScrimController { @Override public void show(boolean scrimmed) { - mStatusBarKeyguardViewManager.showPrimaryBouncer(scrimmed); + mStatusBarKeyguardViewManager.showPrimaryBouncer(scrimmed, "BouncerScrimController#show"); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java index da1c1bc49d23..2d44d401b0b0 100644 --- a/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java +++ b/packages/SystemUI/src/com/android/systemui/assist/AssistManager.java @@ -130,6 +130,8 @@ public class AssistManager { AssistUtils.INVOCATION_TYPE_POWER_BUTTON_LONG_PRESS; public static final int INVOCATION_TYPE_NAV_HANDLE_LONG_PRESS = AssistUtils.INVOCATION_TYPE_NAV_HANDLE_LONG_PRESS; + public static final int INVOCATION_TYPE_LAUNCHER_SYSTEM_SHORTCUT = + AssistUtils.INVOCATION_TYPE_LAUNCHER_SYSTEM_SHORTCUT; public static final int DISMISS_REASON_INVOCATION_CANCELLED = 1; public static final int DISMISS_REASON_TAP = 2; diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java index dfe8eb28b2a6..659d3b46fea9 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java @@ -880,7 +880,7 @@ public class UdfpsController implements DozeReceiver, Dumpable { Log.v(TAG, "aod lock icon long-press rejected by the falsing manager."); return; } - mKeyguardViewManager.showPrimaryBouncer(true); + mKeyguardViewManager.showPrimaryBouncer(true, "UdfpsController#onAodInterrupt"); // play the same haptic as the DeviceEntryIcon longpress if (mOverlay != null && mOverlay.getTouchOverlay() != null) { diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt index 0c6d7920d7f3..48e08fcd90c5 100644 --- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/PrimaryBouncerInteractor.kt @@ -135,7 +135,7 @@ constructor( // TODO(b/243695312): Encapsulate all of the show logic for the bouncer. /** Show the bouncer if necessary and set the relevant states. */ @JvmOverloads - fun show(isScrimmed: Boolean): Boolean { + fun show(isScrimmed: Boolean, reason: String): Boolean { // When the scene container framework is enabled, instead of calling this, call // SceneInteractor#changeScene(Scenes.Bouncer, ...). SceneContainerFlag.assertInLegacyMode() @@ -176,6 +176,7 @@ constructor( return false } + Log.i(TAG, "Show primary bouncer requested, reason: $reason") repository.setPrimaryShowingSoon(true) if (usePrimaryBouncerPassiveAuthDelay()) { Log.d(TAG, "delay bouncer, passive auth may succeed") diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/compose/gestures/EagerTap.kt b/packages/SystemUI/src/com/android/systemui/common/ui/compose/gestures/EagerTap.kt new file mode 100644 index 000000000000..078ea569a63c --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/common/ui/compose/gestures/EagerTap.kt @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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.common.ui.compose.gestures + +import androidx.compose.foundation.gestures.awaitEachGesture +import androidx.compose.foundation.gestures.awaitFirstDown +import androidx.compose.foundation.gestures.waitForUpOrCancellation +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.input.pointer.PointerInputChange +import androidx.compose.ui.input.pointer.PointerInputScope +import kotlinx.coroutines.coroutineScope + +/** + * Detects taps and double taps without waiting for the double tap minimum delay in between + * + * Using [detectTapGestures] with both a single tap and a double tap defined will send only one of + * these event per user interaction. This variant will send the single tap at all times, with the + * optional double tap if the user pressed a second time in a short period of time. + * + * Warning: Use this only if you know that reporting a single tap followed by a double tap won't be + * a problem in your use case. + * + * @param doubleTapEnabled whether this should listen for double tap events. This value is captured + * at the first down movement. + * @param onDoubleTap the double tap callback + * @param onTap the single tap callback + */ +suspend fun PointerInputScope.detectEagerTapGestures( + doubleTapEnabled: () -> Boolean, + onDoubleTap: (Offset) -> Unit, + onTap: () -> Unit, +) = coroutineScope { + awaitEachGesture { + val down = awaitFirstDown() + down.consume() + + // Capture whether double tap is enabled on first down as this state can change following + // the first tap + val isDoubleTapEnabled = doubleTapEnabled() + + // wait for first tap up or long press + val upOrCancel = waitForUpOrCancellation() + + if (upOrCancel != null) { + // tap was successful. + upOrCancel.consume() + onTap.invoke() + + if (isDoubleTapEnabled) { + // check for second tap + val secondDown = + withTimeoutOrNull(viewConfiguration.doubleTapTimeoutMillis) { + val minUptime = + upOrCancel.uptimeMillis + viewConfiguration.doubleTapMinTimeMillis + var change: PointerInputChange + // The second tap doesn't count if it happens before DoubleTapMinTime of the + // first tap + do { + change = awaitFirstDown() + } while (change.uptimeMillis < minUptime) + change + } + + if (secondDown != null) { + // Second tap down detected + + // Might have a long second press as the second tap + val secondUp = waitForUpOrCancellation() + if (secondUp != null) { + secondUp.consume() + onDoubleTap(secondUp.position) + } + } + } + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalLockIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalLockIconViewModel.kt index 19eeabd98c88..931639c8b247 100644 --- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalLockIconViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalLockIconViewModel.kt @@ -130,7 +130,9 @@ constructor( if (SceneContainerFlag.isEnabled) { deviceEntryInteractor.attemptDeviceEntry() } else { - keyguardViewController.get().showPrimaryBouncer(/* scrim */ true) + keyguardViewController + .get() + .showPrimaryBouncer(/* scrim */ true, "CommunalLockIconViewModel#onUserInteraction") } deviceEntrySourceInteractor.attemptEnterDeviceFromDeviceEntryIcon() } diff --git a/packages/SystemUI/src/com/android/systemui/dagger/PerDisplayRepositoriesModule.kt b/packages/SystemUI/src/com/android/systemui/dagger/PerDisplayRepositoriesModule.kt index 3520439fda88..a8f21885907b 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/PerDisplayRepositoriesModule.kt +++ b/packages/SystemUI/src/com/android/systemui/dagger/PerDisplayRepositoriesModule.kt @@ -19,6 +19,7 @@ package com.android.systemui.dagger import com.android.app.displaylib.DefaultDisplayOnlyInstanceRepositoryImpl import com.android.app.displaylib.PerDisplayInstanceRepositoryImpl import com.android.app.displaylib.PerDisplayRepository +import com.android.systemui.display.data.repository.PerDisplayCoroutineScopeRepositoryModule import com.android.systemui.model.SysUIStateInstanceProvider import com.android.systemui.model.SysUiState import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround @@ -26,7 +27,7 @@ import dagger.Module import dagger.Provides /** This module is meant to contain all the code to create the various [PerDisplayRepository<>]. */ -@Module +@Module(includes = [PerDisplayCoroutineScopeRepositoryModule::class]) class PerDisplayRepositoriesModule { @SysUISingleton diff --git a/packages/SystemUI/src/com/android/systemui/display/DisplayModule.kt b/packages/SystemUI/src/com/android/systemui/display/DisplayModule.kt index 908d0aafb2b9..32b69e5423ba 100644 --- a/packages/SystemUI/src/com/android/systemui/display/DisplayModule.kt +++ b/packages/SystemUI/src/com/android/systemui/display/DisplayModule.kt @@ -29,10 +29,10 @@ import com.android.systemui.display.data.repository.DeviceStateRepository import com.android.systemui.display.data.repository.DeviceStateRepositoryImpl import com.android.systemui.display.data.repository.DisplayRepository import com.android.systemui.display.data.repository.DisplayRepositoryImpl -import com.android.systemui.display.data.repository.DisplayScopeRepository -import com.android.systemui.display.data.repository.DisplayScopeRepositoryImpl import com.android.systemui.display.data.repository.DisplayWindowPropertiesRepository import com.android.systemui.display.data.repository.DisplayWindowPropertiesRepositoryImpl +import com.android.systemui.display.data.repository.DisplaysWithDecorationsRepository +import com.android.systemui.display.data.repository.DisplaysWithDecorationsRepositoryImpl import com.android.systemui.display.data.repository.FocusedDisplayRepository import com.android.systemui.display.data.repository.FocusedDisplayRepositoryImpl import com.android.systemui.display.data.repository.PerDisplayRepoDumpHelper @@ -76,14 +76,17 @@ interface DisplayModule { focusedDisplayRepository: FocusedDisplayRepositoryImpl ): FocusedDisplayRepository - @Binds fun displayScopeRepository(impl: DisplayScopeRepositoryImpl): DisplayScopeRepository - @Binds fun displayWindowPropertiesRepository( impl: DisplayWindowPropertiesRepositoryImpl ): DisplayWindowPropertiesRepository @Binds + fun displaysWithDecorationsRepository( + impl: DisplaysWithDecorationsRepositoryImpl + ): DisplaysWithDecorationsRepository + + @Binds fun dumpRegistrationLambda(helper: PerDisplayRepoDumpHelper): PerDisplayRepository.InitCallback @Binds @@ -94,20 +97,6 @@ interface DisplayModule { @Provides @SysUISingleton @IntoMap - @ClassKey(DisplayScopeRepositoryImpl::class) - fun displayScopeRepoCoreStartable( - repoImplLazy: Lazy<DisplayScopeRepositoryImpl> - ): CoreStartable { - return if (StatusBarConnectedDisplays.isEnabled) { - repoImplLazy.get() - } else { - CoreStartable.NOP - } - } - - @Provides - @SysUISingleton - @IntoMap @ClassKey(DisplayWindowPropertiesRepository::class) fun displayWindowPropertiesRepoAsCoreStartable( repoLazy: Lazy<DisplayWindowPropertiesRepositoryImpl> 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 051fe7e5517c..01bbf2d57dd6 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,95 +16,25 @@ package com.android.systemui.display.data.repository -import android.annotation.SuppressLint -import android.view.IWindowManager import com.android.app.displaylib.DisplayRepository as DisplayRepositoryFromLib import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.dagger.qualifiers.Background -import com.android.systemui.statusbar.CommandQueue import javax.inject.Inject -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.channels.awaitClose -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.callbackFlow -import kotlinx.coroutines.flow.distinctUntilChanged -import kotlinx.coroutines.flow.filter -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.merge -import kotlinx.coroutines.flow.scan -import kotlinx.coroutines.flow.stateIn -/** Repository for providing access to display related information and events. */ -interface DisplayRepository : DisplayRepositoryFromLib { - - /** A [StateFlow] that maintains a set of display IDs that should have system decorations. */ - val displayIdsWithSystemDecorations: StateFlow<Set<Int>> -} +/** + * Repository for providing access to display related information and events. + * + * This is now just an interface that extends [DisplayRepositoryFromLib] to avoid changing all the + * imports in sysui using this interface. + */ +interface DisplayRepository : DisplayRepositoryFromLib, DisplaysWithDecorationsRepository @SysUISingleton -@SuppressLint("SharedFlowCreation") class DisplayRepositoryImpl @Inject constructor( - private val commandQueue: CommandQueue, - private val windowManager: IWindowManager, - @Background bgApplicationScope: CoroutineScope, private val displayRepositoryFromLib: com.android.app.displaylib.DisplayRepository, -) : DisplayRepositoryFromLib by displayRepositoryFromLib, DisplayRepository { - - private val decorationEvents: Flow<Event> = callbackFlow { - val callback = - object : CommandQueue.Callbacks { - override fun onDisplayAddSystemDecorations(displayId: Int) { - trySend(Event.Add(displayId)) - } - - override fun onDisplayRemoveSystemDecorations(displayId: Int) { - trySend(Event.Remove(displayId)) - } - } - commandQueue.addCallback(callback) - awaitClose { commandQueue.removeCallback(callback) } - } - - private val initialDisplayIdsWithDecorations: Set<Int> = - displayIds.value.filter { windowManager.shouldShowSystemDecors(it) }.toSet() - - /** - * A [StateFlow] that maintains a set of display IDs that should have system decorations. - * - * Updates to the set are triggered by: - * - Adding displays via [CommandQueue.Callbacks.onDisplayAddSystemDecorations]. - * - Removing displays via [CommandQueue.Callbacks.onDisplayRemoveSystemDecorations]. - * - Removing displays via [displayRemovalEvent] emissions. - * - * The set is initialized with displays that qualify for system decorations based on - * [WindowManager.shouldShowSystemDecors]. - */ - override val displayIdsWithSystemDecorations: StateFlow<Set<Int>> = - merge(decorationEvents, displayRemovalEvent.map { Event.Remove(it) }) - .scan(initialDisplayIdsWithDecorations) { displayIds: Set<Int>, event: Event -> - when (event) { - is Event.Add -> displayIds + event.displayId - is Event.Remove -> displayIds - event.displayId - } - } - .distinctUntilChanged() - .stateIn( - scope = bgApplicationScope, - started = SharingStarted.WhileSubscribed(), - initialValue = initialDisplayIdsWithDecorations, - ) - - private sealed class Event(val displayId: Int) { - class Add(displayId: Int) : Event(displayId) - - class Remove(displayId: Int) : Event(displayId) - } - - private companion object { - const val TAG = "DisplayRepository" - } -} + private val displaysWithDecorationsRepositoryImpl: DisplaysWithDecorationsRepository, +) : + DisplayRepositoryFromLib by displayRepositoryFromLib, + DisplaysWithDecorationsRepository by displaysWithDecorationsRepositoryImpl, + DisplayRepository diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayScopeRepository.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayScopeRepository.kt index d912b6a13d0f..ac59b16bfe3c 100644 --- a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayScopeRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayScopeRepository.kt @@ -17,60 +17,58 @@ package com.android.systemui.display.data.repository import android.view.Display -import com.android.systemui.CoreStartable +import com.android.app.displaylib.PerDisplayInstanceProviderWithTeardown +import com.android.app.displaylib.PerDisplayInstanceRepositoryImpl +import com.android.app.displaylib.PerDisplayRepository import com.android.systemui.coroutines.newTracingContext import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background -import com.android.systemui.statusbar.core.StatusBarConnectedDisplays -import java.util.concurrent.ConcurrentHashMap +import dagger.Module +import dagger.Provides import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.cancel -import com.android.app.tracing.coroutines.launchTraced as launch /** - * Provides per display instances of [CoroutineScope]. These will remain active as long as the - * display is connected, and automatically cancelled when the display is removed. + * Provides per display instances of [CoroutineScope]. + * + * This is used to create a [PerDisplayRepository] of [CoroutineScope] */ -interface DisplayScopeRepository { - fun scopeForDisplay(displayId: Int): CoroutineScope -} - @SysUISingleton -class DisplayScopeRepositoryImpl +class DisplayScopeRepositoryInstanceProvider @Inject constructor( @Background private val backgroundApplicationScope: CoroutineScope, @Background private val backgroundDispatcher: CoroutineDispatcher, - private val displayRepository: DisplayRepository, -) : DisplayScopeRepository, CoreStartable { - - private val perDisplayScopes = ConcurrentHashMap<Int, CoroutineScope>() - - override fun scopeForDisplay(displayId: Int): CoroutineScope { - return perDisplayScopes.computeIfAbsent(displayId) { createScopeForDisplay(displayId) } - } - - override fun start() { - StatusBarConnectedDisplays.unsafeAssertInNewMode() - backgroundApplicationScope.launch { - displayRepository.displayRemovalEvent.collect { displayId -> - val scope = perDisplayScopes.remove(displayId) - scope?.cancel("Display $displayId has been removed.") - } - } - } +) : PerDisplayInstanceProviderWithTeardown<CoroutineScope> { - private fun createScopeForDisplay(displayId: Int): CoroutineScope { + override fun createInstance(displayId: Int): CoroutineScope { return if (displayId == Display.DEFAULT_DISPLAY) { // The default display is connected all the time, therefore we can optimise by reusing // the application scope, and don't need to create a new scope. backgroundApplicationScope } else { - CoroutineScope( - backgroundDispatcher + newTracingContext("DisplayScope$displayId") - ) + CoroutineScope(backgroundDispatcher + newTracingContext("DisplayScope$displayId")) } } + + override fun destroyInstance(instance: CoroutineScope) { + instance.cancel("DisplayContext has been cancelled.") + } +} + +@Module +object PerDisplayCoroutineScopeRepositoryModule { + @SysUISingleton + @Provides + fun provideDisplayCoroutineScopeRepository( + repositoryFactory: PerDisplayInstanceRepositoryImpl.Factory<CoroutineScope>, + instanceProvider: DisplayScopeRepositoryInstanceProvider, + ): PerDisplayRepository<CoroutineScope> { + return repositoryFactory.create( + debugName = "CoroutineScopePerDisplayRepo", + instanceProvider, + ) + } } diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplaysWithDecorationsRepository.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplaysWithDecorationsRepository.kt new file mode 100644 index 000000000000..f4a2ed48f295 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplaysWithDecorationsRepository.kt @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.display.data.repository + +import android.view.IWindowManager +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.statusbar.CommandQueue +import javax.inject.Inject +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.callbackFlow +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.merge +import kotlinx.coroutines.flow.scan +import kotlinx.coroutines.flow.stateIn + +/** Provides the displays with decorations. */ +interface DisplaysWithDecorationsRepository { + /** A [StateFlow] that maintains a set of display IDs that should have system decorations. */ + val displayIdsWithSystemDecorations: StateFlow<Set<Int>> +} + +@SysUISingleton +class DisplaysWithDecorationsRepositoryImpl +@Inject +constructor( + private val commandQueue: CommandQueue, + private val windowManager: IWindowManager, + @Background bgApplicationScope: CoroutineScope, + displayRepository: com.android.app.displaylib.DisplayRepository, +) : DisplaysWithDecorationsRepository { + + private val decorationEvents: Flow<Event> = callbackFlow { + val callback = + object : CommandQueue.Callbacks { + override fun onDisplayAddSystemDecorations(displayId: Int) { + trySend(Event.Add(displayId)) + } + + override fun onDisplayRemoveSystemDecorations(displayId: Int) { + trySend(Event.Remove(displayId)) + } + } + commandQueue.addCallback(callback) + awaitClose { commandQueue.removeCallback(callback) } + } + + private val initialDisplayIdsWithDecorations: Set<Int> = + displayRepository.displayIds.value + .filter { windowManager.shouldShowSystemDecors(it) } + .toSet() + + /** + * A [StateFlow] that maintains a set of display IDs that should have system decorations. + * + * Updates to the set are triggered by: + * - Adding displays via [CommandQueue.Callbacks.onDisplayAddSystemDecorations]. + * - Removing displays via [CommandQueue.Callbacks.onDisplayRemoveSystemDecorations]. + * - Removing displays via [displayRemovalEvent] emissions. + * + * The set is initialized with displays that qualify for system decorations based on + * [WindowManager.shouldShowSystemDecors]. + */ + override val displayIdsWithSystemDecorations: StateFlow<Set<Int>> = + merge(decorationEvents, displayRepository.displayRemovalEvent.map { Event.Remove(it) }) + .scan(initialDisplayIdsWithDecorations) { displayIds: Set<Int>, event: Event -> + when (event) { + is Event.Add -> displayIds + event.displayId + is Event.Remove -> displayIds - event.displayId + } + } + .distinctUntilChanged() + .stateIn( + scope = bgApplicationScope, + started = SharingStarted.WhileSubscribed(), + initialValue = initialDisplayIdsWithDecorations, + ) + + private sealed class Event(val displayId: Int) { + class Add(displayId: Int) : Event(displayId) + + class Remove(displayId: Int) : Event(displayId) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayStore.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayStore.kt index 46048868f503..8756c8813df6 100644 --- a/packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayStore.kt +++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/PerDisplayStore.kt @@ -68,7 +68,7 @@ abstract class PerDisplayStoreImpl<T>( * displays. */ override fun forDisplay(displayId: Int): T? { - if (displayRepository.getDisplay(displayId) == null) { + if (displayRepository.getDisplay(displayId) == null) { Log.e(TAG, "<${instanceClass.simpleName}>: Display with id $displayId doesn't exist.") return null } diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java index 9def81a89e3a..2b16e198b1b1 100644 --- a/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java +++ b/packages/SystemUI/src/com/android/systemui/doze/DozeUi.java @@ -18,7 +18,6 @@ package com.android.systemui.doze; import static com.android.systemui.doze.DozeMachine.State.DOZE; import static com.android.systemui.doze.DozeMachine.State.DOZE_AOD_PAUSED; -import static com.android.systemui.Flags.dozeuiSchedulingAlarmsBackgroundExecution; import android.app.AlarmManager; import android.content.Context; @@ -84,13 +83,7 @@ public class DozeUi implements DozeMachine.Part { mBgExecutor = bgExecutor; mCanAnimateTransition = !params.getDisplayNeedsBlanking(); mDozeParameters = params; - if (dozeuiSchedulingAlarmsBackgroundExecution()) { - mTimeTicker = new AlarmTimeout(alarmManager, this::onTimeTick, "doze_time_tick", - bgHandler); - } else { - mTimeTicker = new AlarmTimeout(alarmManager, this::onTimeTick, "doze_time_tick", - handler); - } + mTimeTicker = new AlarmTimeout(alarmManager, this::onTimeTick, "doze_time_tick", bgHandler); mDozeLog = dozeLog; } @@ -184,7 +177,7 @@ public class DozeUi implements DozeMachine.Part { mTimeTickScheduled = true; long time = System.currentTimeMillis(); - long delta = roundToNextMinute(time) - System.currentTimeMillis(); + long delta = roundToNextMinute(time) - time; boolean scheduled = mTimeTicker.schedule(delta, AlarmTimeout.MODE_RESCHEDULE_IF_SCHEDULED); if (scheduled) { mDozeLog.traceTimeTickScheduled(time, time + delta); @@ -224,14 +217,8 @@ public class DozeUi implements DozeMachine.Part { private void onTimeTick() { verifyLastTimeTick(); - if (dozeuiSchedulingAlarmsBackgroundExecution()) { - mHandler.post(mHost::dozeTimeTick); - } else { - mHost.dozeTimeTick(); - } - // Keep wakelock until a frame has been pushed. - mHandler.post(mWakeLock.wrap(() -> {})); + mHandler.post(mWakeLock.wrap(mHost::dozeTimeTick)); mTimeTickScheduled = false; scheduleTimeTick(); diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index 4755e2845587..099a7f067482 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -140,6 +140,7 @@ import com.android.systemui.animation.TransitionAnimator; import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor; +import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor; import com.android.systemui.communal.ui.viewmodel.CommunalTransitionViewModel; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dagger.qualifiers.UiBackground; @@ -364,6 +365,7 @@ public class KeyguardViewMediator implements CoreStartable, private final Lazy<NotificationShadeDepthController> mNotificationShadeDepthController; private final Lazy<ShadeController> mShadeController; private final Lazy<CommunalSceneInteractor> mCommunalSceneInteractor; + private final Lazy<CommunalSettingsInteractor> mCommunalSettingsInteractor; /* * Records the user id on request to go away, for validation when WM calls back to start the * exit animation. @@ -1567,6 +1569,7 @@ public class KeyguardViewMediator implements CoreStartable, KeyguardInteractor keyguardInteractor, KeyguardTransitionBootInteractor transitionBootInteractor, Lazy<CommunalSceneInteractor> communalSceneInteractor, + Lazy<CommunalSettingsInteractor> communalSettingsInteractor, WindowManagerOcclusionManager wmOcclusionManager) { mContext = context; mUserTracker = userTracker; @@ -1609,6 +1612,7 @@ public class KeyguardViewMediator implements CoreStartable, mKeyguardInteractor = keyguardInteractor; mTransitionBootInteractor = transitionBootInteractor; mCommunalSceneInteractor = communalSceneInteractor; + mCommunalSettingsInteractor = communalSettingsInteractor; mStatusBarStateController = statusBarStateController; statusBarStateController.addCallback(this); @@ -1744,6 +1748,9 @@ public class KeyguardViewMediator implements CoreStartable, mJavaAdapter.alwaysCollectFlow( mWallpaperRepository.getWallpaperSupportsAmbientMode(), this::setWallpaperSupportsAmbientMode); + mJavaAdapter.alwaysCollectFlow( + mKeyguardInteractor.getDozeTimeTick(), + this::triggerTimeUpdate); } @Override @@ -2426,9 +2433,18 @@ public class KeyguardViewMediator implements CoreStartable, private void doKeyguardLocked(Bundle options) { // If the power button behavior requests to open the glanceable hub. if (options != null && options.getBoolean(EXTRA_TRIGGER_HUB)) { - // Set the hub to show immediately when the SysUI window shows, then continue to lock - // the device. - mCommunalSceneInteractor.get().showHubFromPowerButton(); + if (mCommunalSettingsInteractor.get().getAutoOpenEnabled().getValue()) { + // Set the hub to show immediately when the SysUI window shows, then continue to + // lock the device. + mCommunalSceneInteractor.get().showHubFromPowerButton(); + } else { + // If the hub is not available, go to sleep instead of locking. This can happen + // because the power button behavior does not check all possible reasons the hub + // might be disabled. + mPM.goToSleep(android.os.SystemClock.uptimeMillis(), + PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, 0); + return; + } } int currentUserId = mSelectedUserInteractor.getSelectedUserId(); @@ -3762,13 +3778,7 @@ public class KeyguardViewMediator implements CoreStartable, Log.d(TAG, "Status bar manager is disabled for visible background users"); } } else { - try { - mStatusBarService.disableForUser(flags, mStatusBarDisableToken, - mContext.getPackageName(), - mSelectedUserInteractor.getSelectedUserId()); - } catch (RemoteException e) { - Log.d(TAG, "Failed to force clear flags", e); - } + statusBarServiceDisableForUser(flags, "Failed to force clear flags"); } } @@ -3804,18 +3814,29 @@ public class KeyguardViewMediator implements CoreStartable, // Handled in StatusBarDisableFlagsInteractor. if (!KeyguardWmStateRefactor.isEnabled()) { - try { - mStatusBarService.disableForUser(flags, mStatusBarDisableToken, - mContext.getPackageName(), - mSelectedUserInteractor.getSelectedUserId()); - } catch (RemoteException e) { - Log.d(TAG, "Failed to set disable flags: " + flags, e); - } + statusBarServiceDisableForUser(flags, "Failed to set disable flags: "); } } } } + private void statusBarServiceDisableForUser(int flags, String loggingContext) { + Runnable runnable = () -> { + try { + mStatusBarService.disableForUser(flags, mStatusBarDisableToken, + mContext.getPackageName(), + mSelectedUserInteractor.getSelectedUserId()); + } catch (RemoteException e) { + Log.d(TAG, loggingContext + " " + flags, e); + } + }; + if (com.android.systemui.Flags.bouncerUiRevamp()) { + mUiBgExecutor.execute(runnable); + } else { + runnable.run(); + } + } + /** * Handle message sent by {@link #resetStateLocked} * @see #RESET @@ -4056,6 +4077,10 @@ public class KeyguardViewMediator implements CoreStartable, mWallpaperSupportsAmbientMode = supportsAmbientMode; } + private void triggerTimeUpdate(long timeInMillis) { + mUpdateMonitor.triggerTimeUpdate(); + } + private static class StartKeyguardExitAnimParams { @WindowManager.TransitionOldType int mTransit; @@ -4092,12 +4117,23 @@ public class KeyguardViewMediator implements CoreStartable, || aodShowing != mAodShowing || forceCallbacks; mShowing = showing; mAodShowing = aodShowing; - if (notifyDefaultDisplayCallbacks) { - notifyDefaultDisplayCallbacks(showing); - } - if (updateActivityLockScreenState) { - updateActivityLockScreenState(showing, aodShowing, reason); + + if (KeyguardWmReorderAtmsCalls.isEnabled()) { + if (updateActivityLockScreenState) { + updateActivityLockScreenState(showing, aodShowing, reason); + } + if (notifyDefaultDisplayCallbacks) { + notifyDefaultDisplayCallbacks(showing); + } + } else { + if (notifyDefaultDisplayCallbacks) { + notifyDefaultDisplayCallbacks(showing); + } + if (updateActivityLockScreenState) { + updateActivityLockScreenState(showing, aodShowing, reason); + } } + } private void notifyDefaultDisplayCallbacks(boolean showing) { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardWmReorderAtmsCalls.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardWmReorderAtmsCalls.kt new file mode 100644 index 000000000000..7ac52813ff71 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardWmReorderAtmsCalls.kt @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.keyguard + +import com.android.systemui.Flags +import com.android.systemui.flags.FlagToken +import com.android.systemui.flags.RefactorFlagUtils + +/** Helper for reading or using the keyguard wm state refactor flag state. */ +@Suppress("NOTHING_TO_INLINE") +object KeyguardWmReorderAtmsCalls { + /** The aconfig flag name */ + const val FLAG_NAME = Flags.FLAG_KEYGUARD_WM_REORDER_ATMS_CALLS + + /** A token used for dependency declaration */ + val token: FlagToken + get() = FlagToken(FLAG_NAME, isEnabled) + + /** Is the refactor enabled */ + @JvmStatic + inline val isEnabled + get() = Flags.keyguardWmReorderAtmsCalls() + + /** + * Called to ensure code is only run when the flag is enabled. This protects users from the + * unintended behaviors caused by accidentally running new logic, while also crashing on an eng + * build to ensure that the refactor author catches issues in testing. + */ + @JvmStatic + inline fun isUnexpectedlyInLegacyMode() = + RefactorFlagUtils.isUnexpectedlyInLegacyMode(isEnabled, FLAG_NAME) + + /** + * Called to ensure code is only run when the flag is disabled. This will throw an exception if + * the flag is enabled to ensure that the refactor author catches issues in testing. + */ + @JvmStatic + inline fun assertInLegacyMode() = RefactorFlagUtils.assertInLegacyMode(isEnabled, FLAG_NAME) +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardWmStateRefactor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardWmStateRefactor.kt index ddccc5d9e96d..41d14b9e727f 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardWmStateRefactor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardWmStateRefactor.kt @@ -20,7 +20,16 @@ import com.android.systemui.Flags import com.android.systemui.flags.FlagToken import com.android.systemui.flags.RefactorFlagUtils -/** Helper for reading or using the keyguard wm state refactor flag state. */ +/** + * Helper for reading or using the keyguard_wm_state_refactor flag state. + * + * keyguard_wm_state_refactor works both with and without flexiglass (scene_container), but + * flexiglass requires keyguard_wm_state_refactor. For this reason, this class will return isEnabled + * if either keyguard_wm_state_refactor OR scene_container are enabled. This enables us to roll out + * keyguard_wm_state_refactor independently of scene_container, while also ensuring that + * scene_container rolling out ahead of keyguard_wm_state_refactor causes code gated by + * KeyguardWmStateRefactor to be enabled as well. + */ @Suppress("NOTHING_TO_INLINE") object KeyguardWmStateRefactor { /** The aconfig flag name */ @@ -30,10 +39,9 @@ object KeyguardWmStateRefactor { val token: FlagToken get() = FlagToken(FLAG_NAME, isEnabled) - /** Is the refactor enabled */ @JvmStatic inline val isEnabled - get() = Flags.keyguardWmStateRefactor() + get() = Flags.keyguardWmStateRefactor() || Flags.sceneContainer() /** * Called to ensure code is only run when the flag is enabled. This protects users from the diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java index 6b1248b6983e..1fe6eb9ce7c8 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java @@ -42,6 +42,7 @@ import com.android.systemui.broadcast.BroadcastDispatcher; import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.classifier.FalsingModule; import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor; +import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor; import com.android.systemui.communal.ui.viewmodel.CommunalTransitionViewModel; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; @@ -182,6 +183,7 @@ public interface KeyguardModule { KeyguardInteractor keyguardInteractor, KeyguardTransitionBootInteractor transitionBootInteractor, Lazy<CommunalSceneInteractor> communalSceneInteractor, + Lazy<CommunalSettingsInteractor> communalSettingsInteractor, WindowManagerOcclusionManager windowManagerOcclusionManager) { return new KeyguardViewMediator( context, @@ -234,6 +236,7 @@ public interface KeyguardModule { keyguardInteractor, transitionBootInteractor, communalSceneInteractor, + communalSettingsInteractor, windowManagerOcclusionManager); } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt index f8c7a86687dd..f4e804ac5abf 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt @@ -24,6 +24,7 @@ import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor import com.android.systemui.communal.shared.model.CommunalScenes import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.keyguard.KeyguardWmStateRefactor @@ -62,6 +63,7 @@ constructor( override val internalTransitionInteractor: InternalKeyguardTransitionInteractor, transitionInteractor: KeyguardTransitionInteractor, @Background private val scope: CoroutineScope, + @Application private val applicationScope: CoroutineScope, @Background bgDispatcher: CoroutineDispatcher, @Main mainDispatcher: CoroutineDispatcher, keyguardInteractor: KeyguardInteractor, @@ -175,7 +177,7 @@ constructor( private fun listenForLockscreenToPrimaryBouncerDragging() { if (SceneContainerFlag.isEnabled) return var transitionId: UUID? = null - scope.launch("$TAG#listenForLockscreenToPrimaryBouncerDragging") { + applicationScope.launch("$TAG#listenForLockscreenToPrimaryBouncerDragging") { shadeRepository.legacyShadeExpansion.collect { shadeExpansion -> val statusBarState = keyguardInteractor.statusBarState.value val isKeyguardUnlocked = keyguardInteractor.isKeyguardDismissible.value @@ -204,7 +206,7 @@ constructor( id, // This maps the shadeExpansion to a much faster curve, to match // the existing logic - 1f - MathUtils.constrainedMap(0f, 1f, 0.95f, 1f, shadeExpansion), + 1f - MathUtils.constrainedMap(0f, 1f, 0.88f, 1f, shadeExpansion), nextState, ) } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractor.kt index 0a4022ad4de8..e6f8406726f0 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardDismissInteractor.kt @@ -159,7 +159,10 @@ constructor( if (alternateBouncerInteractor.canShowAlternateBouncer.value) { alternateBouncerInteractor.forceShow() } else { - primaryBouncerInteractor.show(true) + primaryBouncerInteractor.show( + true, + "KeyguardDismissInteractor#dismissKeyguardWithCallback", + ) } } } 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 7977000ed5c8..2d5ff61a5015 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 @@ -19,7 +19,6 @@ import android.app.StatusBarManager import android.graphics.Point import android.util.Log import android.util.MathUtils -import com.android.app.animation.Interpolators import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository import com.android.systemui.common.shared.model.NotificationContainerBounds import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor @@ -371,9 +370,11 @@ constructor( currentKeyguardState == LOCKSCREEN && legacyShadeExpansion != 1f ) { - emit(MathUtils.constrainedMap(0f, 1f, 0.95f, 1f, legacyShadeExpansion)) + emit(MathUtils.constrainedMap(0f, 1f, 0.82f, 1f, legacyShadeExpansion)) } else if ( - (legacyShadeExpansion == 0f || legacyShadeExpansion == 1f) && !onGlanceableHub + !onGlanceableHub && + isKeyguardDismissible && + (legacyShadeExpansion == 0f || legacyShadeExpansion == 1f) ) { // Resets alpha state emit(1f) @@ -401,15 +402,7 @@ constructor( // 0f and 1f need to be ignored in the legacy shade expansion. These can // flip arbitrarily as the legacy shade is reset, and would cause the // translation value to jump around unexpectedly. - emit( - MathUtils.lerp( - translationDistance, - 0, - Interpolators.FAST_OUT_LINEAR_IN.getInterpolation( - legacyShadeExpansion - ), - ) - ) + emit(MathUtils.lerp(translationDistance, 0, legacyShadeExpansion)) } } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractor.kt index 6d9b276031e9..ced96e93d87d 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardKeyEventInteractor.kt @@ -136,7 +136,10 @@ constructor( return true } StatusBarState.KEYGUARD -> { - statusBarKeyguardViewManager.showPrimaryBouncer(true) + statusBarKeyguardViewManager.showPrimaryBouncer( + true, + "KeyguardKeyEventInteractor#collapseShadeLockedOrShowPrimaryBouncer", + ) return true } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt index af58d10f3066..df58b215167a 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt @@ -20,6 +20,8 @@ package com.android.systemui.keyguard.domain.interactor import android.annotation.SuppressLint import android.util.Log import com.android.app.tracing.coroutines.flow.filterTraced +import com.android.app.tracing.coroutines.flow.shareInTraced +import com.android.app.tracing.coroutines.flow.stateInTraced import com.android.app.tracing.coroutines.flow.traceAs import com.android.app.tracing.coroutines.launchTraced as launch import com.android.app.tracing.coroutines.traceCoroutine @@ -64,8 +66,6 @@ import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.mapLatest import kotlinx.coroutines.flow.onStart -import kotlinx.coroutines.flow.shareIn -import kotlinx.coroutines.flow.stateIn /** Encapsulates business-logic related to the keyguard transitions. */ @SysUISingleton @@ -102,12 +102,18 @@ constructor( val transitions = repository.transitions val transitionState: StateFlow<TransitionStep> = - transitions.stateIn(scope, SharingStarted.Eagerly, TransitionStep()) + transitions.stateInTraced( + "KTF-transitionState", + scope, + SharingStarted.Eagerly, + TransitionStep(), + ) private val sceneTransitionPair = sceneInteractor.transitionState .pairwise() - .stateIn( + .stateInTraced( + "KTF-sceneTransitionPair", scope, SharingStarted.Eagerly, WithPrev( @@ -130,11 +136,16 @@ constructor( repository.transitions .pairwise() .filter { it.newValue.transitionState == TransitionState.STARTED } - .shareIn(scope, SharingStarted.Eagerly, replay = 1) + .shareInTraced( + "KTF-startedStepWithPrecedingStep", + scope, + SharingStarted.Eagerly, + replay = 1, + ) init { // Collect non-canceled steps and emit transition values. - scope.launch { + scope.launch("KTF-update-non-canceled") { repository.transitions .filter { it.transitionState != TransitionState.CANCELED } .collect { step -> @@ -145,7 +156,7 @@ constructor( } } - scope.launch { + scope.launch("KTF-update-transitionMap") { repository.transitions.collect { // FROM->TO transitionMap[Edge.create(it.from, it.to)]?.emit(it) @@ -160,7 +171,7 @@ constructor( // need to ensure we emit transitionValue(A) = 0f, since no further steps will be emitted // where the from or to states are A. This would leave transitionValue(A) stuck at an // arbitrary non-zero value. - scope.launch { + scope.launch("KTF-update-canceled") { startedStepWithPrecedingStep.collect { (prevStep, startedStep) -> if ( prevStep.transitionState == TransitionState.CANCELED && @@ -180,7 +191,7 @@ constructor( // Safety: When any transition is FINISHED, ensure all other transitionValue flows other // than the FINISHED state are reset to a value of 0f. There have been rare but severe // bugs that get the device stuck in a bad state when these are not properly reset. - scope.launch { + scope.launch("KTF-update-finished") { repository.transitions .filter { it.transitionState == TransitionState.FINISHED } .collect { @@ -201,7 +212,7 @@ constructor( * If the screen is turning off, finish the current transition immediately. Further * frames won't be visible anyway. */ - scope.launch { + scope.launch("KTF-force-finish") { powerInteractor.screenPowerState .filter { it == ScreenPowerState.SCREEN_TURNING_OFF } .collect { repository.forceFinishCurrentTransition() } @@ -347,6 +358,7 @@ constructor( } } } + .traceAs("KTF-transition-simulator") /** * This function is similar to flatMapLatest but it will additionally emit a FINISHED @@ -372,7 +384,7 @@ constructor( traceCoroutine("cancelAndJoin") { job?.cancelAndJoin() } job = - launch("inner") { + launch("KTF-flatMapLatestWithFinished") { val innerFlow = transform(value) try { innerFlow.collect { step -> @@ -398,7 +410,6 @@ constructor( } } } - .traceAs("flatMapLatestWithFinished") /** * Converts old KTF states to UNDEFINED when [SceneContainerFlag] is enabled. @@ -451,7 +462,12 @@ constructor( val startedKeyguardTransitionStep: StateFlow<TransitionStep> = repository.transitions .filter { step -> step.transitionState == TransitionState.STARTED } - .stateIn(scope, SharingStarted.Eagerly, TransitionStep()) + .stateInTraced( + "KTF-startedKeyguardTransitionStep", + scope, + SharingStarted.Eagerly, + TransitionStep(), + ) /** * The [KeyguardState] we're currently in. @@ -517,7 +533,7 @@ constructor( it.from } } - .stateIn(scope, SharingStarted.Eagerly, OFF) + .stateInTraced("KTF-currentKeyguardState", scope, SharingStarted.Eagerly, OFF) val isInTransition = combine(isInTransitionWhere({ true }, { true }), sceneInteractor.transitionState) { @@ -629,7 +645,7 @@ constructor( repository.transitions .filter { it.transitionState == TransitionState.FINISHED } .map { it.to } - .stateIn(scope, SharingStarted.Eagerly, OFF) + .stateInTraced("KTF-finishedKeyguardState", scope, SharingStarted.Eagerly, OFF) companion object { private val TAG = KeyguardTransitionInteractor::class.simpleName 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 68d595ebf0b6..b4e9d8296a74 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 @@ -196,39 +196,50 @@ constructor( .distinctUntilChanged() } - private val lockscreenVisibilityWithScenes = - combine( - sceneInteractor.get().transitionState.flatMapLatestConflated { - when (it) { - is Idle -> { - when (it.currentScene) { - in keyguardContent -> flowOf(true) - in nonKeyguardContent -> flowOf(false) - in keyguardAgnosticContent -> isDeviceNotEnteredDirectly - else -> - throw IllegalStateException("Unknown scene: ${it.currentScene}") - } - } - is Transition -> { - when { - it.isTransitioningSets(from = keyguardContent) -> flowOf(true) - it.isTransitioningSets(from = nonKeyguardContent) -> flowOf(false) - it.isTransitioningSets(from = keyguardAgnosticContent) -> - isDeviceNotEnteredDirectly - else -> - throw IllegalStateException( - "Unknown content: ${it.fromContent}" - ) + private val lockscreenVisibilityWithScenes: Flow<Boolean> = + // The scene container visibility into account as that will be forced to false when the + // device isn't yet provisioned (e.g. still in the setup wizard). + sceneInteractor.get().isVisible.flatMapLatestConflated { isVisible -> + if (isVisible) { + combine( + sceneInteractor.get().transitionState.flatMapLatestConflated { + when (it) { + is Idle -> + when (it.currentScene) { + in keyguardContent -> flowOf(true) + in nonKeyguardContent -> flowOf(false) + in keyguardAgnosticContent -> isDeviceNotEnteredDirectly + else -> + throw IllegalStateException( + "Unknown scene: ${it.currentScene}" + ) + } + is Transition -> + when { + it.isTransitioningSets(from = keyguardContent) -> + flowOf(true) + it.isTransitioningSets(from = nonKeyguardContent) -> + flowOf(false) + it.isTransitioningSets(from = keyguardAgnosticContent) -> + isDeviceNotEnteredDirectly + else -> + throw IllegalStateException( + "Unknown content: ${it.fromContent}" + ) + } } - } + }, + wakeToGoneInteractor.canWakeDirectlyToGone, + ::Pair, + ) + .map { (lockscreenVisibilityByTransitionState, canWakeDirectlyToGone) -> + lockscreenVisibilityByTransitionState && !canWakeDirectlyToGone } - }, - wakeToGoneInteractor.canWakeDirectlyToGone, - ::Pair, - ) - .map { (lockscreenVisibilityByTransitionState, canWakeDirectlyToGone) -> - lockscreenVisibilityByTransitionState && !canWakeDirectlyToGone + } else { + // Lockscreen is never visible when the scene container is invisible. + flowOf(false) } + } private val lockscreenVisibilityLegacy = combine( diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt index 70a827d5e45b..1ea47ec670af 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/DeviceEntryIconViewBinder.kt @@ -39,6 +39,7 @@ import com.android.systemui.plugins.FalsingManager import com.android.systemui.res.R import com.android.systemui.statusbar.VibratorHelper import com.android.systemui.util.kotlin.DisposableHandles +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.DisposableHandle @@ -56,6 +57,7 @@ object DeviceEntryIconViewBinder { @JvmStatic fun bind( applicationScope: CoroutineScope, + mainImmediateDispatcher: CoroutineDispatcher, view: DeviceEntryIconView, viewModel: DeviceEntryIconViewModel, fgViewModel: DeviceEntryForegroundViewModel, @@ -96,6 +98,32 @@ object DeviceEntryIconViewBinder { } disposables += + view.repeatWhenAttached(mainImmediateDispatcher) { + repeatOnLifecycle(Lifecycle.State.CREATED) { + launch("$TAG#viewModel.useBackgroundProtection") { + viewModel.useBackgroundProtection.collect { useBackgroundProtection -> + if (useBackgroundProtection) { + bgView.visibility = View.VISIBLE + } else { + bgView.visibility = View.GONE + } + } + } + launch("$TAG#viewModel.burnInOffsets") { + viewModel.burnInOffsets.collect { burnInOffsets -> + view.translationX = burnInOffsets.x.toFloat() + view.translationY = burnInOffsets.y.toFloat() + view.aodFpDrawable.progress = burnInOffsets.progress + } + } + + launch("$TAG#viewModel.deviceEntryViewAlpha") { + viewModel.deviceEntryViewAlpha.collect { alpha -> view.alpha = alpha } + } + } + } + + disposables += view.repeatWhenAttached { // Repeat on CREATED so that the view will always observe the entire // GONE => AOD transition (even though the view may not be visible until the middle @@ -152,26 +180,6 @@ object DeviceEntryIconViewBinder { } } } - launch("$TAG#viewModel.useBackgroundProtection") { - viewModel.useBackgroundProtection.collect { useBackgroundProtection -> - if (useBackgroundProtection) { - bgView.visibility = View.VISIBLE - } else { - bgView.visibility = View.GONE - } - } - } - launch("$TAG#viewModel.burnInOffsets") { - viewModel.burnInOffsets.collect { burnInOffsets -> - view.translationX = burnInOffsets.x.toFloat() - view.translationY = burnInOffsets.y.toFloat() - view.aodFpDrawable.progress = burnInOffsets.progress - } - } - - launch("$TAG#viewModel.deviceEntryViewAlpha") { - viewModel.deviceEntryViewAlpha.collect { alpha -> view.alpha = alpha } - } } } @@ -212,7 +220,7 @@ object DeviceEntryIconViewBinder { } disposables += - bgView.repeatWhenAttached { + bgView.repeatWhenAttached(mainImmediateDispatcher) { repeatOnLifecycle(Lifecycle.State.CREATED) { launch("$TAG#bgViewModel.alpha") { bgViewModel.alpha.collect { alpha -> bgView.alpha = alpha } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt index aeb327035c79..60460bf68c12 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt @@ -135,7 +135,10 @@ object KeyguardRootViewBinder { } else if ( event.action == MotionEvent.ACTION_UP && !event.isTouchscreenSource() ) { - statusBarKeyguardViewManager?.showBouncer(true) + statusBarKeyguardViewManager?.showBouncer( + true, + "KeyguardRootViewBinder: click on lockscreen", + ) consumed = true } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt index 58d482b8a66f..9c8f04b419fb 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySection.kt @@ -28,6 +28,7 @@ import androidx.constraintlayout.widget.ConstraintSet import com.android.systemui.biometrics.AuthController import com.android.systemui.customization.R as customR import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags import com.android.systemui.keyguard.shared.model.KeyguardSection @@ -48,6 +49,7 @@ import com.android.systemui.shade.ShadeDisplayAware import com.android.systemui.statusbar.VibratorHelper import dagger.Lazy import javax.inject.Inject +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.DisposableHandle @@ -56,6 +58,7 @@ class DefaultDeviceEntrySection @Inject constructor( @Application private val applicationScope: CoroutineScope, + @Main private val mainDispatcher: CoroutineDispatcher, private val authController: AuthController, private val windowManager: WindowManager, @ShadeDisplayAware private val context: Context, @@ -91,6 +94,7 @@ constructor( disposableHandle = DeviceEntryIconViewBinder.bind( applicationScope, + mainDispatcher, it, deviceEntryIconViewModel.get(), deviceEntryForegroundViewModel.get(), diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerUdfpsIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerUdfpsIconViewModel.kt index 9038922466df..803e2c0b0f96 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerUdfpsIconViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerUdfpsIconViewModel.kt @@ -106,7 +106,10 @@ constructor( } fun onTapped() { - statusBarKeyguardViewManager.showPrimaryBouncer(/* scrimmed */ true) + statusBarKeyguardViewManager.showPrimaryBouncer( + /* scrimmed */ true, + "AlternateBouncerUdfpsIconViewModel#onTapped", + ) } val bgColor: Flow<Int> = deviceEntryBackgroundViewModel.color diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModel.kt index cff651114c93..45f43bb484c8 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModel.kt @@ -47,7 +47,9 @@ constructor( /** Reports the alternate bouncer visible state if the scene container flag is enabled. */ val isVisible: Flow<Boolean> = - alternateBouncerInteractor.get().isVisible.onEach { SceneContainerFlag.unsafeAssertInNewMode() } + alternateBouncerInteractor.get().isVisible.onEach { + SceneContainerFlag.unsafeAssertInNewMode() + } /** Progress to a fully transitioned alternate bouncer. 1f represents fully transitioned. */ val transitionToAlternateBouncerProgress: Flow<Float> = @@ -63,7 +65,10 @@ constructor( transitionToAlternateBouncerProgress.map { it == 1f }.distinctUntilChanged() fun onTapped() { - statusBarKeyguardViewManager.showPrimaryBouncer(/* scrimmed */ true) + statusBarKeyguardViewManager.showPrimaryBouncer( + /* scrimmed */ true, + "AlternateBouncerViewModel#onTapped", + ) } fun onRemovedFromWindow() { diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt index 13cd5839e1c8..9b4bd67f227e 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DeviceEntryIconViewModel.kt @@ -257,7 +257,9 @@ constructor( if (SceneContainerFlag.isEnabled) { deviceEntryInteractor.attemptDeviceEntry() } else { - keyguardViewController.get().showPrimaryBouncer(/* scrim */ true) + keyguardViewController + .get() + .showPrimaryBouncer(/* scrim */ true, "DeviceEntryIconViewModel#onUserInteraction") } deviceEntrySourceInteractor.attemptEnterDeviceFromDeviceEntryIcon() } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt index 9312bca04994..a0458f0172f5 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToPrimaryBouncerTransitionViewModel.kt @@ -64,7 +64,7 @@ constructor( val shortcutsAlpha: Flow<Float> = transitionAnimation.sharedFlow( - duration = FromLockscreenTransitionInteractor.TO_PRIMARY_BOUNCER_DURATION, + duration = 200.milliseconds, onStep = alphaForAnimationStep, // Rapid swipes to bouncer, and may end up skipping intermediate values that would've // caused a complete fade out of lockscreen elements. Ensure it goes to 0f. diff --git a/packages/SystemUI/src/com/android/systemui/media/remedia/ui/compose/DismissibleHorizontalPager.kt b/packages/SystemUI/src/com/android/systemui/media/remedia/ui/compose/DismissibleHorizontalPager.kt deleted file mode 100644 index 8df916fe6969..000000000000 --- a/packages/SystemUI/src/com/android/systemui/media/remedia/ui/compose/DismissibleHorizontalPager.kt +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright (C) 2025 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS 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.media.remedia.ui.compose - -import androidx.compose.animation.core.Animatable -import androidx.compose.animation.core.AnimationVector1D -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.BoxScope -import androidx.compose.foundation.pager.HorizontalPager -import androidx.compose.foundation.pager.PagerScope -import androidx.compose.foundation.pager.PagerState -import androidx.compose.foundation.pager.rememberPagerState -import androidx.compose.runtime.Composable -import androidx.compose.runtime.remember -import androidx.compose.runtime.rememberCoroutineScope -import androidx.compose.ui.Modifier -import androidx.compose.ui.geometry.Offset -import androidx.compose.ui.graphics.graphicsLayer -import androidx.compose.ui.input.nestedscroll.NestedScrollConnection -import androidx.compose.ui.input.nestedscroll.NestedScrollSource -import androidx.compose.ui.input.nestedscroll.nestedScroll -import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.Velocity -import androidx.compose.ui.unit.dp -import com.android.compose.modifiers.thenIf -import kotlinx.coroutines.launch - -/** State for a [DismissibleHorizontalPager] */ -class DismissibleHorizontalPagerState( - val isDismissible: Boolean, - val isScrollingEnabled: Boolean, - val pagerState: PagerState, - val offset: Animatable<Float, AnimationVector1D>, -) - -/** - * Returns a remembered [DismissibleHorizontalPagerState] that starts at [initialPage] and has - * [pageCount] total pages. - */ -@Composable -fun rememberDismissibleHorizontalPagerState( - isDismissible: Boolean = true, - isScrollingEnabled: Boolean = true, - initialPage: Int = 0, - pageCount: () -> Int, -): DismissibleHorizontalPagerState { - val pagerState = rememberPagerState(initialPage = initialPage, pageCount = pageCount) - val offset = remember { Animatable(0f) } - - return remember(isDismissible, isScrollingEnabled, pagerState, offset) { - DismissibleHorizontalPagerState( - isDismissible = isDismissible, - isScrollingEnabled = isScrollingEnabled, - pagerState = pagerState, - offset = offset, - ) - } -} - -/** - * A [HorizontalPager] that can be swiped-away to dismiss by the user when swiped farther left or - * right once fully scrolled to the left-most or right-most page, respectively. - */ -@Composable -fun DismissibleHorizontalPager( - state: DismissibleHorizontalPagerState, - onDismissed: () -> Unit, - modifier: Modifier = Modifier, - key: ((Int) -> Any)? = null, - pageSpacing: Dp = 0.dp, - isFalseTouchDetected: Boolean, - indicator: @Composable BoxScope.() -> Unit, - pageContent: @Composable PagerScope.(page: Int) -> Unit, -) { - val scope = rememberCoroutineScope() - - val nestedScrollConnection = remember { - object : NestedScrollConnection { - override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset { - return if (state.offset.value > 0f && available.x < 0f) { - scope.launch { state.offset.snapTo(state.offset.value + available.x) } - Offset(available.x, 0f) - } else if (state.offset.value < 0f && available.x > 0f) { - scope.launch { state.offset.snapTo(state.offset.value + available.x) } - Offset(available.x, 0f) - } else { - Offset.Zero - } - } - - override fun onPostScroll( - consumed: Offset, - available: Offset, - source: NestedScrollSource, - ): Offset { - return if (available.x > 0f) { - scope.launch { state.offset.snapTo(state.offset.value + available.x) } - Offset(available.x, 0f) - } else if (available.x < 0f) { - scope.launch { state.offset.snapTo(state.offset.value + available.x) } - Offset(available.x, 0f) - } else { - Offset.Zero - } - } - - override suspend fun onPostFling(consumed: Velocity, available: Velocity): Velocity { - scope.launch { - state.offset.animateTo( - if (state.offset.value >= state.pagerState.layoutInfo.pageSize / 2f) { - state.pagerState.layoutInfo.pageSize * 2f - } else if ( - state.offset.value <= -state.pagerState.layoutInfo.pageSize / 2f - ) { - -state.pagerState.layoutInfo.pageSize * 2f - } else { - 0f - } - ) - if (state.offset.value != 0f) { - onDismissed() - } - } - return super.onPostFling(consumed, available) - } - } - } - - Box(modifier = modifier) { - HorizontalPager( - state = state.pagerState, - userScrollEnabled = state.isScrollingEnabled && !isFalseTouchDetected, - key = key, - pageSpacing = pageSpacing, - pageContent = pageContent, - modifier = - Modifier.thenIf(state.isDismissible) { - Modifier.nestedScroll(nestedScrollConnection).graphicsLayer { - translationX = state.offset.value - } - }, - ) - - indicator() - } -} diff --git a/packages/SystemUI/src/com/android/systemui/media/remedia/ui/compose/Media.kt b/packages/SystemUI/src/com/android/systemui/media/remedia/ui/compose/Media.kt index f07238895aa5..d6d185195c51 100644 --- a/packages/SystemUI/src/com/android/systemui/media/remedia/ui/compose/Media.kt +++ b/packages/SystemUI/src/com/android/systemui/media/remedia/ui/compose/Media.kt @@ -33,6 +33,7 @@ import androidx.compose.animation.graphics.vector.AnimatedImageVector import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.Canvas import androidx.compose.foundation.Image +import androidx.compose.foundation.OverscrollEffect import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.combinedClickable @@ -54,6 +55,8 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size +import androidx.compose.foundation.pager.HorizontalPager +import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.ButtonDefaults @@ -107,7 +110,9 @@ import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.Constraints import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.DpSize import androidx.compose.ui.unit.dp +import androidx.compose.ui.util.fastCoerceIn import androidx.compose.ui.util.fastForEach import androidx.compose.ui.util.fastForEachIndexed import androidx.compose.ui.util.fastRoundToInt @@ -120,6 +125,7 @@ import com.android.compose.animation.scene.SceneKey import com.android.compose.animation.scene.SceneTransitionLayout import com.android.compose.animation.scene.rememberMutableSceneTransitionLayoutState import com.android.compose.animation.scene.transitions +import com.android.compose.gesture.effect.rememberOffsetOverscrollEffect import com.android.compose.ui.graphics.painter.rememberDrawablePainter import com.android.systemui.common.shared.model.Icon import com.android.systemui.common.ui.compose.Icon @@ -137,7 +143,9 @@ import com.android.systemui.media.remedia.ui.viewmodel.MediaNavigationViewModel import com.android.systemui.media.remedia.ui.viewmodel.MediaOutputSwitcherChipViewModel import com.android.systemui.media.remedia.ui.viewmodel.MediaPlayPauseActionViewModel import com.android.systemui.media.remedia.ui.viewmodel.MediaSecondaryActionViewModel +import com.android.systemui.media.remedia.ui.viewmodel.MediaSettingsButtonViewModel import com.android.systemui.media.remedia.ui.viewmodel.MediaViewModel +import kotlin.math.abs import kotlin.math.max import kotlin.math.min @@ -208,43 +216,16 @@ private fun CardCarouselContent( onDismissed: () -> Unit, modifier: Modifier = Modifier, ) { - val pagerState = - rememberDismissibleHorizontalPagerState( - isDismissible = behavior.isCarouselDismissible, - isScrollingEnabled = behavior.isCarouselScrollingEnabled, - ) { - viewModel.cards.size - } + val pagerState = rememberPagerState { viewModel.cards.size } + LaunchedEffect(pagerState.currentPage) { viewModel.onCardSelected(pagerState.currentPage) } + var isFalseTouchDetected: Boolean by remember(behavior.isCarouselScrollFalseTouch) { mutableStateOf(false) } + val isSwipingEnabled = behavior.isCarouselScrollingEnabled && !isFalseTouchDetected val roundedCornerShape = RoundedCornerShape(32.dp) - LaunchedEffect(pagerState.pagerState.currentPage) { - viewModel.onCardSelected(pagerState.pagerState.currentPage) - } - - DismissibleHorizontalPager( - state = pagerState, - onDismissed = onDismissed, - pageSpacing = 8.dp, - key = { index -> viewModel.cards[index].key }, - indicator = { - if (pagerState.pagerState.pageCount > 1) { - PagerDots( - pagerState = pagerState.pagerState, - activeColor = Color(0xffdee0ff), - nonActiveColor = Color(0xffa7a9ca), - dotSize = 6.dp, - spaceSize = 6.dp, - modifier = - Modifier.align(Alignment.BottomCenter).padding(8.dp).graphicsLayer { - translationX = pagerState.offset.value - }, - ) - } - }, - isFalseTouchDetected = isFalseTouchDetected, + Box( modifier = modifier.padding(8.dp).clip(roundedCornerShape).pointerInput(behavior) { if (behavior.isCarouselScrollFalseTouch != null) { @@ -253,13 +234,54 @@ private fun CardCarouselContent( isFalseTouchDetected = behavior.isCarouselScrollFalseTouch.invoke() } } - }, - ) { index -> - Card( - viewModel = viewModel.cards[index], - presentationStyle = presentationStyle, - modifier = Modifier.clip(roundedCornerShape), - ) + } + ) { + @Composable + fun PagerContent(overscrollEffect: OverscrollEffect? = null) { + Box { + HorizontalPager( + state = pagerState, + userScrollEnabled = isSwipingEnabled, + pageSpacing = 8.dp, + key = { index: Int -> viewModel.cards[index].key }, + overscrollEffect = overscrollEffect ?: rememberOffsetOverscrollEffect(), + ) { pageIndex: Int -> + Card( + viewModel = viewModel.cards[pageIndex], + presentationStyle = presentationStyle, + modifier = Modifier.clip(roundedCornerShape), + ) + } + + if (pagerState.pageCount > 1) { + PagerDots( + pagerState = pagerState, + activeColor = Color(0xffdee0ff), + nonActiveColor = Color(0xffa7a9ca), + dotSize = 6.dp, + spaceSize = 6.dp, + modifier = Modifier.align(Alignment.BottomCenter).padding(8.dp), + ) + } + } + } + + if (behavior.isCarouselDismissible) { + SwipeToDismiss(content = { PagerContent() }, onDismissed = onDismissed) + } else { + val overscrollEffect = rememberOffsetOverscrollEffect() + SwipeToReveal( + foregroundContent = { PagerContent(overscrollEffect) }, + foregroundContentEffect = overscrollEffect, + revealedContent = { revealAmount -> + RevealedContent( + viewModel = viewModel.settingsButtonViewModel, + revealAmount = revealAmount, + ) + }, + isSwipingEnabled = isSwipingEnabled, + ) + } } } @@ -496,16 +518,19 @@ private fun ContentScope.CardForegroundContent( modifier = Modifier.weight(1f).padding(end = 8.dp), ) + val playPauseSize = DpSize(width = 48.dp, height = 48.dp) if (viewModel.actionButtonLayout == MediaCardActionButtonLayout.WithPlayPause) { AnimatedVisibility(visible = viewModel.playPauseAction != null) { PlayPauseAction( viewModel = checkNotNull(viewModel.playPauseAction), - buttonWidth = 48.dp, + buttonSize = playPauseSize, buttonColor = colorScheme.primary, iconColor = colorScheme.onPrimary, buttonCornerRadius = { isPlaying -> if (isPlaying) 16.dp else 48.dp }, ) } + } else { + Spacer(Modifier.size(playPauseSize)) } } @@ -565,14 +590,19 @@ private fun ContentScope.CardForegroundContent( } } - AnimatedVisibility(visible = viewModel.playPauseAction != null) { - PlayPauseAction( - viewModel = checkNotNull(viewModel.playPauseAction), - buttonWidth = 48.dp, - buttonColor = colorScheme.primary, - iconColor = colorScheme.onPrimary, - buttonCornerRadius = { isPlaying -> if (isPlaying) 16.dp else 48.dp }, - ) + val playPauseSize = DpSize(width = 48.dp, height = 48.dp) + if (viewModel.actionButtonLayout == MediaCardActionButtonLayout.WithPlayPause) { + AnimatedVisibility(visible = viewModel.playPauseAction != null) { + PlayPauseAction( + viewModel = checkNotNull(viewModel.playPauseAction), + buttonSize = playPauseSize, + buttonColor = colorScheme.primary, + iconColor = colorScheme.onPrimary, + buttonCornerRadius = { isPlaying -> if (isPlaying) 16.dp else 48.dp }, + ) + } + } else { + Spacer(Modifier.size(playPauseSize)) } } } @@ -628,7 +658,7 @@ private fun ContentScope.CompactCardForeground( AnimatedVisibility(visible = viewModel.playPauseAction != null) { PlayPauseAction( viewModel = checkNotNull(viewModel.playPauseAction), - buttonWidth = 72.dp, + buttonSize = DpSize(width = 72.dp, height = 48.dp), buttonColor = MaterialTheme.colorScheme.primaryContainer, iconColor = MaterialTheme.colorScheme.onPrimaryContainer, buttonCornerRadius = { isPlaying -> if (isPlaying) 16.dp else 24.dp }, @@ -1084,7 +1114,7 @@ private fun OutputSwitcherChip( @Composable private fun ContentScope.PlayPauseAction( viewModel: MediaPlayPauseActionViewModel, - buttonWidth: Dp, + buttonSize: DpSize, buttonColor: Color, iconColor: Color, buttonCornerRadius: (isPlaying: Boolean) -> Dp, @@ -1102,7 +1132,7 @@ private fun ContentScope.PlayPauseAction( enabled = viewModel.onClick != null, colors = ButtonDefaults.buttonColors(containerColor = buttonColor), shape = RoundedCornerShape(cornerRadius), - modifier = Modifier.size(width = buttonWidth, height = 48.dp), + modifier = Modifier.size(buttonSize), ) { when (viewModel.state) { is MediaSessionState.Playing, @@ -1186,6 +1216,65 @@ private fun SecondaryActionContent( } } +/** + * Renders the revealed content on the sides of the horizontal pager. + * + * @param revealAmount A callback that can return the amount of revealing done. This value will be + * in a range slightly larger than `-1` to `+1` where `1` is fully revealed on the left-hand side, + * `-1` is fully revealed on the right-hand side, and `0` is not revealed at all. Numbers lower + * than `-1` or greater than `1` are possible when the overscroll effect adds additional pixels of + * offset. + */ +@Composable +private fun RevealedContent( + viewModel: MediaSettingsButtonViewModel, + revealAmount: () -> Float, + modifier: Modifier = Modifier, +) { + val horizontalPadding = 18.dp + + // This custom layout's purpose is only to place the icon in the center of the revealed content, + // taking into account the amount of reveal. + Layout( + content = { + Icon( + icon = viewModel.icon, + modifier = + Modifier.size(48.dp) + .padding(12.dp) + .graphicsLayer { + alpha = abs(revealAmount()).fastCoerceIn(0f, 1f) + rotationZ = revealAmount() * 90 + } + .clickable { viewModel.onClick() }, + ) + }, + modifier = modifier, + ) { measurables, constraints -> + check(measurables.size == 1) + val placeable = measurables[0].measure(constraints) + val totalWidth = + min(horizontalPadding.roundToPx() * 2 + placeable.measuredWidth, constraints.maxWidth) + + layout(totalWidth, constraints.maxHeight) { + coordinates?.size?.let { size -> + val reveal = revealAmount() + val x = + if (reveal >= 0f) { + ((size.width * abs(reveal)) - placeable.measuredWidth) / 2 + } else { + size.width * (1 - abs(reveal) / 2) - placeable.measuredWidth / 2 + } + + placeable.place( + x = x.fastRoundToInt(), + y = (size.height - placeable.measuredHeight) / 2, + ) + } + } + } +} + /** Enumerates all supported media presentation styles. */ enum class MediaPresentationStyle { /** The "normal" 3-row carousel look. */ diff --git a/packages/SystemUI/src/com/android/systemui/media/remedia/ui/compose/SwipeToDismiss.kt b/packages/SystemUI/src/com/android/systemui/media/remedia/ui/compose/SwipeToDismiss.kt new file mode 100644 index 000000000000..b80bf4143252 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/media/remedia/ui/compose/SwipeToDismiss.kt @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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.media.remedia.ui.compose + +import androidx.compose.animation.core.Animatable +import androidx.compose.foundation.OverscrollEffect +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.offset +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.Modifier +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.input.nestedscroll.NestedScrollConnection +import androidx.compose.ui.input.nestedscroll.NestedScrollSource +import androidx.compose.ui.input.nestedscroll.nestedScroll +import androidx.compose.ui.layout.onSizeChanged +import androidx.compose.ui.unit.IntOffset +import androidx.compose.ui.unit.Velocity +import androidx.compose.ui.util.fastRoundToInt +import kotlinx.coroutines.launch + +/** Swipe to dismiss that supports nested scrolling. */ +@Composable +fun SwipeToDismiss( + content: @Composable (overscrollEffect: OverscrollEffect?) -> Unit, + onDismissed: () -> Unit, + modifier: Modifier = Modifier, +) { + val scope = rememberCoroutineScope() + val offsetAnimatable = remember { Animatable(0f) } + + // This is the width of the revealed content UI box. It's not a state because it's not + // observed in any composition and is an object with a value to avoid the extra cost + // associated with boxing and unboxing an int. + val revealedContentBoxWidth = remember { + object { + var value = 0 + } + } + + val nestedScrollConnection = remember { + object : NestedScrollConnection { + override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset { + return if (offsetAnimatable.value > 0f && available.x < 0f) { + scope.launch { offsetAnimatable.snapTo(offsetAnimatable.value + available.x) } + Offset(available.x, 0f) + } else if (offsetAnimatable.value < 0f && available.x > 0f) { + scope.launch { offsetAnimatable.snapTo(offsetAnimatable.value + available.x) } + Offset(available.x, 0f) + } else { + Offset.Zero + } + } + + override fun onPostScroll( + consumed: Offset, + available: Offset, + source: NestedScrollSource, + ): Offset { + return if (available.x > 0f) { + scope.launch { offsetAnimatable.snapTo(offsetAnimatable.value + available.x) } + Offset(available.x, 0f) + } else if (available.x < 0f) { + scope.launch { offsetAnimatable.snapTo(offsetAnimatable.value + available.x) } + Offset(available.x, 0f) + } else { + Offset.Zero + } + } + + override suspend fun onPostFling(consumed: Velocity, available: Velocity): Velocity { + scope.launch { + offsetAnimatable.animateTo( + if (offsetAnimatable.value >= revealedContentBoxWidth.value / 2f) { + revealedContentBoxWidth.value * 2f + } else if (offsetAnimatable.value <= -revealedContentBoxWidth.value / 2f) { + -revealedContentBoxWidth.value * 2f + } else { + 0f + } + ) + if (offsetAnimatable.value != 0f) { + onDismissed() + } + } + return super.onPostFling(consumed, available) + } + } + } + + Box( + modifier = + modifier + .onSizeChanged { revealedContentBoxWidth.value = it.width } + .nestedScroll(nestedScrollConnection) + .offset { IntOffset(x = offsetAnimatable.value.fastRoundToInt(), y = 0) } + ) { + content(null) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/media/remedia/ui/compose/SwipeToReveal.kt b/packages/SystemUI/src/com/android/systemui/media/remedia/ui/compose/SwipeToReveal.kt new file mode 100644 index 000000000000..770762c7a29f --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/media/remedia/ui/compose/SwipeToReveal.kt @@ -0,0 +1,264 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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.media.remedia.ui.compose + +import androidx.compose.animation.core.Animatable +import androidx.compose.foundation.OverscrollEffect +import androidx.compose.foundation.gestures.Orientation +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.BoxScope +import androidx.compose.foundation.layout.absoluteOffset +import androidx.compose.foundation.layout.offset +import androidx.compose.foundation.overscroll +import androidx.compose.foundation.withoutVisualEffect +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.input.pointer.PointerType +import androidx.compose.ui.layout.Layout +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.unit.IntOffset +import androidx.compose.ui.util.fastCoerceIn +import androidx.compose.ui.util.fastRoundToInt +import com.android.compose.gesture.NestedDraggable +import com.android.compose.gesture.effect.OffsetOverscrollEffect +import com.android.compose.gesture.effect.rememberOffsetOverscrollEffect +import com.android.compose.gesture.nestedDraggable +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch + +/** + * Swipe to reveal that supports nested scrolling and an overscroll effect. + * + * @param foregroundContent The content to show above all else; this is the content that can be + * swiped sideways to reveal the [revealedContent]. This may contain a horizontally-scrollable + * component (for example a `HorizontalPager`). + * @param revealedContent The content that is shown below the [foregroundContent]; this is the + * content that can be revealed by swiping the [foregroundContent] sideways. + */ +@Composable +fun SwipeToReveal( + foregroundContent: @Composable (overscrollEffect: OverscrollEffect?) -> Unit, + foregroundContentEffect: OffsetOverscrollEffect, + revealedContent: @Composable BoxScope.(revealAmount: () -> Float) -> Unit, + isSwipingEnabled: Boolean, + modifier: Modifier = Modifier, +) { + // This composable supports an overscroll effect, to make it possible for the user to + // "stretch" the UI when the side is fully revealed but the user keeps trying to reveal it + // further. + val revealedContentEffect = rememberOffsetOverscrollEffect() + + // This is the width of the revealed content UI box. It's not a state because it's not + // observed in any composition and is an object with a value to avoid the extra cost + // associated with boxing and unboxing an int. + val revealedContentBoxWidth = remember { + object { + var value = 0 + } + } + + // In order to support the drag to reveal, infrastructure has to be put in place where a + // NestedDraggable helps by consuming the unconsumed drags and flings and applying the + // overscroll visual effect. + // + // This is the NestedDraggalbe controller. + val revealedContentDragController = rememberRevealedContentDragController { + revealedContentBoxWidth.value.toFloat() + } + + Box( + modifier = + modifier + .nestedDraggable( + enabled = isSwipingEnabled, + draggable = + remember { + object : NestedDraggable { + override fun onDragStarted( + position: Offset, + sign: Float, + pointersDown: Int, + pointerType: PointerType?, + ): NestedDraggable.Controller { + return revealedContentDragController + } + + override fun shouldConsumeNestedPostScroll(sign: Float): Boolean { + return revealedContentDragController.shouldConsumePostScrolls( + sign + ) + } + + override fun shouldConsumeNestedPreScroll(sign: Float): Boolean { + return revealedContentDragController.shouldConsumePreScrolls( + sign + ) + } + } + }, + orientation = Orientation.Horizontal, + overscrollEffect = revealedContentEffect.withoutVisualEffect(), + ) + .overscroll(revealedContentEffect) + ) { + val density = LocalDensity.current + + /** + * Returns the amount of visual offset, in pixels, that is comprised of both the offset from + * dragging and the overscroll effect's additional pixels after applying its animation curve + * on the raw distance. + */ + fun offsetWithOverscroll(): Float { + return revealedContentDragController.offset + + OffsetOverscrollEffect.computeOffset( + density, + foregroundContentEffect.overscrollDistance, + ) + + OffsetOverscrollEffect.computeOffset( + density, + revealedContentEffect.overscrollDistance, + ) + } + + /** + * Returns the ratio of the amount by which the revealed content is revealed, where: + * - `0` means none of it is revealed + * - `+1` means all of it is revealed to the start of the foreground content + * - `-1` means all of it is revealed to the end of the foreground content + * + * The number could be smaller than `-1` or larger than `+1` to model overscrolling. + */ + fun revealAmount(): Float { + return (offsetWithOverscroll() / revealedContentBoxWidth.value) + } + + Layout( + content = { revealedContent { revealAmount() } }, + modifier = Modifier.matchParentSize(), + ) { measurables, constraints -> + check(measurables.size == 1) + val placeable = measurables[0].measure(constraints.copy(minWidth = 0, minHeight = 0)) + // Keep revealedContentBoxWidth up to date with the latest value. + revealedContentBoxWidth.value = placeable.measuredWidth + + // Place the revealed content on the correct side, depending on the direction of the + // reveal. + val alignedToStart = revealAmount() >= 0f + layout(constraints.maxWidth, constraints.maxHeight) { + coordinates?.size?.let { size -> + placeable.place( + x = if (alignedToStart) 0 else size.width - placeable.measuredWidth, + y = 0, + ) + } + } + } + + Box( + modifier = + Modifier.absoluteOffset { + IntOffset(revealedContentDragController.offset.fastRoundToInt(), y = 0) + } + ) { + foregroundContent(foregroundContentEffect) + } + } +} + +@Composable +private fun rememberRevealedContentDragController( + maxBound: () -> Float +): RevealedContentDragController { + val scope = rememberCoroutineScope() + return remember { RevealedContentDragController(scope = scope, maxBound = maxBound) } +} + +private class RevealedContentDragController( + private val scope: CoroutineScope, + private val maxBound: () -> Float, +) : NestedDraggable.Controller { + private val offsetAnimatable = Animatable(0f) + private var lastTarget = 0f + private var range = 0f..1f + private var shouldConsumePreScrolls by mutableStateOf(false) + + override val autoStopNestedDrags: Boolean + get() = true + + val offset: Float + get() = offsetAnimatable.value + + fun shouldConsumePreScrolls(sign: Float): Boolean { + if (!shouldConsumePreScrolls) return false + + if (lastTarget > 0f && sign < 0f) { + range = 0f..maxBound() + return true + } + + if (lastTarget < 0f && sign > 0f) { + range = -maxBound()..0f + return true + } + + return false + } + + fun shouldConsumePostScrolls(sign: Float): Boolean { + val max = maxBound() + if (sign > 0f && lastTarget < max) { + range = 0f..maxBound() + return true + } + + if (sign < 0f && lastTarget > -max) { + range = -maxBound()..0f + return true + } + + return false + } + + override fun onDrag(delta: Float): Float { + val previousTarget = lastTarget + lastTarget = (lastTarget + delta).fastCoerceIn(range.start, range.endInclusive) + val newTarget = lastTarget + scope.launch { offsetAnimatable.snapTo(newTarget) } + return lastTarget - previousTarget + } + + override suspend fun onDragStopped(velocity: Float, awaitFling: suspend () -> Unit): Float { + val rangeMiddle = range.start + (range.endInclusive - range.start) / 2f + lastTarget = + when { + lastTarget >= rangeMiddle -> range.endInclusive + else -> range.start + } + + shouldConsumePreScrolls = lastTarget != 0f + val newTarget = lastTarget + + scope.launch { offsetAnimatable.animateTo(newTarget) } + return velocity + } +} diff --git a/packages/SystemUI/src/com/android/systemui/media/remedia/ui/viewmodel/MediaSettingsButtonViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/remedia/ui/viewmodel/MediaSettingsButtonViewModel.kt new file mode 100644 index 000000000000..4f4c25c3c74f --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/media/remedia/ui/viewmodel/MediaSettingsButtonViewModel.kt @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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.media.remedia.ui.viewmodel + +import com.android.systemui.common.shared.model.Icon + +data class MediaSettingsButtonViewModel(val icon: Icon.Resource, val onClick: () -> Unit) diff --git a/packages/SystemUI/src/com/android/systemui/media/remedia/ui/viewmodel/MediaViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/remedia/ui/viewmodel/MediaViewModel.kt index 19b08fa212db..a57da63a7a81 100644 --- a/packages/SystemUI/src/com/android/systemui/media/remedia/ui/viewmodel/MediaViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/media/remedia/ui/viewmodel/MediaViewModel.kt @@ -252,6 +252,21 @@ constructor( } } + val settingsButtonViewModel = + MediaSettingsButtonViewModel( + icon = + Icon.Resource( + res = R.drawable.ic_settings, + contentDescription = + ContentDescription.Resource(res = R.string.controls_media_settings_button), + ), + onClick = { + falsingSystem.runIfNotFalseTap(FalsingManager.LOW_PENALTY) { + interactor.openMediaSettings() + } + }, + ) + /** Whether the carousel should be visible. */ val isCarouselVisible: Boolean get() = diff --git a/packages/SystemUI/src/com/android/systemui/model/SceneContainerPlugin.kt b/packages/SystemUI/src/com/android/systemui/model/SceneContainerPlugin.kt index 4559a7aea1a2..7b3f4c61088b 100644 --- a/packages/SystemUI/src/com/android/systemui/model/SceneContainerPlugin.kt +++ b/packages/SystemUI/src/com/android/systemui/model/SceneContainerPlugin.kt @@ -79,6 +79,7 @@ constructor( SceneContainerPluginState( scene = idleState.currentScene, overlays = idleState.currentOverlays, + isVisible = sceneInteractor.get().isVisible.value, invisibleDueToOcclusion = invisibleDueToOcclusion, ) ) @@ -100,12 +101,17 @@ constructor( mapOf<Long, (SceneContainerPluginState) -> Boolean>( SYSUI_STATE_NOTIFICATION_PANEL_VISIBLE to { - it.scene != Scenes.Gone || it.overlays.isNotEmpty() + when { + !it.isVisible -> false + it.scene != Scenes.Gone -> true + it.overlays.isNotEmpty() -> true + else -> false + } }, SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED to { when { - it.invisibleDueToOcclusion -> false + !it.isVisible -> false it.scene == Scenes.Lockscreen -> true it.scene == Scenes.Shade -> true Overlays.NotificationsShade in it.overlays -> true @@ -114,19 +120,23 @@ constructor( }, SYSUI_STATE_QUICK_SETTINGS_EXPANDED to { - it.scene == Scenes.QuickSettings || - Overlays.QuickSettingsShade in it.overlays + when { + !it.isVisible -> false + it.scene == Scenes.QuickSettings -> true + Overlays.QuickSettingsShade in it.overlays -> true + else -> false + } }, - SYSUI_STATE_BOUNCER_SHOWING to { Overlays.Bouncer in it.overlays }, + SYSUI_STATE_BOUNCER_SHOWING to { it.isVisible && Overlays.Bouncer in it.overlays }, SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING to { - it.scene == Scenes.Lockscreen && !it.invisibleDueToOcclusion + it.isVisible && it.scene == Scenes.Lockscreen }, SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING_OCCLUDED to { it.scene == Scenes.Lockscreen && it.invisibleDueToOcclusion }, - SYSUI_STATE_COMMUNAL_HUB_SHOWING to { it.scene == Scenes.Communal }, + SYSUI_STATE_COMMUNAL_HUB_SHOWING to { it.isVisible && it.scene == Scenes.Communal }, ) } @@ -134,5 +144,6 @@ constructor( val scene: SceneKey, val overlays: Set<OverlayKey>, val invisibleDueToOcclusion: Boolean, + val isVisible: Boolean, ) } diff --git a/packages/SystemUI/src/com/android/systemui/model/SysUiStateExt.kt b/packages/SystemUI/src/com/android/systemui/model/SysUiStateExt.kt index 1e18f24c9e65..195535669c7e 100644 --- a/packages/SystemUI/src/com/android/systemui/model/SysUiStateExt.kt +++ b/packages/SystemUI/src/com/android/systemui/model/SysUiStateExt.kt @@ -16,8 +16,6 @@ package com.android.systemui.model -import com.android.systemui.dagger.qualifiers.DisplayId - /** * In-bulk updates multiple flag values and commits the update. * @@ -32,16 +30,8 @@ import com.android.systemui.dagger.qualifiers.DisplayId * SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING to (sceneKey == Scenes.Lockscreen), * ) * ``` - * - * You can inject [displayId] by injecting it using: - * ``` - * @DisplayId private val displayId: Int`, - * ``` */ -fun SysUiState.updateFlags( - @DisplayId displayId: Int, - vararg flagValuePairs: Pair<Long, Boolean>, -) { +fun SysUiState.updateFlags(vararg flagValuePairs: Pair<Long, Boolean>) { flagValuePairs.forEach { (flag, enabled) -> setFlag(flag, enabled) } - commitUpdate(displayId) + commitUpdate() } 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 237ec7cfce7f..6cda192c4198 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java @@ -22,6 +22,7 @@ import static android.view.MotionEvent.TOOL_TYPE_FINGER; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_EXCLUDE_FROM_SCREEN_MAGNIFICATION; import static com.android.systemui.Flags.edgebackGestureHandlerGetRunningTasksBackground; +import static com.android.systemui.Flags.predictiveBackDelayWmTransition; import static com.android.systemui.classifier.Classifier.BACK_GESTURE; import static com.android.systemui.navigationbar.gestural.Utilities.isTrackpadThreeFingerSwipe; import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_TOUCHPAD_GESTURES_DISABLED; @@ -1182,6 +1183,9 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack return; } else if (dx > dy && dx > mTouchSlop) { if (mAllowGesture) { + if (!predictiveBackDelayWmTransition() && mBackAnimation != null) { + mBackAnimation.onThresholdCrossed(); + } if (mBackAnimation == null) { pilferPointers(); } @@ -1197,7 +1201,8 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack // forward touch mEdgeBackPlugin.onMotionEvent(ev); dispatchToBackAnimation(ev); - if (mBackAnimation != null && mThresholdCrossed && !mLastFrameThresholdCrossed) { + if (predictiveBackDelayWmTransition() && mBackAnimation != null + && mThresholdCrossed && !mLastFrameThresholdCrossed) { mBackAnimation.onThresholdCrossed(); } } diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt index 8dc27bf4ac3e..080940169e46 100644 --- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt +++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt @@ -86,7 +86,10 @@ constructor( */ private fun initializeKeyGestureEventHandler() { if (useKeyGestureEventHandler()) { - inputManager.registerKeyGestureEventHandler(callbacks) + inputManager.registerKeyGestureEventHandler( + listOf(KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_NOTES), + callbacks, + ) } } @@ -156,11 +159,8 @@ constructor( controller.updateNoteTaskForCurrentUserAndManagedProfiles() } - override fun handleKeyGestureEvent( - event: KeyGestureEvent, - focusedToken: IBinder?, - ): Boolean { - return this@NoteTaskInitializer.handleKeyGestureEvent(event) + override fun handleKeyGestureEvent(event: KeyGestureEvent, focusedToken: IBinder?) { + this@NoteTaskInitializer.handleKeyGestureEvent(event) } } @@ -202,23 +202,19 @@ constructor( return !isMultiPress && !isLongPress } - private fun handleKeyGestureEvent(event: KeyGestureEvent): Boolean { - // This method is on input hot path and should be kept lightweight. Shift all complex - // processing onto background executor wherever possible. + private fun handleKeyGestureEvent(event: KeyGestureEvent) { if (event.keyGestureType != KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_NOTES) { - return false + return } debugLog { "handleKeyGestureEvent: Received OPEN_NOTES gesture event from keycodes: " + event.keycodes.contentToString() } if (event.keycodes.size == 1 && event.keycodes[0] == KEYCODE_STYLUS_BUTTON_TAIL) { - debugLog { "Note task triggered by stylus tail button" } backgroundExecutor.execute { controller.showNoteTask(TAIL_BUTTON) } - return true + } else { + backgroundExecutor.execute { controller.showNoteTask(KEYBOARD_SHORTCUT) } } - backgroundExecutor.execute { controller.showNoteTask(KEYBOARD_SHORTCUT) } - return true } companion object { diff --git a/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt b/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt index 05a60a6db31e..9f04f69bd0bf 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/QSFragmentCompose.kt @@ -18,6 +18,9 @@ package com.android.systemui.qs.composefragment import android.annotation.SuppressLint import android.content.Context +import android.content.res.Configuration +import android.graphics.Canvas +import android.graphics.Path import android.graphics.PointF import android.graphics.Rect import android.os.Bundle @@ -35,6 +38,7 @@ import androidx.activity.setViewTreeOnBackPressedDispatcherOwner import androidx.annotation.VisibleForTesting import androidx.compose.animation.core.tween import androidx.compose.foundation.ScrollState +import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.Arrangement.spacedBy import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -48,6 +52,7 @@ import androidx.compose.foundation.layout.requiredHeightIn import androidx.compose.foundation.verticalScroll import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue @@ -70,6 +75,8 @@ import androidx.compose.ui.layout.onSizeChanged import androidx.compose.ui.layout.positionInRoot import androidx.compose.ui.layout.positionOnScreen import androidx.compose.ui.platform.ComposeView +import androidx.compose.ui.platform.LocalConfiguration +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.semantics.CustomAccessibilityAction @@ -102,6 +109,7 @@ import com.android.compose.theme.PlatformTheme import com.android.mechanics.GestureContext import com.android.systemui.Dumpable import com.android.systemui.Flags +import com.android.systemui.Flags.notificationShadeBlur import com.android.systemui.brightness.ui.compose.BrightnessSliderContainer import com.android.systemui.brightness.ui.compose.ContainerColors import com.android.systemui.compose.modifiers.sysuiResTag @@ -119,7 +127,6 @@ import com.android.systemui.qs.composefragment.SceneKeys.debugName import com.android.systemui.qs.composefragment.SceneKeys.toIdleSceneKey import com.android.systemui.qs.composefragment.ui.GridAnchor import com.android.systemui.qs.composefragment.ui.NotificationScrimClipParams -import com.android.systemui.qs.composefragment.ui.notificationScrimClip import com.android.systemui.qs.composefragment.ui.quickQuickSettingsToQuickSettings import com.android.systemui.qs.composefragment.ui.toEditMode import com.android.systemui.qs.composefragment.viewmodel.QSFragmentComposeViewModel @@ -235,7 +242,7 @@ constructor( FrameLayoutTouchPassthrough( context, { notificationScrimClippingParams.isEnabled }, - { notificationScrimClippingParams.params.top }, + snapshotFlow { notificationScrimClippingParams.params }, // Only allow scrolling when we are fully expanded. That way, we don't intercept // swipes in lockscreen (when somehow QS is receiving touches). { (scrollState.canScrollForward && viewModel.isQsFullyExpanded) || isCustomizing }, @@ -251,7 +258,7 @@ constructor( @Composable private fun Content() { - PlatformTheme { + PlatformTheme(isDarkTheme = if (notificationShadeBlur()) isSystemInDarkTheme() else true) { ProvideShortcutHelperIndication(interactionsConfig = interactionsConfig()) { // TODO(b/389985793): Make sure that there is no coroutine work or recompositions // happening when alwaysCompose is true but isQsVisibleAndAnyShadeExpanded is false. @@ -270,11 +277,6 @@ constructor( } } .graphicsLayer { alpha = viewModel.viewAlpha } - .thenIf(notificationScrimClippingParams.isEnabled) { - Modifier.notificationScrimClip { - notificationScrimClippingParams.params - } - } .thenIf(!Flags.notificationShadeBlur()) { Modifier.offset { IntOffset( @@ -746,19 +748,31 @@ constructor( Box( Modifier.systemGestureExclusionInShade( enabled = { - layoutState.transitionState is TransitionState.Idle + /* + * While we are transitioning into QS (either from QQS + * or from gone), the global position of the brightness + * slider will change in every frame. This causes + * the modifier to send a new gesture exclusion + * rectangle on every frame. Instead, only apply the + * modifier when this is settled. + */ + layoutState.transitionState is TransitionState.Idle && + viewModel.isNotTransitioning } ) ) { - BrightnessSliderContainer( - viewModel = containerViewModel.brightnessSliderViewModel, - containerColors = - ContainerColors( - Color.Transparent, - ContainerColors.defaultContainerColor, - ), - modifier = Modifier.fillMaxWidth(), - ) + AlwaysDarkMode { + BrightnessSliderContainer( + viewModel = + containerViewModel.brightnessSliderViewModel, + containerColors = + ContainerColors( + Color.Transparent, + ContainerColors.defaultContainerColor, + ), + modifier = Modifier.fillMaxWidth(), + ) + } } } val TileGrid = @@ -1043,17 +1057,75 @@ private const val EDIT_MODE_TIME_MILLIS = 500 private class FrameLayoutTouchPassthrough( context: Context, private val clippingEnabledProvider: () -> Boolean, - private val clippingTopProvider: () -> Int, + private val clippingParams: Flow<NotificationScrimClipParams>, private val canScrollForwardQs: () -> Boolean, private val emitMotionEventForFalsing: () -> Unit, ) : FrameLayout(context) { + + init { + repeatWhenAttached { + repeatOnLifecycle(Lifecycle.State.STARTED) { + clippingParams.collect { currentClipParams = it } + } + } + } + + private val currentClippingPath = Path() + private var lastWidth = -1 + set(value) { + if (field != value) { + field = value + updateClippingPath() + } + } + + private var currentClipParams = NotificationScrimClipParams() + set(value) { + if (field != value) { + field = value + updateClippingPath() + } + } + + private fun updateClippingPath() { + currentClippingPath.rewind() + if (clippingEnabledProvider()) { + val right = width + currentClipParams.rightInset + val left = -currentClipParams.leftInset + val top = currentClipParams.top + val bottom = currentClipParams.bottom + currentClippingPath.addRoundRect( + left.toFloat(), + top.toFloat(), + right.toFloat(), + bottom.toFloat(), + currentClipParams.radius.toFloat(), + currentClipParams.radius.toFloat(), + Path.Direction.CW, + ) + } + invalidate() + } + + override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) { + super.onLayout(changed, left, top, right, bottom) + lastWidth = right - left + } + + override fun dispatchDraw(canvas: Canvas) { + if (!currentClippingPath.isEmpty) { + canvas.clipOutPath(currentClippingPath) + } + super.dispatchDraw(canvas) + } + override fun isTransformedTouchPointInView( x: Float, y: Float, child: View?, outLocalPoint: PointF?, ): Boolean { - return if (clippingEnabledProvider() && y + translationY > clippingTopProvider()) { + return if (clippingEnabledProvider() && y + translationY > currentClipParams.top) { false } else { super.isTransformedTouchPointInView(x, y, child, outLocalPoint) @@ -1236,3 +1308,31 @@ private fun interactionsConfig() = private inline val alwaysCompose get() = Flags.alwaysComposeQsUiFragment() + +/** + * Forces the configuration and themes to be dark theme. This is needed in order to have + * [colorResource] retrieve the dark mode colors. + * + * This should be removed when [notificationShadeBlur] is removed + */ +@Composable +private fun AlwaysDarkMode(content: @Composable () -> Unit) { + if (notificationShadeBlur()) { + content() + } else { + val currentConfig = LocalConfiguration.current + val darkConfig = + Configuration(currentConfig).apply { + uiMode = + (uiMode and (Configuration.UI_MODE_NIGHT_MASK.inv())) or + Configuration.UI_MODE_NIGHT_YES + } + val newContext = LocalContext.current.createConfigurationContext(darkConfig) + CompositionLocalProvider( + LocalConfiguration provides darkConfig, + LocalContext provides newContext, + ) { + content() + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/composefragment/ui/NotificationScrimClip.kt b/packages/SystemUI/src/com/android/systemui/qs/composefragment/ui/NotificationScrimClip.kt deleted file mode 100644 index 3049a40f18c4..000000000000 --- a/packages/SystemUI/src/com/android/systemui/qs/composefragment/ui/NotificationScrimClip.kt +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) 2024 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.qs.composefragment.ui - -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.drawWithContent -import androidx.compose.ui.geometry.CornerRadius -import androidx.compose.ui.geometry.Offset -import androidx.compose.ui.geometry.Size -import androidx.compose.ui.graphics.BlendMode -import androidx.compose.ui.graphics.ClipOp -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.CompositingStrategy -import androidx.compose.ui.graphics.drawscope.clipRect -import androidx.compose.ui.graphics.graphicsLayer - -/** - * Clipping modifier for clipping out the notification scrim as it slides over QS. It will clip out - * ([ClipOp.Difference]) a `RoundRect(-leftInset, top, width + rightInset, bottom, radius, radius)` - * from the QS container. - */ -fun Modifier.notificationScrimClip(clipParams: () -> NotificationScrimClipParams): Modifier { - return this.graphicsLayer { compositingStrategy = CompositingStrategy.Offscreen } - .drawWithContent { - drawContent() - val params = clipParams() - val left = -params.leftInset.toFloat() - val right = size.width + params.rightInset.toFloat() - val top = params.top.toFloat() - val bottom = params.bottom.toFloat() - val clipSize = Size(right - left, bottom - top) - if (!clipSize.isEmpty()) { - clipRect { - drawRoundRect( - color = Color.Black, - cornerRadius = CornerRadius(params.radius.toFloat()), - blendMode = BlendMode.Clear, - topLeft = Offset(left, top), - size = Size(right - left, bottom - top), - ) - } - } - } -} - -/** Params for [notificationScrimClip]. */ -data class NotificationScrimClipParams( - val top: Int = 0, - val bottom: Int = 0, - val leftInset: Int = 0, - val rightInset: Int = 0, - val radius: Int = 0, -) diff --git a/packages/SystemUI/src/com/android/systemui/qs/composefragment/ui/NotificationScrimClipParams.kt b/packages/SystemUI/src/com/android/systemui/qs/composefragment/ui/NotificationScrimClipParams.kt new file mode 100644 index 000000000000..db320d3b9f1c --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/ui/NotificationScrimClipParams.kt @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.composefragment.ui + +/** Params for [notificationScrimClip]. */ +data class NotificationScrimClipParams( + val top: Int = 0, + val bottom: Int = 0, + val leftInset: Int = 0, + val rightInset: Int = 0, + val radius: Int = 0, +) diff --git a/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt index b61fa9cfe264..b7e9c5296bc9 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/composefragment/viewmodel/QSFragmentComposeViewModel.kt @@ -312,6 +312,11 @@ constructor( source = containerViewModel.editModeViewModel.isEditing, ) + /** True if we are not in an expansion (from Gone to QQS/QS) animation. */ + val isNotTransitioning by derivedStateOf { + viewTranslationY == 0f && viewAlpha == 1f && constrainedSquishinessFraction == 1f + } + private val inFirstPage: Boolean get() = inFirstPageViewModel.inFirstPage diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/DragAndDropState.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/DragAndDropState.kt index 405ce8a8e5e0..005c8b26b0d8 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/DragAndDropState.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/DragAndDropState.kt @@ -35,7 +35,6 @@ import androidx.compose.ui.draganddrop.mimeTypes import androidx.compose.ui.draganddrop.toAndroidDragEvent import androidx.compose.ui.geometry.Offset import androidx.compose.ui.unit.IntRect -import androidx.compose.ui.unit.center import androidx.compose.ui.unit.toRect import com.android.systemui.qs.panels.shared.model.SizedTile import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModel @@ -44,6 +43,7 @@ import com.android.systemui.qs.pipeline.shared.TileSpec /** Holds the [TileSpec] of the tile being moved and receives drag and drop events. */ interface DragAndDropState { val draggedCell: SizedTile<EditTileViewModel>? + val isDraggedCellRemovable: Boolean val draggedPosition: Offset val dragInProgress: Boolean val dragType: DragType? @@ -76,7 +76,7 @@ enum class DragType { @Composable fun Modifier.dragAndDropRemoveZone( dragAndDropState: DragAndDropState, - onDrop: (TileSpec) -> Unit, + onDrop: (TileSpec, removalEnabled: Boolean) -> Unit, ): Modifier { val target = remember(dragAndDropState) { @@ -87,13 +87,15 @@ fun Modifier.dragAndDropRemoveZone( override fun onDrop(event: DragAndDropEvent): Boolean { return dragAndDropState.draggedCell?.let { - onDrop(it.tile.tileSpec) + onDrop(it.tile.tileSpec, dragAndDropState.isDraggedCellRemovable) dragAndDropState.onDrop() true } ?: false } override fun onEntered(event: DragAndDropEvent) { + if (!dragAndDropState.isDraggedCellRemovable) return + dragAndDropState.movedOutOfBounds() } } @@ -168,10 +170,10 @@ private fun DragAndDropEvent.toOffset(): Offset { } private fun insertAfter(item: LazyGridItemInfo, offset: Offset): Boolean { - // We want to insert the tile after the target if we're aiming at the right side of a large tile + // We want to insert the tile after the target if we're aiming at the end of a large tile // TODO(ostonge): Verify this behavior in RTL - val itemCenter = item.offset + item.size.center - return item.span != 1 && offset.x > itemCenter.x + val itemCenter = item.offset.x + item.size.width * .75 + return item.span != 1 && offset.x > itemCenter } @OptIn(ExperimentalFoundationApi::class) diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditTileListState.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditTileListState.kt index 868855840922..70f1674acd3b 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditTileListState.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/EditTileListState.kt @@ -25,6 +25,7 @@ import androidx.compose.runtime.snapshots.SnapshotStateList import androidx.compose.runtime.toMutableStateList import androidx.compose.ui.geometry.Offset import com.android.systemui.qs.panels.shared.model.SizedTile +import com.android.systemui.qs.panels.ui.compose.selection.PlacementEvent import com.android.systemui.qs.panels.ui.model.GridCell import com.android.systemui.qs.panels.ui.model.TileGridCell import com.android.systemui.qs.panels.ui.model.toGridCells @@ -60,6 +61,11 @@ class EditTileListState( override var dragType by mutableStateOf<DragType?>(null) private set + // A dragged cell can be removed if it was added in the drag movement OR if it's marked as + // removable + override val isDraggedCellRemovable: Boolean + get() = dragType == DragType.Add || draggedCell?.tile?.isRemovable ?: false + override val dragInProgress: Boolean get() = draggedCell != null @@ -76,10 +82,16 @@ class EditTileListState( return _tiles.indexOfFirst { it is TileGridCell && it.tile.tileSpec == tileSpec } } + fun isRemovable(tileSpec: TileSpec): Boolean { + return _tiles.find { + it is TileGridCell && it.tile.tileSpec == tileSpec && it.tile.isRemovable + } != null + } + /** Resize the tile corresponding to the [TileSpec] to [toIcon] */ fun resizeTile(tileSpec: TileSpec, toIcon: Boolean) { val fromIndex = indexOf(tileSpec) - if (fromIndex != -1) { + if (fromIndex != INVALID_INDEX) { val cell = _tiles[fromIndex] as TileGridCell if (cell.isIcon == toIcon) return @@ -97,9 +109,6 @@ class EditTileListState( override fun onStarted(cell: SizedTile<EditTileViewModel>, dragType: DragType) { draggedCell = cell this.dragType = dragType - - // Add spacers to the grid to indicate where the user can move a tile - regenerateGrid() } override fun onTargeting(target: Int, insertAfter: Boolean) { @@ -111,7 +120,7 @@ class EditTileListState( } val insertionIndex = if (insertAfter) target + 1 else target - if (fromIndex != -1) { + if (fromIndex != INVALID_INDEX) { val cell = _tiles.removeAt(fromIndex) regenerateGrid() _tiles.add(insertionIndex.coerceIn(0, _tiles.size), cell) @@ -149,6 +158,43 @@ class EditTileListState( regenerateGrid() } + /** + * Return the appropriate index to move the tile to for the placement [event] + * + * The grid includes spacers. As a result, indexes from the grid need to be translated to the + * corresponding index from [currentTileSpecs]. + */ + fun targetIndexForPlacement(event: PlacementEvent): Int { + val currentTileSpecs = tileSpecs() + return when (event) { + is PlacementEvent.PlaceToTileSpec -> { + currentTileSpecs.indexOf(event.targetSpec) + } + is PlacementEvent.PlaceToIndex -> { + if (event.targetIndex >= _tiles.size) { + currentTileSpecs.size + } else if (event.targetIndex <= 0) { + 0 + } else { + // The index may point to a spacer, so first find the first tile located + // after index, then use its position as a target + val targetTile = + _tiles.subList(event.targetIndex, _tiles.size).firstOrNull { + it is TileGridCell + } as? TileGridCell + + if (targetTile == null) { + currentTileSpecs.size + } else { + val targetIndex = currentTileSpecs.indexOf(targetTile.tile.tileSpec) + val fromIndex = currentTileSpecs.indexOf(event.movingSpec) + if (fromIndex < targetIndex) targetIndex - 1 else targetIndex + } + } + } + } + } + /** Regenerate the list of [GridCell] with their new potential rows */ private fun regenerateGrid() { _tiles.filterIsInstance<TileGridCell>().toGridCells(columns).let { @@ -170,4 +216,8 @@ class EditTileListState( _tiles.addAll(it) } } + + companion object { + const val INVALID_INDEX = -1 + } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/TileDetails.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/TileDetails.kt index 1176095fbb1c..f3ed07a8a753 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/TileDetails.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/TileDetails.kt @@ -46,6 +46,8 @@ import com.android.systemui.bluetooth.qsdialog.BluetoothDetailsViewModel import com.android.systemui.plugins.qs.TileDetailsViewModel import com.android.systemui.qs.flags.QsDetailedView import com.android.systemui.qs.panels.ui.viewmodel.DetailsViewModel +import com.android.systemui.qs.tiles.dialog.CastDetailsContent +import com.android.systemui.qs.tiles.dialog.CastDetailsViewModel import com.android.systemui.qs.tiles.dialog.InternetDetailsContent import com.android.systemui.qs.tiles.dialog.InternetDetailsViewModel import com.android.systemui.qs.tiles.dialog.ModesDetailsContent @@ -155,6 +157,7 @@ private fun MapTileDetailsContent(tileDetailsViewModel: TileDetailsViewModel) { is BluetoothDetailsViewModel -> BluetoothDetailsContent(tileDetailsViewModel.detailsContentViewModel) is ModesDetailsViewModel -> ModesDetailsContent(tileDetailsViewModel) + is CastDetailsViewModel -> CastDetailsContent(tileDetailsViewModel) } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt index 46f05d0ac895..f8eaa6c3bcfb 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/EditTile.kt @@ -19,6 +19,7 @@ package com.android.systemui.qs.panels.ui.compose.infinitegrid import androidx.compose.animation.AnimatedContent +import androidx.compose.animation.animateColorAsState import androidx.compose.animation.animateContentSize import androidx.compose.animation.core.LinearEasing import androidx.compose.animation.core.animateDpAsState @@ -34,6 +35,7 @@ import androidx.compose.foundation.border import androidx.compose.foundation.clickable import androidx.compose.foundation.clipScrollableContainer import androidx.compose.foundation.gestures.Orientation +import androidx.compose.foundation.gestures.detectTapGestures import androidx.compose.foundation.layout.Arrangement.spacedBy import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.BoxScope @@ -78,6 +80,7 @@ import androidx.compose.material3.TopAppBarDefaults import androidx.compose.runtime.Composable import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.State import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.key @@ -96,6 +99,7 @@ import androidx.compose.ui.geometry.Offset import androidx.compose.ui.geometry.isSpecified import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.graphicsLayer +import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.layout.MeasureScope import androidx.compose.ui.layout.layout import androidx.compose.ui.layout.onGloballyPositioned @@ -125,6 +129,7 @@ import com.android.systemui.qs.panels.shared.model.SizedTileImpl import com.android.systemui.qs.panels.ui.compose.DragAndDropState import com.android.systemui.qs.panels.ui.compose.DragType import com.android.systemui.qs.panels.ui.compose.EditTileListState +import com.android.systemui.qs.panels.ui.compose.EditTileListState.Companion.INVALID_INDEX import com.android.systemui.qs.panels.ui.compose.dragAndDropRemoveZone import com.android.systemui.qs.panels.ui.compose.dragAndDropTileList import com.android.systemui.qs.panels.ui.compose.dragAndDropTileSource @@ -152,7 +157,6 @@ import com.android.systemui.qs.panels.ui.model.AvailableTileGridCell import com.android.systemui.qs.panels.ui.model.GridCell import com.android.systemui.qs.panels.ui.model.SpacerGridCell import com.android.systemui.qs.panels.ui.model.TileGridCell -import com.android.systemui.qs.panels.ui.viewmodel.AvailableEditActions import com.android.systemui.qs.panels.ui.viewmodel.BounceableTileViewModel import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModel import com.android.systemui.qs.pipeline.shared.TileSpec @@ -161,7 +165,6 @@ import com.android.systemui.res.R import kotlin.math.abs import kotlin.math.roundToInt import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.delay import kotlinx.coroutines.launch object TileType @@ -225,7 +228,7 @@ fun DefaultEditTileGrid( columns: Int, largeTilesSpan: Int, modifier: Modifier, - onAddTile: (TileSpec) -> Unit, + onAddTile: (TileSpec, Int) -> Unit, onRemoveTile: (TileSpec) -> Unit, onSetTiles: (List<TileSpec>) -> Unit, onResize: (TileSpec, toIcon: Boolean) -> Unit, @@ -243,6 +246,15 @@ fun DefaultEditTileGrid( null } + LaunchedEffect(selectionState.placementEvent) { + selectionState.placementEvent?.let { event -> + listState + .targetIndexForPlacement(event) + .takeIf { it != INVALID_INDEX } + ?.let { onAddTile(event.movingSpec, it) } + } + } + Scaffold( containerColor = Color.Transparent, topBar = { EditModeTopBar(onStopEditing = onStopEditing, onReset = reset) }, @@ -272,29 +284,23 @@ fun DefaultEditTileGrid( .padding(top = innerPadding.calculateTopPadding()) .clipScrollableContainer(Orientation.Vertical) .verticalScroll(scrollState) - .dragAndDropRemoveZone(listState, onRemoveTile), + .dragAndDropRemoveZone(listState) { spec, removalEnabled -> + if (removalEnabled) { + // If removal is enabled, remove the tile + onRemoveTile(spec) + } else { + // Otherwise submit the new tile ordering + onSetTiles(listState.tileSpecs()) + selectionState.select(spec) + } + }, ) { - AnimatedContent( - targetState = listState.dragInProgress || selectionState.selected, - label = "QSEditHeader", - contentAlignment = Alignment.Center, + CurrentTilesGridHeader( + listState, + selectionState, + onRemoveTile, modifier = Modifier.fillMaxWidth().heightIn(min = 48.dp), - ) { showRemoveTarget -> - EditGridHeader { - if (showRemoveTarget) { - RemoveTileTarget { - selectionState.selection?.let { - selectionState.unSelect() - onRemoveTile(it) - } - } - } else { - EditGridCenteredText( - text = stringResource(id = R.string.drag_to_rearrange_tiles) - ) - } - } - } + ) CurrentTilesGrid( listState, @@ -315,7 +321,7 @@ fun DefaultEditTileGrid( // Using the fully qualified name here as a workaround for AnimatedVisibility // not being available from a Box androidx.compose.animation.AnimatedVisibility( - visible = !listState.dragInProgress, + visible = !listState.dragInProgress && !selectionState.placementEnabled, enter = fadeIn(), exit = fadeOut(), ) { @@ -340,7 +346,7 @@ fun DefaultEditTileGrid( availableTiles, selectionState, columns, - onAddTile, + { onAddTile(it, listState.tileSpecs().size) }, // Add to the end listState, ) } @@ -398,6 +404,76 @@ private fun AutoScrollGrid( } } +private enum class EditModeHeaderState { + Remove, + Place, + Idle, +} + +@Composable +private fun rememberEditModeState( + listState: EditTileListState, + selectionState: MutableSelectionState, +): State<EditModeHeaderState> { + val editGridHeaderState = remember { mutableStateOf(EditModeHeaderState.Idle) } + LaunchedEffect( + listState.dragInProgress, + selectionState.selected, + selectionState.placementEnabled, + ) { + val canRemove = + listState.isDraggedCellRemovable || + selectionState.selection?.let { listState.isRemovable(it) } ?: false + + editGridHeaderState.value = + when { + selectionState.placementEnabled -> EditModeHeaderState.Place + canRemove -> EditModeHeaderState.Remove + else -> EditModeHeaderState.Idle + } + } + + return editGridHeaderState +} + +@Composable +private fun CurrentTilesGridHeader( + listState: EditTileListState, + selectionState: MutableSelectionState, + onRemoveTile: (TileSpec) -> Unit, + modifier: Modifier = Modifier, +) { + val editGridHeaderState by rememberEditModeState(listState, selectionState) + + AnimatedContent( + targetState = editGridHeaderState, + label = "QSEditHeader", + contentAlignment = Alignment.Center, + modifier = modifier, + ) { state -> + EditGridHeader { + when (state) { + EditModeHeaderState.Remove -> { + RemoveTileTarget { + selectionState.selection?.let { + selectionState.unSelect() + onRemoveTile(it) + } + } + } + EditModeHeaderState.Place -> { + EditGridCenteredText(text = stringResource(id = R.string.tap_to_position_tile)) + } + EditModeHeaderState.Idle -> { + EditGridCenteredText( + text = stringResource(id = R.string.drag_to_rearrange_tiles) + ) + } + } + } + } +} + @Composable private fun EditGridHeader( modifier: Modifier = Modifier, @@ -484,8 +560,14 @@ private fun CurrentTilesGrid( } .testTag(CURRENT_TILES_GRID_TEST_TAG), ) { - EditTiles(cells, listState, selectionState, coroutineScope, largeTilesSpan, onRemoveTile) { - resizingOperation -> + EditTiles( + cells, + listState, + selectionState, + coroutineScope, + largeTilesSpan, + onRemoveTile = onRemoveTile, + ) { resizingOperation -> when (resizingOperation) { is TemporaryResizeOperation -> { currentListState.resizeTile(resizingOperation.spec, resizingOperation.toIcon) @@ -585,6 +667,7 @@ private fun GridCell.key(index: Int): Any { * @param selectionState the [MutableSelectionState] for this grid * @param coroutineScope the [CoroutineScope] to be used for the tiles * @param largeTilesSpan the width used for large tiles + * @param onRemoveTile the callback when a tile is removed from this grid * @param onResize the callback when a tile has a new [ResizeOperation] */ fun LazyGridScope.EditTiles( @@ -628,12 +711,33 @@ fun LazyGridScope.EditTiles( modifier = Modifier.animateItem(), ) } - is SpacerGridCell -> SpacerGridCell() + is SpacerGridCell -> + SpacerGridCell( + Modifier.pointerInput(Unit) { + detectTapGestures(onTap = { selectionState.onTap(index) }) + } + ) } } } @Composable +private fun rememberTileState( + tile: EditTileViewModel, + selectionState: MutableSelectionState, +): State<TileState> { + val tileState = remember { mutableStateOf(TileState.None) } + val canShowRemovalBadge = tile.isRemovable + + LaunchedEffect(selectionState.selection, selectionState.placementEnabled, canShowRemovalBadge) { + tileState.value = + selectionState.tileStateFor(tile.tileSpec, tileState.value, canShowRemovalBadge) + } + + return tileState +} + +@Composable private fun TileGridCell( cell: TileGridCell, index: Int, @@ -646,29 +750,7 @@ private fun TileGridCell( modifier: Modifier = Modifier, ) { val stateDescription = stringResource(id = R.string.accessibility_qs_edit_position, index + 1) - val canShowRemovalBadge = cell.tile.availableEditActions.contains(AvailableEditActions.REMOVE) - var tileState by remember { mutableStateOf(TileState.None) } - - LaunchedEffect(selectionState.selection, canShowRemovalBadge) { - tileState = - when { - selectionState.selection == cell.tile.tileSpec -> { - if (tileState == TileState.None && canShowRemovalBadge) { - // The tile decoration is None if a tile is newly composed OR the removal - // badge can't be shown. - // For newly composed and selected tiles, such as dragged tiles or moved - // tiles from resizing, introduce a short delay. This avoids clipping issues - // on the border and resizing handle, as well as letting the selection - // animation play correctly. - delay(250) - } - TileState.Selected - } - canShowRemovalBadge -> TileState.Removable - else -> TileState.None - } - } - + val tileState by rememberTileState(cell.tile, selectionState) val resizingState = rememberResizingState(cell.tile.tileSpec, cell.isIcon) val progress: () -> Float = { if (tileState == TileState.Selected) { @@ -696,12 +778,16 @@ private fun TileGridCell( with(LocalDensity.current) { (largeTilesSpan - 1) * TileArrangementPadding.roundToPx() } val colors = EditModeTileDefaults.editTileColors() val toggleSizeLabel = stringResource(R.string.accessibility_qs_edit_toggle_tile_size_action) - val clickLabel = + val togglePlacementModeLabel = + stringResource(R.string.accessibility_qs_edit_toggle_placement_mode) + val decorationClickLabel = when (tileState) { - TileState.None -> null TileState.Removable -> stringResource(id = R.string.accessibility_qs_edit_remove_tile_action) TileState.Selected -> toggleSizeLabel + TileState.None, + TileState.Placeable, + TileState.GreyedOut -> null } InteractiveTileContainer( tileState = tileState, @@ -720,8 +806,13 @@ private fun TileGridCell( coroutineScope.launch { resizingState.toggleCurrentValue() } } }, - onClickLabel = clickLabel, + onClickLabel = decorationClickLabel, ) { + val placeableColor = MaterialTheme.colorScheme.primary.copy(alpha = .4f) + val backgroundColor by + animateColorAsState( + if (tileState == TileState.Placeable) placeableColor else colors.background + ) Box( modifier .fillMaxSize() @@ -734,7 +825,11 @@ private fun TileGridCell( CustomAccessibilityAction(toggleSizeLabel) { onResize(FinalResizeOperation(cell.tile.tileSpec, !cell.isIcon)) true - } + }, + CustomAccessibilityAction(togglePlacementModeLabel) { + selectionState.togglePlacementMode(cell.tile.tileSpec) + true + }, ) } .selectableTile(cell.tile.tileSpec, selectionState) @@ -744,9 +839,14 @@ private fun TileGridCell( DragType.Move, selectionState::unSelect, ) - .tileBackground(colors.background) + .tileBackground { backgroundColor } ) { - EditTile(tile = cell.tile, state = resizingState, progress = progress) + EditTile( + tile = cell.tile, + tileState = tileState, + state = resizingState, + progress = progress, + ) } } } @@ -791,7 +891,7 @@ private fun AvailableTileGridCell( } else { Modifier } - Box(draggableModifier.fillMaxSize().tileBackground(colors.background)) { + Box(draggableModifier.fillMaxSize().tileBackground { colors.background }) { // Icon SmallTileContent( iconProvider = { cell.tile.icon }, @@ -834,11 +934,13 @@ private fun SpacerGridCell(modifier: Modifier = Modifier) { @Composable fun EditTile( tile: EditTileViewModel, + tileState: TileState, state: ResizingState, progress: () -> Float, colors: TileColors = EditModeTileDefaults.editTileColors(), ) { val iconSizeDiff = CommonTileDefaults.IconSize - CommonTileDefaults.LargeTileIconSize + val alpha by animateFloatAsState(if (tileState == TileState.GreyedOut) .4f else 1f) Row( horizontalArrangement = spacedBy(6.dp), verticalAlignment = Alignment.CenterVertically, @@ -871,7 +973,8 @@ fun EditTile( placeable.place(startPadding.roundToInt(), 0) } } - .largeTilePadding(), + .largeTilePadding() + .graphicsLayer { this.alpha = alpha }, ) { // Icon Box(Modifier.size(ToggleTargetSize)) { @@ -889,7 +992,7 @@ fun EditTile( label = tile.label.text, secondaryLabel = tile.appName?.text, colors = colors, - modifier = Modifier.weight(1f).graphicsLayer { alpha = progress() }, + modifier = Modifier.weight(1f).graphicsLayer { this.alpha = progress() }, ) } } @@ -908,9 +1011,9 @@ private fun MeasureScope.iconHorizontalCenter(containerSize: Int): Float { CommonTileDefaults.TileStartPadding.toPx() } -private fun Modifier.tileBackground(color: Color): Modifier { +private fun Modifier.tileBackground(color: () -> Color): Modifier { // Clip tile contents from overflowing past the tile - return clip(RoundedCornerShape(InactiveCornerRadius)).drawBehind { drawRect(color) } + return clip(RoundedCornerShape(InactiveCornerRadius)).drawBehind { drawRect(color()) } } private object EditModeTileDefaults { diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt index 984343a45797..233af548fff2 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/infinitegrid/InfiniteGridLayout.kt @@ -42,7 +42,6 @@ import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModel import com.android.systemui.qs.panels.ui.viewmodel.IconTilesViewModel import com.android.systemui.qs.panels.ui.viewmodel.InfiniteGridViewModel import com.android.systemui.qs.panels.ui.viewmodel.TileViewModel -import com.android.systemui.qs.pipeline.domain.interactor.CurrentTilesInteractor.Companion.POSITION_AT_END import com.android.systemui.qs.pipeline.shared.TileSpec import com.android.systemui.qs.shared.ui.ElementKeys.toElementKey import com.android.systemui.res.R @@ -171,7 +170,7 @@ constructor( otherTiles = otherTiles, columns = columns, modifier = modifier, - onAddTile = { onAddTile(it, POSITION_AT_END) }, + onAddTile = onAddTile, onRemoveTile = onRemoveTile, onSetTiles = onSetTiles, onResize = iconTilesViewModel::resize, diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/MutableSelectionState.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/MutableSelectionState.kt index 3dfde86bf8d9..50b29557683d 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/MutableSelectionState.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/MutableSelectionState.kt @@ -16,15 +16,17 @@ package com.android.systemui.qs.panels.ui.compose.selection -import androidx.compose.foundation.gestures.detectTapGestures import androidx.compose.runtime.Composable +import androidx.compose.runtime.Stable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.input.pointer.pointerInput +import com.android.systemui.common.ui.compose.gestures.detectEagerTapGestures import com.android.systemui.qs.pipeline.shared.TileSpec +import kotlinx.coroutines.delay /** Creates the state of the current selected tile that is remembered across compositions. */ @Composable @@ -38,6 +40,17 @@ class MutableSelectionState { var selection by mutableStateOf<TileSpec?>(null) private set + /** + * Whether the current selection is in placement mode or not. + * + * A tile in placement mode can be positioned by tapping at the desired location in the grid. + */ + var placementEnabled by mutableStateOf(false) + private set + + /** Latest event from coming from placement mode. */ + var placementEvent by mutableStateOf<PlacementEvent?>(null) + val selected: Boolean get() = selection != null @@ -47,37 +60,122 @@ class MutableSelectionState { fun unSelect() { selection = null + exitPlacementMode() } -} -/** - * Listens for click events to select/unselect the given [TileSpec]. Use this on current tiles as - * they can be selected. - */ -fun Modifier.selectableTile( - tileSpec: TileSpec, - selectionState: MutableSelectionState, - onClick: () -> Unit = {}, -): Modifier { - return pointerInput(Unit) { - detectTapGestures( - onTap = { - if (selectionState.selection == tileSpec) { - selectionState.unSelect() - } else { - selectionState.select(tileSpec) + /** Selects [tileSpec] and enable placement mode. */ + fun enterPlacementMode(tileSpec: TileSpec) { + selection = tileSpec + placementEnabled = true + } + + /** Disable placement mode but maintains current selection. */ + private fun exitPlacementMode() { + placementEnabled = false + } + + fun togglePlacementMode(tileSpec: TileSpec) { + if (placementEnabled) exitPlacementMode() else enterPlacementMode(tileSpec) + } + + suspend fun tileStateFor( + tileSpec: TileSpec, + previousState: TileState, + canShowRemovalBadge: Boolean, + ): TileState { + return when { + placementEnabled && selection == tileSpec -> TileState.Placeable + placementEnabled -> TileState.GreyedOut + selection == tileSpec -> { + if (previousState == TileState.None && canShowRemovalBadge) { + // The tile decoration is None if a tile is newly composed OR the removal + // badge can't be shown. + // For newly composed and selected tiles, such as dragged tiles or moved + // tiles from resizing, introduce a short delay. This avoids clipping issues + // on the border and resizing handle, as well as letting the selection + // animation play correctly. + delay(250) } - onClick() + TileState.Selected } - ) + canShowRemovalBadge -> TileState.Removable + else -> TileState.None + } + } + + /** + * Tap callback on a tile. + * + * Tiles can be selected and placed using placement mode. + */ + fun onTap(tileSpec: TileSpec) { + when { + placementEnabled && selection == tileSpec -> { + exitPlacementMode() + } + placementEnabled -> { + selection?.let { placementEvent = PlacementEvent.PlaceToTileSpec(it, tileSpec) } + exitPlacementMode() + } + selection == tileSpec -> { + unSelect() + } + else -> { + select(tileSpec) + } + } + } + + /** + * Tap on a position. + * + * Use on grid items not associated with a [TileSpec], such as a spacer. Spacers can't be + * selected, but selections can be moved to their position. + */ + fun onTap(index: Int) { + when { + placementEnabled -> { + selection?.let { placementEvent = PlacementEvent.PlaceToIndex(it, index) } + exitPlacementMode() + } + selected -> { + unSelect() + } + } } } +// Not using data classes here as distinct placement events may have the same moving spec and target +@Stable +sealed interface PlacementEvent { + val movingSpec: TileSpec + + /** Placement event corresponding to [movingSpec] moving to [targetSpec]'s position */ + class PlaceToTileSpec(override val movingSpec: TileSpec, val targetSpec: TileSpec) : + PlacementEvent + + /** Placement event corresponding to [movingSpec] moving to [targetIndex] */ + class PlaceToIndex(override val movingSpec: TileSpec, val targetIndex: Int) : PlacementEvent +} + /** - * Listens for click events to unselect any tile. Use this on available tiles as they can't be - * selected. + * Listens for click events on selectable tiles. + * + * Use this on current tiles as they can be selected. + * + * @param tileSpec the [TileSpec] of the tile this modifier is applied to + * @param selectionState the [MutableSelectionState] representing the grid's selection */ @Composable -fun Modifier.clearSelectionTile(selectionState: MutableSelectionState): Modifier { - return pointerInput(Unit) { detectTapGestures(onTap = { selectionState.unSelect() }) } +fun Modifier.selectableTile(tileSpec: TileSpec, selectionState: MutableSelectionState): Modifier { + return pointerInput(Unit) { + detectEagerTapGestures( + doubleTapEnabled = { + // Double tap enabled if where not in placement mode already + !selectionState.placementEnabled + }, + onDoubleTap = { selectionState.enterPlacementMode(tileSpec) }, + onTap = { selectionState.onTap(tileSpec) }, + ) + } } diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/Selection.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/Selection.kt index 57f63c755b43..8ffc4be88e7c 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/Selection.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/compose/selection/Selection.kt @@ -17,6 +17,7 @@ package com.android.systemui.qs.panels.ui.compose.selection import androidx.compose.animation.animateColor +import androidx.compose.animation.core.Animatable import androidx.compose.animation.core.Transition import androidx.compose.animation.core.animateFloat import androidx.compose.animation.core.animateFloatAsState @@ -37,9 +38,13 @@ import androidx.compose.material3.Icon import androidx.compose.material3.LocalMinimumInteractiveComponentSize import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.State import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.drawBehind @@ -73,7 +78,9 @@ import com.android.systemui.qs.panels.ui.compose.selection.SelectionDefaults.RES import com.android.systemui.qs.panels.ui.compose.selection.SelectionDefaults.ResizingPillHeight import com.android.systemui.qs.panels.ui.compose.selection.SelectionDefaults.ResizingPillWidth import com.android.systemui.qs.panels.ui.compose.selection.SelectionDefaults.SelectedBorderWidth +import com.android.systemui.qs.panels.ui.compose.selection.TileState.GreyedOut import com.android.systemui.qs.panels.ui.compose.selection.TileState.None +import com.android.systemui.qs.panels.ui.compose.selection.TileState.Placeable import com.android.systemui.qs.panels.ui.compose.selection.TileState.Removable import com.android.systemui.qs.panels.ui.compose.selection.TileState.Selected import kotlin.math.cos @@ -104,10 +111,11 @@ fun InteractiveTileContainer( ) { val transition: Transition<TileState> = updateTransition(tileState) val decorationColor by transition.animateColor() - val decorationAngle by transition.animateAngle() + val decorationAngle by animateAngle(tileState) val decorationSize by transition.animateSize() val decorationOffset by transition.animateOffset() - val decorationAlpha by transition.animateFloat { state -> if (state == None) 0f else 1f } + val decorationAlpha by + transition.animateFloat { state -> if (state == Removable || state == Selected) 1f else 0f } val badgeIconAlpha by transition.animateFloat { state -> if (state == Removable) 1f else 0f } val selectionBorderAlpha by transition.animateFloat { state -> if (state == Selected) 1f else 0f } @@ -282,27 +290,61 @@ private fun Modifier.resizable(selected: Boolean, state: ResizingState): Modifie } enum class TileState { + /** Tile is displayed as-is, no additional decoration needed. */ None, + /** Tile can be removed by the user. This is displayed by a badge in the upper end corner. */ Removable, + /** + * Tile is selected and resizable. One tile can be selected at a time in the grid. This is when + * we display the resizing handle and a highlighted border around the tile. + */ Selected, + /** + * Tile placeable. This state means that the grid is in placement mode and this tile is + * selected. It should be highlighted to stand out in the grid. + */ + Placeable, + /** + * Tile is faded out. This state means that the grid is in placement mode and this tile isn't + * selected. It serves as a target to place the selected tile. + */ + GreyedOut, } @Composable private fun Transition<TileState>.animateColor(): State<Color> { return animateColor { state -> when (state) { - None -> Color.Transparent + None, + GreyedOut -> Color.Transparent Removable -> MaterialTheme.colorScheme.primaryContainer - Selected -> MaterialTheme.colorScheme.primary + Selected, + Placeable -> MaterialTheme.colorScheme.primary } } } +/** + * Animate the angle of the tile decoration based on the previous state + * + * Some [TileState] don't have a visible decoration, and the angle should only animate when going + * between visible states. + */ @Composable -private fun Transition<TileState>.animateAngle(): State<Float> { - return animateFloat { state -> - if (state == Removable) BADGE_ANGLE_RAD else RESIZING_PILL_ANGLE_RAD +private fun animateAngle(tileState: TileState): State<Float> { + val animatable = remember { Animatable(0f) } + var animate by remember { mutableStateOf(false) } + LaunchedEffect(tileState) { + val targetAngle = tileState.decorationAngle() + + if (targetAngle == null) { + animate = false + } else { + if (animate) animatable.animateTo(targetAngle) else animatable.snapTo(targetAngle) + animate = true + } } + return animatable.asState() } @Composable @@ -310,7 +352,9 @@ private fun Transition<TileState>.animateSize(): State<Size> { return animateSize { state -> with(LocalDensity.current) { when (state) { - None -> Size.Zero + None, + Placeable, + GreyedOut -> Size.Zero Removable -> Size(BadgeSize.toPx()) Selected -> Size(ResizingPillWidth.toPx(), ResizingPillHeight.toPx()) } @@ -323,7 +367,9 @@ private fun Transition<TileState>.animateOffset(): State<Offset> { return animateOffset { state -> with(LocalDensity.current) { when (state) { - None -> Offset.Zero + None, + Placeable, + GreyedOut -> Offset.Zero Removable -> Offset(BadgeXOffset.toPx(), BadgeYOffset.toPx()) Selected -> Offset(-SelectedBorderWidth.toPx(), 0f) } @@ -331,6 +377,16 @@ private fun Transition<TileState>.animateOffset(): State<Offset> { } } +private fun TileState.decorationAngle(): Float? { + return when (this) { + Removable -> BADGE_ANGLE_RAD + Selected -> RESIZING_PILL_ANGLE_RAD + None, + Placeable, + GreyedOut -> null // No visible decoration + } +} + private fun Size(size: Float) = Size(size, size) private fun offsetForAngle(angle: Float, radius: Float, center: Offset): Offset { diff --git a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditTileViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditTileViewModel.kt index be6ce5c5b4f4..cf325f531c38 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditTileViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/panels/ui/viewmodel/EditTileViewModel.kt @@ -66,6 +66,9 @@ data class EditTileViewModel( ) : CategoryAndName { override val name get() = label.text + + val isRemovable + get() = availableEditActions.contains(AvailableEditActions.REMOVE) } enum class AvailableEditActions { diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java index c60e3da9d833..49ec1baeeeee 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/CastTile.java @@ -48,11 +48,13 @@ import com.android.systemui.flags.FeatureFlags; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.qs.QSTile.BooleanState; +import com.android.systemui.plugins.qs.TileDetailsViewModel; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.qs.QSHost; import com.android.systemui.qs.QsEventLogger; import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.qs.tileimpl.QSTileImpl; +import com.android.systemui.qs.tiles.dialog.CastDetailsViewModel; import com.android.systemui.res.R; import com.android.systemui.shade.domain.interactor.ShadeDialogContextInteractor; import com.android.systemui.statusbar.connectivity.NetworkController; @@ -93,6 +95,7 @@ public class CastTile extends QSTileImpl<BooleanState> { private final ShadeDialogContextInteractor mShadeDialogContextInteractor; private boolean mCastTransportAllowed; private boolean mHotspotConnected; + private final CastDetailsViewModel.Factory mCastDetailsViewModelFactory; @Inject public CastTile( @@ -113,7 +116,8 @@ public class CastTile extends QSTileImpl<BooleanState> { ConnectivityRepository connectivityRepository, TileJavaAdapter javaAdapter, FeatureFlags featureFlags, - ShadeDialogContextInteractor shadeDialogContextInteractor + ShadeDialogContextInteractor shadeDialogContextInteractor, + CastDetailsViewModel.Factory castDetailsViewModelFactory ) { super(host, uiEventLogger, backgroundLooper, mainHandler, falsingManager, metricsLogger, statusBarStateController, activityStarter, qsLogger); @@ -124,6 +128,7 @@ public class CastTile extends QSTileImpl<BooleanState> { mJavaAdapter = javaAdapter; mFeatureFlags = featureFlags; mShadeDialogContextInteractor = shadeDialogContextInteractor; + mCastDetailsViewModelFactory = castDetailsViewModelFactory; mController.observe(this, mCallback); mKeyguard.observe(this, mCallback); if (!mFeatureFlags.isEnabled(SIGNAL_CALLBACK_DEPRECATION)) { @@ -172,12 +177,7 @@ public class CastTile extends QSTileImpl<BooleanState> { @Override protected void handleClick(@Nullable Expandable expandable) { - if (getState().state == Tile.STATE_UNAVAILABLE) { - return; - } - - List<CastDevice> activeDevices = getActiveDevices(); - if (willPopDialog()) { + handleClick(() -> { if (!mKeyguard.isShowing()) { showDialog(expandable); } else { @@ -187,16 +187,44 @@ public class CastTile extends QSTileImpl<BooleanState> { showDialog(null /* view */); }); } + }); + } + + @Override + public boolean getDetailsViewModel(Consumer<TileDetailsViewModel> callback) { + CastDetailsViewModel viewModel = mCastDetailsViewModelFactory + .create(mShadeDialogContextInteractor.getContext(), ROUTE_TYPE_REMOTE_DISPLAY); + handleClick(() -> { + if (!mKeyguard.isShowing()) { + callback.accept(viewModel); + } else { + mActivityStarter.dismissKeyguardThenExecute(() -> { + callback.accept(viewModel); + return false; + }, null /* cancelAction */, true/* afterKeyguardGone */); + } + }); + return true; + } + + private void handleClick(Runnable showPromptCallback) { + if (getState().state == Tile.STATE_UNAVAILABLE) { + return; + } + + List<CastDevice> activeDevices = getActiveDevices(); + if (willShowPrompt()) { + showPromptCallback.run(); } else { mController.stopCasting(activeDevices.get(0), StopReason.STOP_QS_TILE); } } - // We want to pop up the media route selection dialog if we either have no active devices - // (neither routes nor projection), or if we have an active route. In other cases, we assume - // that a projection is active. This is messy, but this tile never correctly handled the - // case where multiple devices were active :-/. - private boolean willPopDialog() { + // We want to pop up the media route selection dialog (or show the cast details view) if we + // either have no active devices (neither routes nor projection), or if we have an active + // route. In other cases, we assume that a projection is active. This is messy, but this tile + // never correctly handled the case where multiple devices were active :-/. + private boolean willShowPrompt() { List<CastDevice> activeDevices = getActiveDevices(); return activeDevices.isEmpty() || (activeDevices.get(0).getTag() instanceof RouteInfo); } @@ -303,7 +331,7 @@ public class CastTile extends QSTileImpl<BooleanState> { state.secondaryLabel = ""; } state.expandedAccessibilityClassName = Button.class.getName(); - state.forceExpandIcon = willPopDialog(); + state.forceExpandIcon = willShowPrompt(); } else { state.state = Tile.STATE_UNAVAILABLE; String noWifi = mContext.getString(R.string.quick_settings_cast_no_network); diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/CastDetailsContent.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/CastDetailsContent.kt new file mode 100644 index 000000000000..cd33c964ce1b --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/CastDetailsContent.kt @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles.dialog + +import android.view.LayoutInflater +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.viewinterop.AndroidView +import com.android.internal.R +import com.android.internal.app.MediaRouteControllerContentManager + +@Composable +fun CastDetailsContent(castDetailsViewModel: CastDetailsViewModel) { + if (castDetailsViewModel.shouldShowChooserDialog()) { + // TODO(b/378514236): Show the chooser UI here. + return + } + + val contentManager: MediaRouteControllerContentManager = remember { + castDetailsViewModel.createControllerContentManager() + } + + AndroidView( + modifier = Modifier.fillMaxWidth().fillMaxHeight(), + factory = { context -> + // Inflate with the existing dialog xml layout + val view = + LayoutInflater.from(context).inflate(R.layout.media_route_controller_dialog, null) + contentManager.bindViews(view) + contentManager.onAttachedToWindow() + + view + }, + onRelease = { contentManager.onDetachedFromWindow() }, + ) +} diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/CastDetailsViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/CastDetailsViewModel.kt new file mode 100644 index 000000000000..72322eff8bed --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/CastDetailsViewModel.kt @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.qs.tiles.dialog + +import android.content.Context +import android.content.Intent +import android.graphics.drawable.Drawable +import android.provider.Settings +import com.android.internal.app.MediaRouteControllerContentManager +import com.android.internal.app.MediaRouteDialogPresenter +import com.android.systemui.plugins.qs.TileDetailsViewModel +import com.android.systemui.qs.tiles.base.domain.actions.QSTileIntentUserInputHandler +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject + +/** The view model used for the screen record details view in the Quick Settings */ +class CastDetailsViewModel +@AssistedInject +constructor( + private val qsTileIntentUserActionHandler: QSTileIntentUserInputHandler, + @Assisted private val context: Context, + @Assisted private val routeTypes: Int, +) : MediaRouteControllerContentManager.Delegate, TileDetailsViewModel { + @AssistedFactory + fun interface Factory { + fun create(context: Context, routeTypes: Int): CastDetailsViewModel + } + + fun shouldShowChooserDialog(): Boolean { + return MediaRouteDialogPresenter.shouldShowChooserDialog(context, routeTypes) + } + + fun createControllerContentManager(): MediaRouteControllerContentManager { + return MediaRouteControllerContentManager(context, this) + } + + override fun clickOnSettingsButton() { + qsTileIntentUserActionHandler.handle( + /* expandable= */ null, + Intent(Settings.ACTION_CAST_SETTINGS), + ) + } + + // TODO(b/388321032): Replace this string with a string in a translatable xml file, + override val title: String + get() = "Cast screen to device" + + // TODO(b/388321032): Replace this string with a string in a translatable xml file, + override val subTitle: String + get() = "Searching for devices..." + + override fun setMediaRouteDeviceTitle(title: CharSequence?) { + // TODO(b/378514236): Finish implementing this function. + } + + override fun setMediaRouteDeviceIcon(icon: Drawable?) { + // TODO(b/378514236): Finish implementing this function. + } + + override fun dismissView() { + // TODO(b/378514236): Finish implementing this function. + } +} diff --git a/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayCoreStartable.kt b/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayCoreStartable.kt index 263ef09ea767..5d4a774d77f9 100644 --- a/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayCoreStartable.kt +++ b/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayCoreStartable.kt @@ -19,14 +19,18 @@ package com.android.systemui.reardisplay import android.content.Context import android.hardware.devicestate.DeviceStateManager import android.hardware.devicestate.feature.flags.Flags +import android.os.Handler +import android.view.accessibility.AccessibilityManager import androidx.annotation.VisibleForTesting import com.android.keyguard.KeyguardUpdateMonitor import com.android.keyguard.KeyguardUpdateMonitorCallback import com.android.systemui.CoreStartable import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application +import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.display.domain.interactor.RearDisplayStateInteractor import com.android.systemui.statusbar.phone.SystemUIDialog +import java.util.concurrent.atomic.AtomicBoolean import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job @@ -52,6 +56,8 @@ internal constructor( private val rearDisplayInnerDialogDelegateFactory: RearDisplayInnerDialogDelegate.Factory, @Application private val scope: CoroutineScope, private val keyguardUpdateMonitor: KeyguardUpdateMonitor, + private val accessibilityManager: AccessibilityManager, + @Background private val handler: Handler, ) : CoreStartable, AutoCloseable { companion object { @@ -77,6 +83,12 @@ internal constructor( override fun start() { if (Flags.deviceStateRdmV2()) { var dialog: SystemUIDialog? = null + var touchExplorationEnabled = AtomicBoolean(false) + + accessibilityManager.addTouchExplorationStateChangeListener( + { enabled -> touchExplorationEnabled.set(enabled) }, + handler, + ) keyguardUpdateMonitor.registerCallback(keyguardCallback) @@ -99,6 +111,7 @@ internal constructor( rearDisplayInnerDialogDelegateFactory.create( rearDisplayContext, deviceStateManager::cancelStateRequest, + touchExplorationEnabled.get(), ) dialog = delegate.createDialog().apply { show() } } diff --git a/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayInnerDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayInnerDialogDelegate.kt index f5facf42ee67..96f1bd270239 100644 --- a/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayInnerDialogDelegate.kt +++ b/packages/SystemUI/src/com/android/systemui/reardisplay/RearDisplayInnerDialogDelegate.kt @@ -20,7 +20,10 @@ import android.annotation.SuppressLint import android.content.Context import android.os.Bundle import android.view.MotionEvent +import android.view.View +import android.widget.Button import android.widget.SeekBar +import android.widget.TextView import com.android.systemui.haptics.slider.HapticSlider import com.android.systemui.haptics.slider.HapticSliderPlugin import com.android.systemui.haptics.slider.HapticSliderViewBinder @@ -45,6 +48,7 @@ class RearDisplayInnerDialogDelegate internal constructor( private val systemUIDialogFactory: SystemUIDialog.Factory, @Assisted private val rearDisplayContext: Context, + @Assisted private val touchExplorationEnabled: Boolean, private val vibratorHelper: VibratorHelper, private val msdlPlayer: MSDLPlayer, private val systemClock: SystemClock, @@ -82,6 +86,7 @@ internal constructor( fun create( rearDisplayContext: Context, onCanceledRunnable: Runnable, + touchExplorationEnabled: Boolean, ): RearDisplayInnerDialogDelegate } @@ -95,11 +100,32 @@ internal constructor( @SuppressLint("ClickableViewAccessibility") override fun onCreate(dialog: SystemUIDialog, savedInstanceState: Bundle?) { + dialog.apply { setContentView(R.layout.activity_rear_display_enabled) setCanceledOnTouchOutside(false) + requireViewById<Button>(R.id.cancel_button).let { it -> + if (!touchExplorationEnabled) { + return@let + } + + it.visibility = View.VISIBLE + it.setOnClickListener { onCanceledRunnable.run() } + } + + requireViewById<TextView>(R.id.seekbar_instructions).let { it -> + if (touchExplorationEnabled) { + it.visibility = View.GONE + } + } + requireViewById<SeekBar>(R.id.seekbar).let { it -> + if (touchExplorationEnabled) { + it.visibility = View.GONE + return@let + } + // Create and bind the HapticSliderPlugin val hapticSliderPlugin = HapticSliderPlugin( 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 3ad0867192d3..06fc8610c97b 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 @@ -33,10 +33,8 @@ import com.android.systemui.bouncer.domain.interactor.SimBouncerInteractor import com.android.systemui.bouncer.shared.logging.BouncerUiEvent import com.android.systemui.classifier.FalsingCollector import com.android.systemui.classifier.FalsingCollectorActual -import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application -import com.android.systemui.dagger.qualifiers.DisplayId import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor import com.android.systemui.deviceentry.domain.interactor.DeviceEntryHapticsInteractor import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor @@ -82,6 +80,7 @@ import com.android.systemui.util.kotlin.pairwise import com.android.systemui.util.kotlin.sample import com.android.systemui.util.printSection import com.android.systemui.util.println +import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow import com.google.android.msdl.data.model.MSDLToken import com.google.android.msdl.domain.MSDLPlayer import dagger.Lazy @@ -123,7 +122,6 @@ constructor( private val bouncerInteractor: BouncerInteractor, private val keyguardInteractor: KeyguardInteractor, private val sysUiState: SysUiState, - @DisplayId private val displayId: Int, private val sceneLogger: SceneLogger, @FalsingCollectorActual private val falsingCollector: FalsingCollector, private val falsingManager: FalsingManager, @@ -197,7 +195,8 @@ constructor( return } - printSection("Scene state") { + printSection("Framework state") { + println("isVisible", sceneInteractor.isVisible.value) println("currentScene", sceneInteractor.currentScene.value.debugName) println( "currentOverlays", @@ -732,21 +731,26 @@ constructor( sceneInteractor.transitionState .mapNotNull { it as? ObservableTransitionState.Idle } .distinctUntilChanged(), + sceneInteractor.isVisible, occlusionInteractor.invisibleDueToOcclusion, - ) { idleState, invisibleDueToOcclusion -> + ) { idleState, isVisible, invisibleDueToOcclusion -> SceneContainerPlugin.SceneContainerPluginState( scene = idleState.currentScene, overlays = idleState.currentOverlays, + isVisible = isVisible, invisibleDueToOcclusion = invisibleDueToOcclusion, ) } - .collect { sceneContainerPluginState -> + .map { sceneContainerPluginState -> + SceneContainerPlugin.EvaluatorByFlag.map { (flag, evaluator) -> + flag to evaluator(sceneContainerPluginState) + } + .toMap() + } + .distinctUntilChanged() + .collect { flags -> sysUiState.updateFlags( - displayId, - *SceneContainerPlugin.EvaluatorByFlag.map { (flag, evaluator) -> - flag to evaluator.invoke(sceneContainerPluginState) - } - .toTypedArray(), + *(flags.entries.map { (key, value) -> key to value }).toTypedArray() ) } } diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java index 24e7976011f4..b90624245cc5 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java @@ -206,6 +206,7 @@ import com.google.android.msdl.data.model.MSDLToken; import com.google.android.msdl.domain.MSDLPlayer; import dagger.Lazy; + import kotlin.Unit; import kotlinx.coroutines.CoroutineDispatcher; @@ -4267,7 +4268,8 @@ public final class NotificationPanelViewController implements == AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD.getId() || action == AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_UP.getId()) { - mStatusBarKeyguardViewManager.showPrimaryBouncer(true); + mStatusBarKeyguardViewManager.showPrimaryBouncer(true, + "NotificationPanelViewController#performAccessibilityAction"); return true; } return super.performAccessibilityAction(host, action, args); diff --git a/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrierGroupController.java b/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrierGroupController.java index 4b8cc00e1c28..421b5ea23278 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrierGroupController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/carrier/ShadeCarrierGroupController.java @@ -39,9 +39,13 @@ import androidx.annotation.VisibleForTesting; import com.android.keyguard.CarrierTextManager; import com.android.settingslib.AccessibilityContentDescriptions; import com.android.settingslib.mobile.TelephonyIcons; +import com.android.systemui.Flags; import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.dagger.qualifiers.Application; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; +import com.android.systemui.kairos.ExperimentalKairosApi; +import com.android.systemui.kairos.KairosNetwork; import com.android.systemui.plugins.ActivityStarter; import com.android.systemui.res.R; import com.android.systemui.shade.ShadeDisplayAware; @@ -52,17 +56,30 @@ import com.android.systemui.statusbar.connectivity.ui.MobileContextProvider; import com.android.systemui.statusbar.phone.StatusBarLocation; import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags; import com.android.systemui.statusbar.pipeline.mobile.ui.MobileUiAdapter; +import com.android.systemui.statusbar.pipeline.mobile.ui.MobileUiAdapterKairos; import com.android.systemui.statusbar.pipeline.mobile.ui.binder.MobileIconsBinder; import com.android.systemui.statusbar.pipeline.mobile.ui.view.ModernShadeCarrierGroupMobileView; import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsViewModel; +import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsViewModelKairos; import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.ShadeCarrierGroupMobileIconViewModel; import com.android.systemui.util.CarrierConfigTracker; +import dagger.Lazy; + +import kotlin.OptIn; +import kotlin.Pair; + +import kotlinx.coroutines.CoroutineScope; +import kotlinx.coroutines.Job; + +import java.util.ArrayList; import java.util.List; +import java.util.concurrent.CancellationException; import java.util.function.Consumer; import javax.inject.Inject; +@OptIn(markerClass = ExperimentalKairosApi.class) public class ShadeCarrierGroupController { private static final String TAG = "ShadeCarrierGroup"; @@ -100,38 +117,43 @@ public class ShadeCarrierGroupController { private final ShadeCarrierGroupControllerLogger mLogger; + private final Lazy<MobileUiAdapterKairos> mMobileUiAdapterKairos; + private final CoroutineScope mAppScope; + private final KairosNetwork mKairosNetwork; + private final SignalCallback mSignalCallback = new SignalCallback() { - @Override - public void setMobileDataIndicators(@NonNull MobileDataIndicators indicators) { - int slotIndex = getSlotIndex(indicators.subId); - if (slotIndex >= SIM_SLOTS) { - Log.w(TAG, "setMobileDataIndicators - slot: " + slotIndex); - return; - } - if (slotIndex == INVALID_SIM_SLOT_INDEX) { - Log.e(TAG, "Invalid SIM slot index for subscription: " + indicators.subId); - return; - } - mInfos[slotIndex] = new CellSignalState( - indicators.statusIcon.visible, - indicators.statusIcon.icon, - indicators.statusIcon.contentDescription, - indicators.typeContentDescription.toString(), - indicators.roaming - ); - mMainHandler.obtainMessage(H.MSG_UPDATE_STATE).sendToTarget(); - } + @Override + public void setMobileDataIndicators(@NonNull MobileDataIndicators indicators) { + int slotIndex = getSlotIndex(indicators.subId); + if (slotIndex >= SIM_SLOTS) { + Log.w(TAG, "setMobileDataIndicators - slot: " + slotIndex); + return; + } + if (slotIndex == INVALID_SIM_SLOT_INDEX) { + Log.e(TAG, "Invalid SIM slot index for subscription: " + indicators.subId); + return; + } + mInfos[slotIndex] = new CellSignalState( + indicators.statusIcon.visible, + indicators.statusIcon.icon, + indicators.statusIcon.contentDescription, + indicators.typeContentDescription.toString(), + indicators.roaming + ); + mMainHandler.obtainMessage(H.MSG_UPDATE_STATE).sendToTarget(); + } - @Override - public void setNoSims(boolean hasNoSims, boolean simDetected) { - if (hasNoSims) { - for (int i = 0; i < SIM_SLOTS; i++) { - mInfos[i] = mInfos[i].changeVisibility(false); - } - } - mMainHandler.obtainMessage(H.MSG_UPDATE_STATE).sendToTarget(); + @Override + public void setNoSims(boolean hasNoSims, boolean simDetected) { + if (hasNoSims) { + for (int i = 0; i < SIM_SLOTS; i++) { + mInfos[i] = mInfos[i].changeVisibility(false); } - }; + } + mMainHandler.obtainMessage(H.MSG_UPDATE_STATE).sendToTarget(); + } + }; + private final ArrayList<Job> mBindingJobs = new ArrayList<>(); private static class Callback implements CarrierTextManager.CarrierTextCallback { private H mHandler; @@ -159,7 +181,10 @@ public class ShadeCarrierGroupController { SlotIndexResolver slotIndexResolver, MobileUiAdapter mobileUiAdapter, MobileContextProvider mobileContextProvider, - StatusBarPipelineFlags statusBarPipelineFlags + StatusBarPipelineFlags statusBarPipelineFlags, + Lazy<MobileUiAdapterKairos> mobileUiAdapterKairos, + CoroutineScope appScope, + KairosNetwork kairosNetwork ) { mContext = context; mActivityStarter = activityStarter; @@ -174,6 +199,9 @@ public class ShadeCarrierGroupController { .build(); mCarrierConfigTracker = carrierConfigTracker; mSlotIndexResolver = slotIndexResolver; + mMobileUiAdapterKairos = mobileUiAdapterKairos; + mAppScope = appScope; + mKairosNetwork = kairosNetwork; View.OnClickListener onClickListener = v -> { if (!v.isVisibleToUser()) { return; @@ -195,8 +223,12 @@ public class ShadeCarrierGroupController { mMobileIconsViewModel = mobileUiAdapter.getMobileIconsViewModel(); if (mStatusBarPipelineFlags.useNewShadeCarrierGroupMobileIcons()) { - mobileUiAdapter.setShadeCarrierGroupController(this); - MobileIconsBinder.bind(view, mMobileIconsViewModel); + if (Flags.statusBarMobileIconKairos()) { + mobileUiAdapterKairos.get().setShadeCarrierGroupController(this); + } else { + mobileUiAdapter.setShadeCarrierGroupController(this); + MobileIconsBinder.bind(view, mMobileIconsViewModel); + } } mCarrierDividers[0] = view.getCarrierDivider1(); @@ -243,21 +275,52 @@ public class ShadeCarrierGroupController { List<IconData> iconDataList = processSubIdList(subIds); - for (IconData iconData : iconDataList) { - ShadeCarrier carrier = mCarrierGroups[iconData.slotIndex]; - - Context mobileContext = - mMobileContextProvider.getMobileContextForSub(iconData.subId, mContext); - ModernShadeCarrierGroupMobileView modernMobileView = ModernShadeCarrierGroupMobileView - .constructAndBind( - mobileContext, - mMobileIconsViewModel.getLogger(), - "mobile_carrier_shade_group", - (ShadeCarrierGroupMobileIconViewModel) mMobileIconsViewModel - .viewModelForSub(iconData.subId, - StatusBarLocation.SHADE_CARRIER_GROUP) - ); - carrier.addModernMobileView(modernMobileView); + if (Flags.statusBarMobileIconKairos()) { + for (Job job : mBindingJobs) { + job.cancel(new CancellationException()); + } + mBindingJobs.clear(); + MobileIconsViewModelKairos mobileIconsViewModel = + mMobileUiAdapterKairos.get().getMobileIconsViewModel(); + for (IconData iconData : iconDataList) { + ShadeCarrier carrier = mCarrierGroups[iconData.slotIndex]; + + Context mobileContext = + mMobileContextProvider.getMobileContextForSub(iconData.subId, mContext); + + Pair<ModernShadeCarrierGroupMobileView, Job> viewAndJob = + ModernShadeCarrierGroupMobileView.constructAndBind( + mobileContext, + mobileIconsViewModel.getLogger(), + "mobile_carrier_shade_group", + mobileIconsViewModel.shadeCarrierGroupIcon(iconData.subId), + mAppScope, + iconData.subId, + StatusBarLocation.SHADE_CARRIER_GROUP, + mKairosNetwork + ); + mBindingJobs.add(viewAndJob.getSecond()); + carrier.addModernMobileView(viewAndJob.getFirst()); + } + } else { + for (IconData iconData : iconDataList) { + ShadeCarrier carrier = mCarrierGroups[iconData.slotIndex]; + + Context mobileContext = + mMobileContextProvider.getMobileContextForSub(iconData.subId, mContext); + + ModernShadeCarrierGroupMobileView modernMobileView = + ModernShadeCarrierGroupMobileView + .constructAndBind( + mobileContext, + mMobileIconsViewModel.getLogger(), + "mobile_carrier_shade_group", + (ShadeCarrierGroupMobileIconViewModel) mMobileIconsViewModel + .viewModelForSub(iconData.subId, + StatusBarLocation.SHADE_CARRIER_GROUP) + ); + carrier.addModernMobileView(modernMobileView); + } } } @@ -288,7 +351,6 @@ public class ShadeCarrierGroupController { * Sets a {@link OnSingleCarrierChangedListener}. * * This will get notified when the number of carriers changes between 1 and "not one". - * @param listener */ public void setOnSingleCarrierChangedListener( @Nullable OnSingleCarrierChangedListener listener) { @@ -489,6 +551,9 @@ public class ShadeCarrierGroupController { private final MobileUiAdapter mMobileUiAdapter; private final MobileContextProvider mMobileContextProvider; private final StatusBarPipelineFlags mStatusBarPipelineFlags; + private final CoroutineScope mAppScope; + private final KairosNetwork mKairosNetwork; + private final Lazy<MobileUiAdapterKairos> mMobileUiAdapterKairos; @Inject public Builder( @@ -503,7 +568,10 @@ public class ShadeCarrierGroupController { SlotIndexResolver slotIndexResolver, MobileUiAdapter mobileUiAdapter, MobileContextProvider mobileContextProvider, - StatusBarPipelineFlags statusBarPipelineFlags + StatusBarPipelineFlags statusBarPipelineFlags, + @Application CoroutineScope appScope, + KairosNetwork kairosNetwork, + Lazy<MobileUiAdapterKairos> mobileUiAdapterKairos ) { mActivityStarter = activityStarter; mHandler = handler; @@ -517,6 +585,9 @@ public class ShadeCarrierGroupController { mMobileUiAdapter = mobileUiAdapter; mMobileContextProvider = mobileContextProvider; mStatusBarPipelineFlags = statusBarPipelineFlags; + mAppScope = appScope; + mKairosNetwork = kairosNetwork; + mMobileUiAdapterKairos = mobileUiAdapterKairos; } public Builder setShadeCarrierGroup(ShadeCarrierGroup view) { @@ -538,8 +609,10 @@ public class ShadeCarrierGroupController { mSlotIndexResolver, mMobileUiAdapter, mMobileContextProvider, - mStatusBarPipelineFlags - ); + mStatusBarPipelineFlags, + mMobileUiAdapterKairos, + mAppScope, + mKairosNetwork); } } @@ -571,7 +644,8 @@ public class ShadeCarrierGroupController { public static class SubscriptionManagerSlotIndexResolver implements SlotIndexResolver { @Inject - public SubscriptionManagerSlotIndexResolver() {} + public SubscriptionManagerSlotIndexResolver() { + } @Override public int getSlotIndex(int subscriptionId) { 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 5609326362fc..88242762da78 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 @@ -14,6 +14,8 @@ * limitations under the License. */ +@file:OptIn(ExperimentalKairosApi::class) + package com.android.systemui.shade.ui.viewmodel import android.content.Context @@ -28,6 +30,8 @@ import androidx.compose.ui.graphics.Color import com.android.app.tracing.coroutines.launchTraced as launch import com.android.compose.animation.scene.OverlayKey import com.android.systemui.battery.BatteryMeterViewController +import com.android.systemui.kairos.ExperimentalKairosApi +import com.android.systemui.kairos.KairosNetwork import com.android.systemui.lifecycle.ExclusiveActivatable import com.android.systemui.lifecycle.Hydrator import com.android.systemui.plugins.ActivityStarter @@ -47,6 +51,7 @@ import com.android.systemui.statusbar.phone.ui.StatusBarIconController import com.android.systemui.statusbar.phone.ui.TintedIconManager import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractor import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsViewModel +import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsViewModelKairos import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import java.util.Locale @@ -76,6 +81,8 @@ constructor( private val tintedIconManagerFactory: TintedIconManager.Factory, private val batteryMeterViewControllerFactory: BatteryMeterViewController.Factory, val statusBarIconController: StatusBarIconController, + val kairosNetwork: KairosNetwork, + val mobileIconsViewModelKairos: dagger.Lazy<MobileIconsViewModelKairos>, ) : ExclusiveActivatable() { private val hydrator = Hydrator("ShadeHeaderViewModel.hydrator") diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt b/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt index 2bacee12db8a..9940ae523b60 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/BlurUtils.kt @@ -16,12 +16,15 @@ package com.android.systemui.statusbar +import android.annotation.SuppressLint import android.app.ActivityManager import android.content.res.Resources +import android.os.Build import android.os.SystemProperties import android.os.Trace import android.os.Trace.TRACE_TAG_APP import android.util.IndentingPrintWriter +import android.util.Log import android.util.MathUtils import android.view.CrossWindowBlurListeners import android.view.CrossWindowBlurListeners.CROSS_WINDOW_BLUR_SUPPORTED @@ -45,7 +48,7 @@ open class BlurUtils @Inject constructor( private val crossWindowBlurListeners: CrossWindowBlurListeners, dumpManager: DumpManager ) : Dumpable { - val minBlurRadius = resources.getDimensionPixelSize(R.dimen.min_window_blur_radius).toFloat(); + val minBlurRadius = resources.getDimensionPixelSize(R.dimen.min_window_blur_radius).toFloat() val maxBlurRadius = if (Flags.notificationShadeBlur()) { blurConfig.maxBlurRadiusPx } else { @@ -55,6 +58,9 @@ open class BlurUtils @Inject constructor( private var lastAppliedBlur = 0 private var earlyWakeupEnabled = false + /** When this is true, early wakeup flag is not reset on surface flinger when blur drops to 0 */ + private var persistentEarlyWakeupRequired = false + init { dumpManager.registerDumpable(this) } @@ -91,11 +97,8 @@ open class BlurUtils @Inject constructor( return } if (lastAppliedBlur == 0 && radius != 0) { - Trace.asyncTraceForTrackBegin( - TRACE_TAG_APP, TRACK_NAME, "eEarlyWakeup (prepareBlur)", 0) - earlyWakeupEnabled = true createTransaction().use { - it.setEarlyWakeupStart() + earlyWakeupStart(it, "eEarlyWakeup (prepareBlur)") it.apply() } } @@ -116,19 +119,15 @@ open class BlurUtils @Inject constructor( if (shouldBlur(radius)) { it.setBackgroundBlurRadius(viewRootImpl.surfaceControl, radius) if (!earlyWakeupEnabled && lastAppliedBlur == 0 && radius != 0) { - Trace.asyncTraceForTrackBegin( - TRACE_TAG_APP, - TRACK_NAME, - "eEarlyWakeup (applyBlur)", - 0 - ) - it.setEarlyWakeupStart() - earlyWakeupEnabled = true + earlyWakeupStart(it, "eEarlyWakeup (applyBlur)") } - if (earlyWakeupEnabled && lastAppliedBlur != 0 && radius == 0) { - it.setEarlyWakeupEnd() - Trace.asyncTraceForTrackEnd(TRACE_TAG_APP, TRACK_NAME, 0) - earlyWakeupEnabled = false + if ( + earlyWakeupEnabled && + lastAppliedBlur != 0 && + radius == 0 && + !persistentEarlyWakeupRequired + ) { + earlyWakeupEnd(it, "applyBlur") } lastAppliedBlur = radius } @@ -137,6 +136,26 @@ open class BlurUtils @Inject constructor( } } + private fun v(verboseLog: String) { + if (isLoggable) Log.v(TAG, verboseLog) + } + + @SuppressLint("MissingPermission") + private fun earlyWakeupStart(transaction: SurfaceControl.Transaction, traceMethodName: String) { + v("earlyWakeupStart from $traceMethodName") + Trace.asyncTraceForTrackBegin(TRACE_TAG_APP, TRACK_NAME, traceMethodName, 0) + transaction.setEarlyWakeupStart() + earlyWakeupEnabled = true + } + + @SuppressLint("MissingPermission") + private fun earlyWakeupEnd(transaction: SurfaceControl.Transaction, loggingContext: String) { + v("earlyWakeupEnd from $loggingContext") + transaction.setEarlyWakeupEnd() + Trace.asyncTraceForTrackEnd(TRACE_TAG_APP, TRACK_NAME, 0) + earlyWakeupEnabled = false + } + @VisibleForTesting open fun createTransaction(): SurfaceControl.Transaction { return SurfaceControl.Transaction() @@ -177,7 +196,39 @@ open class BlurUtils @Inject constructor( } } + /** + * Enables/disables the early wakeup flag on surface flinger. Keeps the early wakeup flag on + * until it reset by passing false to this method. + */ + fun setPersistentEarlyWakeup(persistentWakeup: Boolean, viewRootImpl: ViewRootImpl?) { + persistentEarlyWakeupRequired = persistentWakeup + if (viewRootImpl == null || !supportsBlursOnWindows()) return + if (persistentEarlyWakeupRequired) { + if (earlyWakeupEnabled) return + createTransaction().use { + earlyWakeupStart(it, "setEarlyWakeup") + it.apply() + } + } else { + if (!earlyWakeupEnabled) return + if (lastAppliedBlur > 0) { + Log.w( + TAG, + "resetEarlyWakeup invoked when lastAppliedBlur $lastAppliedBlur is " + + "non-zero, this means that the early wakeup signal was reset while blur" + + " was still active", + ) + } + createTransaction().use { + earlyWakeupEnd(it, "resetEarlyWakeup") + it.apply() + } + } + } + companion object { const val TRACK_NAME = "BlurUtils" + private const val TAG = "BlurUtils" + private val isLoggable = Log.isLoggable(TAG, Log.VERBOSE) || Build.isDebuggable() } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java index e44701dba87c..4daf61a895c7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java @@ -64,6 +64,7 @@ import androidx.annotation.VisibleForTesting; import com.android.internal.annotations.KeepForWeakReference; import com.android.internal.os.SomeArgs; +import com.android.internal.statusbar.DisableStates; import com.android.internal.statusbar.IAddTileResultCallback; import com.android.internal.statusbar.IStatusBar; import com.android.internal.statusbar.IUndoMediaTransferCallback; @@ -85,6 +86,7 @@ import java.io.FileOutputStream; import java.io.OutputStream; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.Map; /** * This class takes the functions from IStatusBar that come in on @@ -184,6 +186,8 @@ public class CommandQueue extends IStatusBar.Stub implements private static final int MSG_TOGGLE_QUICK_SETTINGS_PANEL = 82 << MSG_SHIFT; private static final int MSG_WALLET_ACTION_LAUNCH_GESTURE = 83 << MSG_SHIFT; private static final int MSG_DISPLAY_REMOVE_SYSTEM_DECORATIONS = 85 << MSG_SHIFT; + private static final int MSG_DISABLE_ALL = 86 << MSG_SHIFT; + public static final int FLAG_EXCLUDE_NONE = 0; public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0; public static final int FLAG_EXCLUDE_RECENTS_PANEL = 1 << 1; @@ -654,7 +658,8 @@ public class CommandQueue extends IStatusBar.Stub implements /** * Called to notify that disable flags are updated. - * @see Callbacks#disable(int, int, int, boolean). + * @see Callbacks#disable(int, int, int, boolean) + * @see Callbacks#disableForAllDisplays(DisableStates) */ public void disable(int displayId, @DisableFlags int state1, @Disable2Flags int state2, boolean animate) { @@ -682,6 +687,27 @@ public class CommandQueue extends IStatusBar.Stub implements disable(displayId, state1, state2, true); } + @Override + public void disableForAllDisplays(DisableStates disableStates) throws RemoteException { + synchronized (mLock) { + for (Map.Entry<Integer, Pair<Integer, Integer>> displaysWithStates : + disableStates.displaysWithStates.entrySet()) { + int displayId = displaysWithStates.getKey(); + Pair<Integer, Integer> states = displaysWithStates.getValue(); + setDisabled(displayId, states.first, states.second); + } + mHandler.removeMessages(MSG_DISABLE_ALL); + Message msg = mHandler.obtainMessage(MSG_DISABLE_ALL, disableStates); + if (Looper.myLooper() == mHandler.getLooper()) { + // If its the right looper execute immediately so hides can be handled quickly. + mHandler.handleMessage(msg); + msg.recycle(); + } else { + msg.sendToTarget(); + } + } + } + /** * Apply current disable flags by {@link CommandQueue#disable(int, int, int, boolean)}. * @@ -1552,6 +1578,21 @@ public class CommandQueue extends IStatusBar.Stub implements args.argi4 != 0 /* animate */); } break; + case MSG_DISABLE_ALL: + DisableStates disableStates = (DisableStates) msg.obj; + boolean animate = disableStates.animate; + Map<Integer, Pair<Integer, Integer>> displaysWithDisableStates = + disableStates.displaysWithStates; + for (Map.Entry<Integer, Pair<Integer, Integer>> displayWithDisableStates : + displaysWithDisableStates.entrySet()) { + int displayId = displayWithDisableStates.getKey(); + Pair<Integer, Integer> states = displayWithDisableStates.getValue(); + for (int i = 0; i < mCallbacks.size(); i++) { + mCallbacks.get(i).disable(displayId, states.first, states.second, + animate); + } + } + break; case MSG_EXPAND_NOTIFICATIONS: for (int i = 0; i < mCallbacks.size(); i++) { mCallbacks.get(i).animateExpandNotificationsPanel(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGroupingUtil.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGroupingUtil.java index 03c191e40ccf..2030606e4274 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGroupingUtil.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGroupingUtil.java @@ -38,6 +38,7 @@ import com.android.internal.R; import com.android.internal.widget.CachingIconView; import com.android.internal.widget.ConversationLayout; import com.android.internal.widget.ImageFloatingTextView; +import com.android.systemui.statusbar.notification.icon.IconPack; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.NotificationContentView; import com.android.systemui.statusbar.notification.row.shared.AsyncGroupHeaderViewInflation; @@ -61,10 +62,19 @@ public class NotificationGroupingUtil { private static final VisibilityApplicator VISIBILITY_APPLICATOR = new VisibilityApplicator(); private static final VisibilityApplicator APP_NAME_APPLICATOR = new AppNameApplicator(); private static final ResultApplicator LEFT_ICON_APPLICATOR = new LeftIconApplicator(); - private static final DataExtractor ICON_EXTRACTOR = new DataExtractor() { + + @VisibleForTesting + static final DataExtractor ICON_EXTRACTOR = new DataExtractor() { @Override public Object extractData(ExpandableNotificationRow row) { - return row.getEntry().getSbn().getNotification(); + if (NotificationBundleUi.isEnabled()) { + if (row.getEntryAdapter().getSbn() != null) { + return row.getEntryAdapter().getSbn().getNotification(); + } + return null; + } else { + return row.getEntry().getSbn().getNotification(); + } } }; @@ -253,7 +263,7 @@ public class NotificationGroupingUtil { if (NotificationBundleUi.isEnabled()) { sbn = row.getEntryAdapter() != null ? row.getEntryAdapter().getSbn() : null; } else { - sbn = row.getEntry().getSbn(); + sbn = row.getEntryLegacy().getSbn(); } return (sbn != null && sbn.getNotification().showsTime()); } @@ -357,7 +367,8 @@ public class NotificationGroupingUtil { boolean isEmpty(View view); } - private interface DataExtractor { + @VisibleForTesting + interface DataExtractor { Object extractData(ExpandableNotificationRow row); } @@ -395,13 +406,17 @@ public class NotificationGroupingUtil { } } - private abstract static class IconComparator implements ViewComparator { + @VisibleForTesting + static class IconComparator implements ViewComparator { @Override public boolean compare(View parent, View child, Object parentData, Object childData) { return false; } protected boolean hasSameIcon(Object parentData, Object childData) { + if (parentData == null || childData == null) { + return false; + } Icon parentIcon = ((Notification) parentData).getSmallIcon(); Icon childIcon = ((Notification) childData).getSmallIcon(); return parentIcon.sameAs(childIcon); @@ -411,6 +426,10 @@ public class NotificationGroupingUtil { * @return whether two ImageViews have the same colorFilterSet or none at all */ protected boolean hasSameColor(Object parentData, Object childData) { + if ((parentData == null && childData != null) + || (parentData != null && childData == null)) { + return false; + } int parentColor = ((Notification) parentData).color; int childColor = ((Notification) childData).color; return parentColor == childColor; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt index 472dc823423e..e292bcf1f7a8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShadeDepthController.kt @@ -24,6 +24,7 @@ import android.util.IndentingPrintWriter import android.util.Log import android.util.MathUtils import android.view.Choreographer +import android.view.Display import android.view.View import androidx.annotation.VisibleForTesting import androidx.dynamicanimation.animation.FloatPropertyCompat @@ -42,7 +43,9 @@ import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.shade.ShadeExpansionChangeEvent import com.android.systemui.shade.ShadeExpansionListener +import com.android.systemui.shade.data.repository.ShadeDisplaysRepository import com.android.systemui.shade.domain.interactor.ShadeModeInteractor +import com.android.systemui.shade.shared.flag.ShadeWindowGoesAround import com.android.systemui.statusbar.phone.BiometricUnlockController import com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_WAKE_AND_UNLOCK import com.android.systemui.statusbar.phone.DozeParameters @@ -52,6 +55,7 @@ import com.android.systemui.util.WallpaperController import com.android.systemui.wallpapers.domain.interactor.WallpaperInteractor import com.android.systemui.window.domain.interactor.WindowRootViewBlurInteractor import com.android.wm.shell.appzoomout.AppZoomOut +import dagger.Lazy import java.io.PrintWriter import java.util.Optional import javax.inject.Inject @@ -83,6 +87,7 @@ constructor( private val appZoomOutOptional: Optional<AppZoomOut>, @Application private val applicationScope: CoroutineScope, dumpManager: DumpManager, + private val shadeDisplaysRepository: Lazy<ShadeDisplaysRepository>, ) : ShadeExpansionListener, Dumpable { companion object { private const val WAKE_UP_ANIMATION_ENABLED = true @@ -228,6 +233,14 @@ constructor( private data class WakeAndUnlockBlurData(val radius: Float, val useZoom: Boolean = true) + private val isShadeOnDefaultDisplay: Boolean + get() = + if (ShadeWindowGoesAround.isEnabled) { + shadeDisplaysRepository.get().displayId.value == Display.DEFAULT_DISPLAY + } else { + true + } + /** Blur radius of the wake and unlock animation on this frame, and whether to zoom out. */ private var wakeAndUnlockBlurData = WakeAndUnlockBlurData(0f) set(value) { @@ -265,9 +278,14 @@ constructor( var blur = shadeRadius.toInt() // If the blur comes from waking up, we don't want to zoom out the background val zoomOut = - if (shadeRadius != wakeAndUnlockBlurData.radius|| wakeAndUnlockBlurData.useZoom) - blurRadiusToZoomOut(blurRadius = shadeRadius) - else 0f + when { + // When the shade is in another display, we don't want to zoom out the background. + // Only the default display is supported right now. + !isShadeOnDefaultDisplay -> 0f + shadeRadius != wakeAndUnlockBlurData.radius || wakeAndUnlockBlurData.useZoom -> + blurRadiusToZoomOut(blurRadius = shadeRadius) + else -> 0f + } // Make blur be 0 if it is necessary to stop blur effect. if (scrimsVisible) { if (!Flags.notificationShadeBlur()) { @@ -353,7 +371,9 @@ constructor( interpolator = Interpolators.FAST_OUT_SLOW_IN addUpdateListener { animation: ValueAnimator -> wakeAndUnlockBlurData = - WakeAndUnlockBlurData(blurUtils.blurRadiusOfRatio(animation.animatedValue as Float)) + WakeAndUnlockBlurData( + blurUtils.blurRadiusOfRatio(animation.animatedValue as Float) + ) } addListener( object : AnimatorListenerAdapter() { @@ -428,8 +448,10 @@ constructor( applicationScope.launch { wallpaperInteractor.wallpaperSupportsAmbientMode.collect { supported -> wallpaperSupportsAmbientMode = supported - if (getNewWakeBlurRadius(prevDozeAmount) == wakeAndUnlockBlurData.radius - && !wakeAndUnlockBlurData.useZoom) { + if ( + getNewWakeBlurRadius(prevDozeAmount) == wakeAndUnlockBlurData.radius && + !wakeAndUnlockBlurData.useZoom + ) { // Update wake and unlock radius only if the previous value comes from wake-up. updateWakeBlurRadius(prevDozeAmount) } @@ -452,6 +474,21 @@ constructor( scheduleUpdate() } } + + applicationScope.launch { + windowRootViewBlurInteractor.isBlurCurrentlySupported.collect { supported -> + if (supported) { + // when battery saver changes, try scheduling an update. + scheduleUpdate() + } else { + // when blur becomes unsupported, no more updates will be scheduled, + // reset updateScheduled state. + updateScheduled = false + // reset blur and internal state to 0 + onBlurApplied(0, 0.0f) + } + } + } } fun addListener(listener: DepthListener) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt index 922afc7825c4..9cae6c135fb0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/call/ui/viewmodel/CallChipViewModel.kt @@ -329,6 +329,7 @@ constructor( cookie, component, launchCujType = Cuj.CUJ_STATUS_BAR_APP_LAUNCH_FROM_CALL_CHIP, + returnCujType = Cuj.CUJ_STATUS_BAR_APP_RETURN_TO_CALL_CHIP, ) { override suspend fun createController( forLaunch: Boolean diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModel.kt index 6ce350cb95f5..c862460ad6f7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/casttootherdevice/ui/viewmodel/CastToOtherDeviceChipViewModel.kt @@ -194,6 +194,7 @@ constructor( ): OngoingActivityChipModel.Active { return OngoingActivityChipModel.Active.Timer( key = KEY, + isImportantForPrivacy = true, icon = OngoingActivityChipModel.ChipIcon.SingleColorIcon( Icon.Resource( @@ -232,6 +233,7 @@ constructor( private fun createIconOnlyCastChip(deviceName: String?): OngoingActivityChipModel.Active { return OngoingActivityChipModel.Active.IconOnly( key = KEY, + isImportantForPrivacy = true, icon = OngoingActivityChipModel.ChipIcon.SingleColorIcon( Icon.Resource( diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/model/NotificationChipModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/model/NotificationChipModel.kt index 1f2079d83e6f..356731cb3777 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/model/NotificationChipModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/domain/model/NotificationChipModel.kt @@ -17,7 +17,7 @@ package com.android.systemui.statusbar.chips.notification.domain.model import com.android.systemui.statusbar.StatusBarIconView -import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel +import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModels /** Modeling all the data needed to render a status bar notification chip. */ data class NotificationChipModel( @@ -25,7 +25,7 @@ data class NotificationChipModel( /** The user-readable name of the app that posted this notification. */ val appName: String, val statusBarChipIconView: StatusBarIconView?, - val promotedContent: PromotedNotificationContentModel, + val promotedContent: PromotedNotificationContentModels, /** The time when the notification first appeared as promoted. */ val creationTime: Long, /** True if the app managing this notification is currently visible to the user. */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt index 1a802d634894..dfbd12d5a215 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/notification/ui/viewmodel/NotifChipsViewModel.kt @@ -72,6 +72,8 @@ constructor( headsUpState: TopPinnedState ): OngoingActivityChipModel.Active { StatusBarNotifChips.unsafeAssertInNewMode() + // Chips are never shown when locked, so it's safe to use the version with sensitive content + val chipContent = promotedContent.privateVersion val contentDescription = getContentDescription(this.appName) val icon = if (this.statusBarChipIconView != null) { @@ -115,65 +117,62 @@ constructor( // If the user tapped this chip to show the HUN, we want to just show the icon because // the HUN will show the rest of the information. return OngoingActivityChipModel.Active.IconOnly( - this.key, - icon, - colors, - onClickListenerLegacy, - clickBehavior, + key = this.key, + icon = icon, + colors = colors, + onClickListenerLegacy = onClickListenerLegacy, + clickBehavior = clickBehavior, ) } - if (this.promotedContent.shortCriticalText != null) { + if (chipContent.shortCriticalText != null) { return OngoingActivityChipModel.Active.Text( - this.key, - icon, - colors, - this.promotedContent.shortCriticalText, - onClickListenerLegacy, - clickBehavior, + key = this.key, + icon = icon, + colors = colors, + text = chipContent.shortCriticalText, + onClickListenerLegacy = onClickListenerLegacy, + clickBehavior = clickBehavior, ) } - if ( - Flags.promoteNotificationsAutomatically() && - this.promotedContent.wasPromotedAutomatically - ) { + if (Flags.promoteNotificationsAutomatically() && chipContent.wasPromotedAutomatically) { // When we're promoting notifications automatically, the `when` time set on the // notification will likely just be set to the current time, which would cause the chip // to always show "now". We don't want early testers to get that experience since it's // not what will happen at launch, so just don't show any time.onometerstate return OngoingActivityChipModel.Active.IconOnly( - this.key, - icon, - colors, - onClickListenerLegacy, - clickBehavior, + key = this.key, + icon = icon, + colors = colors, + onClickListenerLegacy = onClickListenerLegacy, + clickBehavior = clickBehavior, ) } - if (this.promotedContent.time == null) { + if (chipContent.time == null) { return OngoingActivityChipModel.Active.IconOnly( - this.key, - icon, - colors, - onClickListenerLegacy, - clickBehavior, + key = this.key, + icon = icon, + colors = colors, + onClickListenerLegacy = onClickListenerLegacy, + clickBehavior = clickBehavior, ) } - when (this.promotedContent.time) { + when (chipContent.time) { is PromotedNotificationContentModel.When.Time -> { return if ( - this.promotedContent.time.currentTimeMillis >= + chipContent.time.currentTimeMillis >= systemClock.currentTimeMillis() + FUTURE_TIME_THRESHOLD_MILLIS ) { OngoingActivityChipModel.Active.ShortTimeDelta( - this.key, - icon, - colors, - time = this.promotedContent.time.currentTimeMillis, - onClickListenerLegacy, - clickBehavior, + key = this.key, + icon = icon, + colors = colors, + time = chipContent.time.currentTimeMillis, + onClickListenerLegacy = onClickListenerLegacy, + clickBehavior = clickBehavior, ) } else { // Don't show a `when` time that's close to now or in the past because it's @@ -185,21 +184,21 @@ constructor( // automatically handles this for us and we're hoping to launch the notification // chips at the same time as the Compose chips. return OngoingActivityChipModel.Active.IconOnly( - this.key, - icon, - colors, - onClickListenerLegacy, - clickBehavior, + key = this.key, + icon = icon, + colors = colors, + onClickListenerLegacy = onClickListenerLegacy, + clickBehavior = clickBehavior, ) } } is PromotedNotificationContentModel.When.Chronometer -> { return OngoingActivityChipModel.Active.Timer( - this.key, - icon, - colors, - startTimeMs = this.promotedContent.time.elapsedRealtimeMillis, - isEventInFuture = this.promotedContent.time.isCountDown, + key = this.key, + icon = icon, + colors = colors, + startTimeMs = chipContent.time.elapsedRealtimeMillis, + isEventInFuture = chipContent.time.isCountDown, onClickListenerLegacy = onClickListenerLegacy, clickBehavior = clickBehavior, ) 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 572c7faf19e6..fce428dcb7e1 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 @@ -94,9 +94,11 @@ constructor( TAG, LogLevel.INFO, {}, - { "State: Recording(taskPackage=null) due to force-start" }, + { + "State: Recording(hostPackage=null, taskPackage=null) due to force-start" + }, ) - ScreenRecordChipModel.Recording(recordedTask = null) + ScreenRecordChipModel.Recording(hostPackage = null, recordedTask = null) } else { when (screenRecordState) { is ScreenRecordModel.DoingNothing -> { @@ -124,13 +126,25 @@ constructor( } else { null } + val hostPackage = + if (mediaProjectionState is MediaProjectionState.Projecting) { + mediaProjectionState.hostPackage + } else { + null + } logger.log( TAG, LogLevel.INFO, - { str1 = recordedTask?.baseIntent?.component?.packageName }, - { "State: Recording(taskPackage=$str1)" }, + { + str1 = hostPackage + str2 = recordedTask?.baseIntent?.component?.packageName + }, + { "State: Recording(hostPackage=$str1, taskPackage=$str2)" }, + ) + ScreenRecordChipModel.Recording( + hostPackage = hostPackage, + recordedTask = recordedTask, ) - ScreenRecordChipModel.Recording(recordedTask) } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/domain/model/ScreenRecordChipModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/domain/model/ScreenRecordChipModel.kt index ba6cd4df8d47..0a7278792e70 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/domain/model/ScreenRecordChipModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/domain/model/ScreenRecordChipModel.kt @@ -29,10 +29,13 @@ sealed interface ScreenRecordChipModel { /** * There's an active screen recording happening. * + * @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). * @property recordedTask the task being recorded if the user is recording only a single app. * Null if the user is recording the entire screen or we don't have the task info yet. */ data class Recording( + val hostPackage: String?, val recordedTask: ActivityManager.RunningTaskInfo?, ) : ScreenRecordChipModel } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModel.kt index 55c89a96422f..363b8beab2d7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/ui/viewmodel/ScreenRecordChipViewModel.kt @@ -77,6 +77,7 @@ constructor( is ScreenRecordChipModel.Starting -> { OngoingActivityChipModel.Active.Countdown( key = KEY, + isImportantForPrivacy = true, colors = ColorsModel.Red, secondsUntilStarted = state.millisUntilStarted.toCountdownSeconds(), ) @@ -84,6 +85,7 @@ constructor( is ScreenRecordChipModel.Recording -> { OngoingActivityChipModel.Active.Timer( key = KEY, + isImportantForPrivacy = true, icon = OngoingActivityChipModel.ChipIcon.SingleColorIcon( Icon.Resource( diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModel.kt index 92e17bdd511a..0defa531d18d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/sharetoapp/ui/viewmodel/ShareToAppChipViewModel.kt @@ -222,6 +222,7 @@ constructor( ): OngoingActivityChipModel.Active { return OngoingActivityChipModel.Active.Timer( key = KEY, + isImportantForPrivacy = true, icon = OngoingActivityChipModel.ChipIcon.SingleColorIcon( Icon.Resource( @@ -257,6 +258,7 @@ constructor( private fun createIconOnlyShareToAppChip(): OngoingActivityChipModel.Active { return OngoingActivityChipModel.Active.IconOnly( key = KEY, + isImportantForPrivacy = true, icon = OngoingActivityChipModel.ChipIcon.SingleColorIcon( Icon.Resource( diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChip.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChip.kt index b94e7b249233..104c2b546200 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChip.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/compose/OngoingActivityChip.kt @@ -107,11 +107,15 @@ fun OngoingActivityChip( } } .thenIf(isClickable) { Modifier.widthIn(min = minWidth) } - .layout { measurable, constraints -> - val placeable = measurable.measure(constraints) - layout(placeable.width, placeable.height) { - if (constraints.maxWidth >= minWidth.roundToPx()) { - placeable.place(0, 0) + // For non-privacy-related chips, only show the chip if there's enough space for at + // least the minimum width. + .thenIf(!model.isImportantForPrivacy) { + Modifier.layout { measurable, constraints -> + val placeable = measurable.measure(constraints) + layout(placeable.width, placeable.height) { + if (constraints.maxWidth >= minWidth.roundToPx()) { + placeable.place(0, 0) + } } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt index 364e6656ee9d..e28f3684b0fa 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/model/OngoingActivityChipModel.kt @@ -57,6 +57,11 @@ sealed class OngoingActivityChipModel { * A key that uniquely identifies this chip. Used for better visual effects, like animation. */ open val key: String, + /** + * True if this chip is critical for privacy so we should keep it visible at all times, and + * false otherwise. + */ + open val isImportantForPrivacy: Boolean = false, /** The icon to show on the chip. If null, no icon will be shown. */ open val icon: ChipIcon?, /** What colors to use for the chip. */ @@ -81,6 +86,7 @@ sealed class OngoingActivityChipModel { /** This chip shows only an icon and nothing else. */ data class IconOnly( override val key: String, + override val isImportantForPrivacy: Boolean = false, override val icon: ChipIcon, override val colors: ColorsModel, override val onClickListenerLegacy: View.OnClickListener?, @@ -91,6 +97,7 @@ sealed class OngoingActivityChipModel { ) : Active( key, + isImportantForPrivacy, icon, colors, onClickListenerLegacy, @@ -105,6 +112,7 @@ sealed class OngoingActivityChipModel { /** The chip shows a timer, counting up from [startTimeMs]. */ data class Timer( override val key: String, + override val isImportantForPrivacy: Boolean = false, override val icon: ChipIcon, override val colors: ColorsModel, /** @@ -138,6 +146,7 @@ sealed class OngoingActivityChipModel { ) : Active( key, + isImportantForPrivacy, icon, colors, onClickListenerLegacy, @@ -155,6 +164,7 @@ sealed class OngoingActivityChipModel { */ data class ShortTimeDelta( override val key: String, + override val isImportantForPrivacy: Boolean = false, override val icon: ChipIcon, override val colors: ColorsModel, /** @@ -175,6 +185,7 @@ sealed class OngoingActivityChipModel { ) : Active( key, + isImportantForPrivacy, icon, colors, onClickListenerLegacy, @@ -196,6 +207,7 @@ sealed class OngoingActivityChipModel { */ data class Countdown( override val key: String, + override val isImportantForPrivacy: Boolean = false, override val colors: ColorsModel, /** The number of seconds until an event is started. */ val secondsUntilStarted: Long, @@ -205,6 +217,7 @@ sealed class OngoingActivityChipModel { ) : Active( key, + isImportantForPrivacy, icon = null, colors, onClickListenerLegacy = null, @@ -219,6 +232,7 @@ sealed class OngoingActivityChipModel { /** This chip shows the specified [text] in the chip. */ data class Text( override val key: String, + override val isImportantForPrivacy: Boolean = false, override val icon: ChipIcon, override val colors: ColorsModel, // TODO(b/361346412): Enforce a max length requirement? @@ -231,6 +245,7 @@ sealed class OngoingActivityChipModel { ) : Active( key, + isImportantForPrivacy, icon, colors, onClickListenerLegacy, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt index 8228b5533fca..76d2af86e239 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModel.kt @@ -265,11 +265,12 @@ constructor( // [shouldSquish] returns false for that model, but protect against it just in case.) val currentIcon = icon ?: return this return OngoingActivityChipModel.Active.IconOnly( - key, - currentIcon, - colors, - onClickListenerLegacy, - clickBehavior, + key = key, + isImportantForPrivacy = isImportantForPrivacy, + icon = currentIcon, + colors = colors, + onClickListenerLegacy = onClickListenerLegacy, + clickBehavior = clickBehavior, ) } @@ -374,6 +375,9 @@ constructor( * Sort the given chip [bundle] in order of priority, and divide the chips between active, * overflow, and inactive (see [MultipleOngoingActivityChipsModel] for a description of each). */ + // IMPORTANT: PromotedNotificationsInteractor re-implements this same ordering scheme. Any + // changes here should also be made in PromotedNotificationsInteractor. + // TODO(b/402471288): Create a single source of truth for the ordering. private fun rankChips(bundle: ChipBundle): MultipleOngoingActivityChipsModel { val activeChips = mutableListOf<OngoingActivityChipModel.Active>() val overflowChips = mutableListOf<OngoingActivityChipModel.Active>() diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarOrchestratorStore.kt b/packages/SystemUI/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarOrchestratorStore.kt index 7964950a2917..499e3ae2fa5b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarOrchestratorStore.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/core/MultiDisplayStatusBarOrchestratorStore.kt @@ -16,10 +16,10 @@ package com.android.systemui.statusbar.core +import com.android.app.displaylib.PerDisplayRepository import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.display.data.repository.DisplayRepository -import com.android.systemui.display.data.repository.DisplayScopeRepository import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore import com.android.systemui.statusbar.data.repository.StatusBarPerDisplayStoreImpl import com.android.systemui.statusbar.phone.AutoHideControllerStore @@ -40,7 +40,7 @@ constructor( private val statusBarModeRepositoryStore: StatusBarModeRepositoryStore, private val initializerStore: StatusBarInitializerStore, private val autoHideControllerStore: AutoHideControllerStore, - private val displayScopeRepository: DisplayScopeRepository, + private val displayScopeRepository: PerDisplayRepository<CoroutineScope>, private val statusBarWindowStateRepositoryStore: StatusBarWindowStateRepositoryStore, ) : StatusBarPerDisplayStoreImpl<StatusBarOrchestrator>( @@ -59,10 +59,10 @@ constructor( val statusBarWindowController = statusBarWindowControllerStore.forDisplay(displayId) ?: return null val autoHideController = autoHideControllerStore.forDisplay(displayId) ?: return null + val displayScope = displayScopeRepository[displayId] ?: return null return factory.create( displayId, - // TODO: b/398825844 - Handle nullness to prevent leaking CoroutineScope. - displayScopeRepository.scopeForDisplay(displayId), + displayScope, statusBarWindowStateRepositoryStore.forDisplay(displayId), statusBarModeRepository, statusBarInitializer, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/LightBarControllerStore.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/LightBarControllerStore.kt index 3c0d6c3b8df3..b257c2bfc29a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/LightBarControllerStore.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/LightBarControllerStore.kt @@ -16,11 +16,11 @@ package com.android.systemui.statusbar.data.repository +import com.android.app.displaylib.PerDisplayRepository import com.android.systemui.CoreStartable import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.display.data.repository.DisplayRepository -import com.android.systemui.display.data.repository.DisplayScopeRepository import com.android.systemui.display.data.repository.PerDisplayStore import com.android.systemui.statusbar.phone.LightBarController import com.android.systemui.statusbar.phone.LightBarControllerImpl @@ -41,7 +41,7 @@ constructor( @Background backgroundApplicationScope: CoroutineScope, displayRepository: DisplayRepository, private val factory: LightBarControllerImpl.Factory, - private val displayScopeRepository: DisplayScopeRepository, + private val displayScopeRepository: PerDisplayRepository<CoroutineScope>, private val statusBarModeRepositoryStore: StatusBarModeRepositoryStore, private val darkIconDispatcherStore: DarkIconDispatcherStore, ) : @@ -55,13 +55,9 @@ constructor( val darkIconDispatcher = darkIconDispatcherStore.forDisplay(displayId) ?: return null val statusBarModePerDisplayRepository = statusBarModeRepositoryStore.forDisplay(displayId) ?: return null + val displayScope = displayScopeRepository[displayId] ?: return null return factory - .create( - displayId, - displayScopeRepository.scopeForDisplay(displayId), - darkIconDispatcher, - statusBarModePerDisplayRepository, - ) + .create(displayId, displayScope, darkIconDispatcher, statusBarModePerDisplayRepository) .also { it.start() } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/PrivacyDotViewControllerStore.kt b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/PrivacyDotViewControllerStore.kt index 32dc8407ac90..f3c68553abfd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/PrivacyDotViewControllerStore.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/data/repository/PrivacyDotViewControllerStore.kt @@ -16,11 +16,11 @@ package com.android.systemui.statusbar.data.repository +import com.android.app.displaylib.PerDisplayRepository import com.android.systemui.CoreStartable import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.display.data.repository.DisplayRepository -import com.android.systemui.display.data.repository.DisplayScopeRepository import com.android.systemui.display.data.repository.PerDisplayStore import com.android.systemui.display.data.repository.SingleDisplayStore import com.android.systemui.statusbar.core.StatusBarConnectedDisplays @@ -44,7 +44,7 @@ constructor( @Background backgroundApplicationScope: CoroutineScope, displayRepository: DisplayRepository, private val factory: PrivacyDotViewControllerImpl.Factory, - private val displayScopeRepository: DisplayScopeRepository, + private val displayScopeRepository: PerDisplayRepository<CoroutineScope>, private val statusBarConfigurationControllerStore: StatusBarConfigurationControllerStore, private val contentInsetsProviderStore: StatusBarContentInsetsProviderStore, ) : @@ -58,11 +58,8 @@ constructor( val configurationController = statusBarConfigurationControllerStore.forDisplay(displayId) ?: return null val contentInsetsProvider = contentInsetsProviderStore.forDisplay(displayId) ?: return null - return factory.create( - displayScopeRepository.scopeForDisplay(displayId), - configurationController, - contentInsetsProvider, - ) + val displayScope = displayScopeRepository[displayId] ?: return null + return factory.create(displayScope, configurationController, contentInsetsProvider) } override suspend fun onDisplayRemovalAction(instance: PrivacyDotViewController) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java index 4558017a98c8..b5ab0920a470 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotificationEntry.java @@ -68,6 +68,7 @@ import com.android.systemui.statusbar.notification.collection.notifcollection.No import com.android.systemui.statusbar.notification.headsup.PinnedStatus; import com.android.systemui.statusbar.notification.icon.IconPack; import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel; +import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModels; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.ExpandableNotificationRowController; import com.android.systemui.statusbar.notification.row.NotificationGuts; @@ -198,7 +199,7 @@ public final class NotificationEntry extends ListEntry { // TODO(b/377565433): Move into NotificationContentModel during/after // NotificationRowContentBinderRefactor. - private PromotedNotificationContentModel mPromotedNotificationContentModel; + private PromotedNotificationContentModels mPromotedNotificationContentModels; /** * True if both @@ -1106,9 +1107,9 @@ public final class NotificationEntry extends ListEntry { * Gets the content needed to render this notification as a promoted notification on various * surfaces (like status bar chips and AOD). */ - public PromotedNotificationContentModel getPromotedNotificationContentModel() { + public PromotedNotificationContentModels getPromotedNotificationContentModels() { if (PromotedNotificationContentModel.featureFlagEnabled()) { - return mPromotedNotificationContentModel; + return mPromotedNotificationContentModels; } else { Log.wtf(TAG, "getting promoted content without feature flag enabled", new Throwable()); return null; @@ -1127,10 +1128,10 @@ public final class NotificationEntry extends ListEntry { * Sets the content needed to render this notification as a promoted notification on various * surfaces (like status bar chips and AOD). */ - public void setPromotedNotificationContentModel( - @Nullable PromotedNotificationContentModel promotedNotificationContentModel) { + public void setPromotedNotificationContentModels( + @Nullable PromotedNotificationContentModels promotedNotificationContentModels) { if (PromotedNotificationContentModel.featureFlagEnabled()) { - this.mPromotedNotificationContentModel = promotedNotificationContentModel; + this.mPromotedNotificationContentModels = promotedNotificationContentModels; } else { Log.wtf(TAG, "setting promoted content without feature flag enabled", new Throwable()); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ColorizedFgsCoordinator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ColorizedFgsCoordinator.java index afba85b49c30..c8ec85d0a227 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ColorizedFgsCoordinator.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ColorizedFgsCoordinator.java @@ -24,10 +24,9 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.systemui.dagger.qualifiers.Application; -import com.android.systemui.statusbar.notification.collection.ListEntry; -import com.android.systemui.statusbar.notification.collection.PipelineEntry; import com.android.systemui.statusbar.notification.collection.NotifPipeline; import com.android.systemui.statusbar.notification.collection.NotificationEntry; +import com.android.systemui.statusbar.notification.collection.PipelineEntry; import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifComparator; import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter; @@ -100,7 +99,7 @@ public class ColorizedFgsCoordinator implements Coordinator { public boolean isInSection(PipelineEntry entry) { NotificationEntry notificationEntry = entry.getRepresentativeEntry(); if (notificationEntry != null) { - return isRichOngoing(notificationEntry); + return isRichOngoing(notificationEntry) || isPromotedNotifChip(notificationEntry); } return false; } @@ -159,4 +158,10 @@ public class ColorizedFgsCoordinator implements Coordinator { return entry.getImportance() > IMPORTANCE_MIN && notification.isStyle(Notification.CallStyle.class); } + + private boolean isPromotedNotifChip(NotificationEntry entry) { + return PromotedNotificationUi.isEnabled() + && entry.getImportance() > IMPORTANCE_MIN + && mOrderedPromotedNotifKeys.contains(entry.getKey()); + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt index a0eab43f854b..26b86f9ed74d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt @@ -15,6 +15,8 @@ */ package com.android.systemui.statusbar.notification.collection.coordinator +import com.android.systemui.Flags.notificationSkipSilentUpdates + import android.app.Notification import android.app.Notification.GROUP_ALERT_SUMMARY import android.util.ArrayMap @@ -465,15 +467,32 @@ constructor( } hunMutator.updateNotification(posted.key, pinnedStatus) } - } else { + } else { // shouldHeadsUpEver = false if (posted.isHeadsUpEntry) { - // We don't want this to be interrupting anymore, let's remove it - // If the notification is pinned by the user, the only way a user can un-pin - // it is by tapping the status bar notification chip. Since that's a clear - // user action, we should remove the HUN immediately instead of waiting for - // any sort of minimum timeout. - val shouldRemoveImmediately = posted.isPinnedByUser - hunMutator.removeNotification(posted.key, shouldRemoveImmediately) + if (notificationSkipSilentUpdates()) { + if (posted.isPinnedByUser) { + // We don't want this to be interrupting anymore, let's remove it + // If the notification is pinned by the user, the only way a user + // can un-pin it by tapping the status bar notification chip. Since + // that's a clear user action, we should remove the HUN immediately + // instead of waiting for any sort of minimum timeout. + // TODO(b/401068530) Ensure that status bar chip HUNs are not + // removed for silent update + hunMutator.removeNotification(posted.key, + /* releaseImmediately= */ true) + } else { + // Do NOT remove HUN for non-user update. + // Let the HUN show for its remaining duration. + } + } else { + // We don't want this to be interrupting anymore, let's remove it + // If the notification is pinned by the user, the only way a user can + // un-pin it is by tapping the status bar notification chip. Since + // that's a clear user action, we should remove the HUN immediately + // instead of waiting for any sort of minimum timeout. + val shouldRemoveImmediately = posted.isPinnedByUser + hunMutator.removeNotification(posted.key, shouldRemoveImmediately) + } } else { // Don't let the bind finish cancelHeadsUpBind(posted.entry) @@ -573,24 +592,34 @@ constructor( isBinding = isBinding, ) } - // Handle cancelling heads up here, rather than in the OnBeforeFinalizeFilter, so - // that - // work can be done before the ShadeListBuilder is run. This prevents re-entrant - // behavior between this Coordinator, HeadsUpManager, and VisualStabilityManager. - if (posted?.shouldHeadsUpEver == false) { - if (posted.isHeadsUpEntry) { - // We don't want this to be interrupting anymore, let's remove it - mHeadsUpManager.removeNotification( - posted.key, - /* removeImmediately= */ false, - "onEntryUpdated", - ) - } else if (posted.isBinding) { + if (notificationSkipSilentUpdates()) { + // TODO(b/403703828) Move canceling to OnBeforeFinalizeFilter, since we are not + // removing from HeadsUpManager and don't need to deal with re-entrant behavior + // between HeadsUpCoordinator, HeadsUpManager, and VisualStabilityManager. + if (posted?.shouldHeadsUpEver == false + && !posted.isHeadsUpEntry && posted.isBinding) { // Don't let the bind finish cancelHeadsUpBind(posted.entry) } + } else { + // Handle cancelling heads up here, rather than in the OnBeforeFinalizeFilter, + // so that work can be done before the ShadeListBuilder is run. This prevents + // re-entrant behavior between this Coordinator, HeadsUpManager, and + // VisualStabilityManager. + if (posted?.shouldHeadsUpEver == false) { + if (posted.isHeadsUpEntry) { + // We don't want this to be interrupting anymore, let's remove it + mHeadsUpManager.removeNotification( + posted.key, + /* removeImmediately= */ false, + "onEntryUpdated", + ) + } else if (posted.isBinding) { + // Don't let the bind finish + cancelHeadsUpBind(posted.entry) + } + } } - // Update last updated time for this entry setUpdateTime(entry, mSystemClock.currentTimeMillis()) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java index 1e5aa01714be..6042bff4bb97 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java @@ -83,7 +83,6 @@ import com.android.systemui.statusbar.notification.logging.dagger.NotificationsL import com.android.systemui.statusbar.notification.promoted.PromotedNotificationContentExtractor; import com.android.systemui.statusbar.notification.promoted.PromotedNotificationContentExtractorImpl; import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel; -import com.android.systemui.statusbar.notification.row.NotificationActionClickManager; import com.android.systemui.statusbar.notification.row.NotificationEntryProcessorFactory; import com.android.systemui.statusbar.notification.row.NotificationEntryProcessorFactoryLooperImpl; import com.android.systemui.statusbar.notification.row.NotificationGutsManager; @@ -325,7 +324,7 @@ public interface NotificationsModule { if (PromotedNotificationContentModel.featureFlagEnabled()) { return implProvider.get(); } else { - return (entry, recoveredBuilder, imageModelProvider) -> null; + return (entry, recoveredBuilder, redactionType, imageModelProvider) -> null; } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractor.kt index adc049e7cdf1..e7cc342ab65c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/domain/interactor/RenderNotificationListInteractor.kt @@ -20,6 +20,7 @@ import android.app.Notification.CallStyle.CALL_TYPE_ONGOING import android.app.Notification.CallStyle.CALL_TYPE_SCREENING import android.app.Notification.CallStyle.CALL_TYPE_UNKNOWN import android.app.Notification.EXTRA_CALL_TYPE +import android.app.Notification.FLAG_ONGOING_EVENT import android.app.PendingIntent import android.content.Context import android.graphics.drawable.Icon @@ -29,12 +30,13 @@ import com.android.app.tracing.traceSection import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.statusbar.StatusBarIconView import com.android.systemui.statusbar.notification.collection.GroupEntry -import com.android.systemui.statusbar.notification.collection.PipelineEntry import com.android.systemui.statusbar.notification.collection.NotificationEntry +import com.android.systemui.statusbar.notification.collection.PipelineEntry import com.android.systemui.statusbar.notification.collection.provider.SectionStyleProvider import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationListRepository import com.android.systemui.statusbar.notification.data.repository.ActiveNotificationsStore import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel +import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModels import com.android.systemui.statusbar.notification.shared.ActiveNotificationEntryModel import com.android.systemui.statusbar.notification.shared.ActiveNotificationGroupModel import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel @@ -140,7 +142,7 @@ private class ActiveNotificationsStoreBuilder( private fun NotificationEntry.toModel(): ActiveNotificationModel { val promotedContent = if (PromotedNotificationContentModel.featureFlagEnabled()) { - promotedNotificationContentModel + promotedNotificationContentModels } else { null } @@ -149,6 +151,8 @@ private class ActiveNotificationsStoreBuilder( key = key, groupKey = sbn.groupKey, whenTime = sbn.notification.`when`, + isForegroundService = sbn.notification.isForegroundService, + isOngoingEvent = (sbn.notification.flags and FLAG_ONGOING_EVENT) != 0, isAmbient = sectionStyleProvider.isMinimized(this), isRowDismissed = isRowDismissed, isSilent = sectionStyleProvider.isSilent(this), @@ -176,6 +180,8 @@ private fun ActiveNotificationsStore.createOrReuse( key: String, groupKey: String?, whenTime: Long, + isForegroundService: Boolean, + isOngoingEvent: Boolean, isAmbient: Boolean, isRowDismissed: Boolean, isSilent: Boolean, @@ -194,13 +200,15 @@ private fun ActiveNotificationsStore.createOrReuse( isGroupSummary: Boolean, bucket: Int, callType: CallType, - promotedContent: PromotedNotificationContentModel?, + promotedContent: PromotedNotificationContentModels?, ): ActiveNotificationModel { return individuals[key]?.takeIf { it.isCurrent( key = key, groupKey = groupKey, whenTime = whenTime, + isForegroundService = isForegroundService, + isOngoingEvent = isOngoingEvent, isAmbient = isAmbient, isRowDismissed = isRowDismissed, isSilent = isSilent, @@ -226,6 +234,8 @@ private fun ActiveNotificationsStore.createOrReuse( key = key, groupKey = groupKey, whenTime = whenTime, + isForegroundService = isForegroundService, + isOngoingEvent = isOngoingEvent, isAmbient = isAmbient, isRowDismissed = isRowDismissed, isSilent = isSilent, @@ -252,6 +262,8 @@ private fun ActiveNotificationModel.isCurrent( key: String, groupKey: String?, whenTime: Long, + isForegroundService: Boolean, + isOngoingEvent: Boolean, isAmbient: Boolean, isRowDismissed: Boolean, isSilent: Boolean, @@ -270,12 +282,14 @@ private fun ActiveNotificationModel.isCurrent( isGroupSummary: Boolean, bucket: Int, callType: CallType, - promotedContent: PromotedNotificationContentModel?, + promotedContent: PromotedNotificationContentModels?, ): Boolean { return when { key != this.key -> false groupKey != this.groupKey -> false whenTime != this.whenTime -> false + isForegroundService != this.isForegroundService -> false + isOngoingEvent != this.isOngoingEvent -> false isAmbient != this.isAmbient -> false isRowDismissed != this.isRowDismissed -> false isSilent != this.isSilent -> false diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractor.kt index 7e19ff115a92..a8a7e885d1f7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractor.kt @@ -40,6 +40,8 @@ import android.graphics.drawable.Icon import com.android.systemui.Flags import com.android.systemui.dagger.SysUISingleton import com.android.systemui.shade.ShadeDisplayAware +import com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_NONE +import com.android.systemui.statusbar.NotificationLockscreenUserManager.RedactionType import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.promoted.AutomaticPromotionCoordinator.Companion.EXTRA_AUTOMATICALLY_EXTRACTED_SHORT_CRITICAL_TEXT import com.android.systemui.statusbar.notification.promoted.AutomaticPromotionCoordinator.Companion.EXTRA_WAS_AUTOMATICALLY_PROMOTED @@ -48,6 +50,7 @@ import com.android.systemui.statusbar.notification.promoted.shared.model.Promote import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel.OldProgress import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel.Style import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel.When +import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModels import com.android.systemui.statusbar.notification.row.shared.ImageModel import com.android.systemui.statusbar.notification.row.shared.ImageModelProvider import com.android.systemui.statusbar.notification.row.shared.ImageModelProvider.ImageSizeClass.MediumSquare @@ -60,8 +63,9 @@ interface PromotedNotificationContentExtractor { fun extractContent( entry: NotificationEntry, recoveredBuilder: Notification.Builder, + @RedactionType redactionType: Int, imageModelProvider: ImageModelProvider, - ): PromotedNotificationContentModel? + ): PromotedNotificationContentModels? } @SysUISingleton @@ -76,8 +80,9 @@ constructor( override fun extractContent( entry: NotificationEntry, recoveredBuilder: Notification.Builder, + @RedactionType redactionType: Int, imageModelProvider: ImageModelProvider, - ): PromotedNotificationContentModel? { + ): PromotedNotificationContentModels? { if (!PromotedNotificationContentModel.featureFlagEnabled()) { logger.logExtractionSkipped(entry, "feature flags disabled") return null @@ -95,7 +100,55 @@ constructor( return null } - val contentBuilder = PromotedNotificationContentModel.Builder(entry.key) + val privateVersion = + extractPrivateContent( + key = entry.key, + notification = notification, + recoveredBuilder = recoveredBuilder, + lastAudiblyAlertedMs = entry.lastAudiblyAlertedMs, + imageModelProvider = imageModelProvider, + ) + val publicVersion = + if (redactionType == REDACTION_TYPE_NONE) { + privateVersion + } else { + if (notification.publicVersion == null) { + privateVersion.toDefaultPublicVersion() + } else { + // TODO(b/400991304): implement extraction for [Notification.publicVersion] + privateVersion.toDefaultPublicVersion() + } + } + return PromotedNotificationContentModels( + privateVersion = privateVersion, + publicVersion = publicVersion, + ) + .also { logger.logExtractionSucceeded(entry, it) } + } + + private fun PromotedNotificationContentModel.toDefaultPublicVersion(): + PromotedNotificationContentModel = + PromotedNotificationContentModel.Builder(key = identity.key).let { + it.style = if (style == Style.Ineligible) Style.Ineligible else Style.Base + it.smallIcon = smallIcon + it.iconLevel = iconLevel + it.appName = appName + it.time = time + it.lastAudiblyAlertedMs = lastAudiblyAlertedMs + it.profileBadgeResId = profileBadgeResId + it.colors = colors + it.build() + } + + private fun extractPrivateContent( + key: String, + notification: Notification, + recoveredBuilder: Notification.Builder, + lastAudiblyAlertedMs: Long, + imageModelProvider: ImageModelProvider, + ): PromotedNotificationContentModel { + + val contentBuilder = PromotedNotificationContentModel.Builder(key) // TODO: Pitch a fit if style is unsupported or mandatory fields are missing once // FLAG_PROMOTED_ONGOING is set reliably and we're not testing status bar chips. @@ -108,7 +161,7 @@ constructor( contentBuilder.subText = notification.subText() contentBuilder.time = notification.extractWhen() contentBuilder.shortCriticalText = notification.shortCriticalText() - contentBuilder.lastAudiblyAlertedMs = entry.lastAudiblyAlertedMs + contentBuilder.lastAudiblyAlertedMs = lastAudiblyAlertedMs contentBuilder.profileBadgeResId = null // TODO contentBuilder.title = notification.title(recoveredBuilder.style) contentBuilder.text = notification.text(recoveredBuilder.style) @@ -124,7 +177,7 @@ constructor( recoveredBuilder.extractStyleContent(notification, contentBuilder, imageModelProvider) - return contentBuilder.build().also { logger.logExtractionSucceeded(entry, it) } + return contentBuilder.build() } private fun Notification.smallIconModel(imageModelProvider: ImageModelProvider): ImageModel? = @@ -199,16 +252,18 @@ constructor( extras?.getBoolean(EXTRA_PROGRESS_INDETERMINATE) private fun Notification.extractWhen(): When? { + val whenTime = getWhen() + return when { showsChronometer() -> { When.Chronometer( elapsedRealtimeMillis = - `when` + systemClock.elapsedRealtime() - systemClock.currentTimeMillis(), + whenTime + systemClock.elapsedRealtime() - systemClock.currentTimeMillis(), isCountDown = chronometerCountDown(), ) } - showsTime() -> When.Time(currentTimeMillis = `when`) + showsTime() -> When.Time(currentTimeMillis = whenTime) else -> null } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationLogger.kt index 5f9678a06b59..6b6203d6ea4d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationLogger.kt @@ -23,7 +23,7 @@ import com.android.systemui.log.core.LogLevel.ERROR import com.android.systemui.log.core.LogLevel.INFO import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.logKey -import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel +import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModels import javax.inject.Inject @OptIn(ExperimentalStdlibApi::class) @@ -56,7 +56,7 @@ constructor(@PromotedNotificationLog private val buffer: LogBuffer) { fun logExtractionSucceeded( entry: NotificationEntry, - content: PromotedNotificationContentModel, + content: PromotedNotificationContentModels, ) { buffer.log( EXTRACTION_TAG, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/AODPromotedNotificationInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/AODPromotedNotificationInteractor.kt index ec4ee4560ea1..d9778bdde0a5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/AODPromotedNotificationInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/AODPromotedNotificationInteractor.kt @@ -22,6 +22,7 @@ import com.android.systemui.statusbar.notification.promoted.shared.model.Promote import com.android.systemui.util.kotlin.FlowDumperImpl import javax.inject.Inject import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.map @SysUISingleton @@ -34,6 +35,16 @@ constructor( /** The content to show as the promoted notification on AOD */ val content: Flow<PromotedNotificationContentModel?> = promotedNotificationsInteractor.aodPromotedNotification + .map { + // TODO(b/400991304): show the private version when unlocked + it?.publicVersion + } + .distinctUntilNewInstance() val isPresent: Flow<Boolean> = content.map { it != null }.dumpWhileCollecting("isPresent") + + /** + * Returns flow where all subsequent repetitions of the same object instance are filtered out. + */ + private fun <T> Flow<T>.distinctUntilNewInstance() = distinctUntilChanged { a, b -> a === b } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/PromotedNotificationsInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/PromotedNotificationsInteractor.kt index a99ca072b6c8..08e7528631c7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/PromotedNotificationsInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/PromotedNotificationsInteractor.kt @@ -19,17 +19,24 @@ package com.android.systemui.statusbar.notification.promoted.domain.interactor import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.statusbar.chips.call.domain.interactor.CallChipInteractor +import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractor +import com.android.systemui.statusbar.chips.mediaprojection.domain.model.ProjectionChipModel import com.android.systemui.statusbar.chips.notification.domain.interactor.StatusBarNotificationChipsInteractor +import com.android.systemui.statusbar.chips.screenrecord.domain.interactor.ScreenRecordChipInteractor +import com.android.systemui.statusbar.chips.screenrecord.domain.model.ScreenRecordChipModel import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor -import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel.Style.Ineligible +import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModels import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel import com.android.systemui.statusbar.phone.ongoingcall.shared.model.OngoingCallModel import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map @@ -38,52 +45,169 @@ import kotlinx.coroutines.flow.map * presented order of current notification status bar chips. */ @SysUISingleton +@OptIn(ExperimentalCoroutinesApi::class) class PromotedNotificationsInteractor @Inject constructor( - activeNotificationsInteractor: ActiveNotificationsInteractor, + private val activeNotificationsInteractor: ActiveNotificationsInteractor, + screenRecordChipInteractor: ScreenRecordChipInteractor, + mediaProjectionChipInteractor: MediaProjectionChipInteractor, callChipInteractor: CallChipInteractor, notifChipsInteractor: StatusBarNotificationChipsInteractor, @Background backgroundDispatcher: CoroutineDispatcher, ) { + private val screenRecordChipNotification: Flow<NotifAndPromotedContent?> = + screenRecordChipInteractor.screenRecordState.flatMapLatest { screenRecordModel -> + when (screenRecordModel) { + is ScreenRecordChipModel.DoingNothing -> flowOf(null) + is ScreenRecordChipModel.Starting -> flowOf(null) + is ScreenRecordChipModel.Recording -> + createRecordingNotificationFlow(hostPackage = screenRecordModel.hostPackage) + } + } + + private val mediaProjectionChipNotification: Flow<NotifAndPromotedContent?> = + mediaProjectionChipInteractor.projection.flatMapLatest { projectionModel -> + when (projectionModel) { + is ProjectionChipModel.NotProjecting -> flowOf(null) + is ProjectionChipModel.Projecting -> + createRecordingNotificationFlow( + hostPackage = projectionModel.projectionState.hostPackage + ) + } + } + + /** + * Creates a flow emitting the screen-recording-related notification corresponding to the given + * package name (if we can find it). + * + * @param 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). + */ + private fun createRecordingNotificationFlow( + hostPackage: String? + ): Flow<NotifAndPromotedContent?> = + if (hostPackage == null) { + flowOf(null) + } else { + activeNotificationsInteractor.allRepresentativeNotifications + .map { allNotifs -> + findBestMatchingMediaProjectionNotif(allNotifs.values, hostPackage) + } + .distinctUntilChanged() + } + + /** + * Finds the best notification that matches the given [hostPackage] that looks like a recording + * notification, or null if we couldn't find a uniquely good match. + */ + private fun findBestMatchingMediaProjectionNotif( + allNotifs: Collection<ActiveNotificationModel>, + hostPackage: String, + ): NotifAndPromotedContent? { + val candidates = allNotifs.filter { it.packageName == hostPackage } + if (candidates.isEmpty()) { + return null + } + + candidates + .findOnlyOrNull { it.isForegroundService } + ?.let { + return it.toNotifAndPromotedContent() + } + candidates + .findOnlyOrNull { it.isOngoingEvent } + ?.let { + return it.toNotifAndPromotedContent() + } + candidates + .findOnlyOrNull { it.isForegroundService && it.isOngoingEvent } + ?.let { + return it.toNotifAndPromotedContent() + } + // We weren't able to find exactly 1 match for the given [hostPackage], so just don't match + // at all. + return null + } + + /** + * Returns the single notification matching the given [predicate] if there's only 1 match, or + * null if there's 0 or 2+ matches. + */ + private fun List<ActiveNotificationModel>.findOnlyOrNull( + predicate: (ActiveNotificationModel) -> Boolean + ): ActiveNotificationModel? { + val list = this.filter(predicate) + return if (list.size == 1) { + list.first() + } else { + null + } + } + + private fun ActiveNotificationModel.toNotifAndPromotedContent(): NotifAndPromotedContent { + return NotifAndPromotedContent(this.key, this.promotedContent) + } + + private val callNotification: Flow<NotifAndPromotedContent?> = + callChipInteractor.ongoingCallState + .map { + when (it) { + is OngoingCallModel.InCall -> + NotifAndPromotedContent(it.notificationKey, it.promotedContent) + is OngoingCallModel.NoCall -> null + } + } + .distinctUntilChanged() + + private val promotedChipNotifications: Flow<List<NotifAndPromotedContent>> = + notifChipsInteractor.allNotificationChips + .map { chips -> chips.map { NotifAndPromotedContent(it.key, it.promotedContent) } } + .distinctUntilChanged() + /** * This is the ordered list of notifications (and the promoted content) represented as chips in * the status bar. */ private val orderedChipNotifications: Flow<List<NotifAndPromotedContent>> = - combine(callChipInteractor.ongoingCallState, notifChipsInteractor.allNotificationChips) { - callState, - notifChips -> - buildList { - val callData = callState.getNotifData()?.also { add(it) } - addAll( - notifChips.mapNotNull { - when (it.key) { - callData?.key -> null // do not re-add the same call - else -> NotifAndPromotedContent(it.key, it.promotedContent) - } - } - ) + combine( + screenRecordChipNotification, + mediaProjectionChipNotification, + callNotification, + promotedChipNotifications, + ) { screenRecordNotif, mediaProjectionNotif, callNotif, promotedNotifs -> + val chipNotifications = mutableListOf<NotifAndPromotedContent>() + val usedKeys = mutableListOf<String>() + + fun addToList(item: NotifAndPromotedContent?) { + if (item != null && !usedKeys.contains(item.key)) { + chipNotifications.add(item) + usedKeys.add(item.key) + } } - } - private fun OngoingCallModel.getNotifData(): NotifAndPromotedContent? = - when (this) { - is OngoingCallModel.InCall -> NotifAndPromotedContent(notificationKey, promotedContent) - is OngoingCallModel.NoCall -> null + // IMPORTANT: This ordering is prescribed by OngoingActivityChipsViewModel. Be sure to + // always keep this ordering in sync with that view model. + // TODO(b/402471288): Create a single source of truth for the ordering. + addToList(screenRecordNotif) + addToList(mediaProjectionNotif) + addToList(callNotif) + promotedNotifs.forEach { addToList(it) } + + chipNotifications } /** * The top promoted notification represented by a chip, with the order determined by the order * of the chips, not the notifications. */ - private val topPromotedChipNotification: Flow<PromotedNotificationContentModel?> = + private val topPromotedChipNotification: Flow<PromotedNotificationContentModels?> = orderedChipNotifications .map { list -> list.firstNotNullOfOrNull { it.promotedContent } } .distinctUntilNewInstance() /** This is the AOD promoted notification, which should avoid regular changing. */ - val aodPromotedNotification: Flow<PromotedNotificationContentModel?> = + val aodPromotedNotification: Flow<PromotedNotificationContentModels?> = combine( topPromotedChipNotification, activeNotificationsInteractor.topLevelRepresentativeNotifications, @@ -105,13 +229,13 @@ constructor( .flowOn(backgroundDispatcher) private fun List<ActiveNotificationModel>.firstAodEligibleOrNull(): - PromotedNotificationContentModel? { + PromotedNotificationContentModels? { return this.firstNotNullOfOrNull { it.promotedContent?.takeIfAodEligible() } } - private fun PromotedNotificationContentModel.takeIfAodEligible(): - PromotedNotificationContentModel? { - return this.takeUnless { it.style == Ineligible } + private fun PromotedNotificationContentModels.takeIfAodEligible(): + PromotedNotificationContentModels? { + return this.takeUnless { it.privateVersion.style == Ineligible } } /** @@ -127,7 +251,7 @@ constructor( */ private data class NotifAndPromotedContent( val key: String, - val promotedContent: PromotedNotificationContentModel?, + val promotedContent: PromotedNotificationContentModels?, ) { /** * Define the equals of this object to only check the reference equality of the promoted @@ -145,7 +269,7 @@ constructor( /** Define the hashCode to be very quick, even if it increases collisions. */ override fun hashCode(): Int { var result = key.hashCode() - result = 31 * result + (promotedContent?.identity?.hashCode() ?: 0) + result = 31 * result + (promotedContent?.key?.hashCode() ?: 0) return result } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/shared/model/PromotedNotificationContentModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/shared/model/PromotedNotificationContentModel.kt index 57b07204fc6a..ffacf62fccce 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/shared/model/PromotedNotificationContentModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/promoted/shared/model/PromotedNotificationContentModel.kt @@ -29,6 +29,31 @@ import com.android.systemui.statusbar.notification.row.ImageResult import com.android.systemui.statusbar.notification.row.LazyImage import com.android.systemui.statusbar.notification.row.shared.ImageModel +data class PromotedNotificationContentModels( + /** The potentially redacted version of the content that will be exposed to the public */ + val publicVersion: PromotedNotificationContentModel, + /** The unredacted version of the content that will be kept private */ + val privateVersion: PromotedNotificationContentModel, +) { + val key: String + get() = privateVersion.identity.key + + init { + check(publicVersion.identity.key == privateVersion.identity.key) { + "public and private models must have the same key" + } + } + + fun toRedactedString(): String { + val publicVersionString = + "==privateVersion".takeIf { privateVersion === publicVersion } + ?: publicVersion.toRedactedString() + return ("PromotedNotificationContentModels(" + + "privateVersion=${privateVersion.toRedactedString()}, " + + "publicVersion=$publicVersionString)") + } +} + /** * The content needed to render a promoted notification to surfaces besides the notification stack, * like the skeleton view on AOD or the status bar chip. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java index 2a3b266c8d10..bef3c691cb4d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java @@ -976,7 +976,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView return mEntry; } - @Nullable + @NonNull public EntryAdapter getEntryAdapter() { NotificationBundleUi.unsafeAssertInNewMode(); return mEntryAdapter; @@ -1005,7 +1005,9 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } else if (isAboveShelf() != wasAboveShelf) { mAboveShelfChangedListener.onAboveShelfStateChanged(!wasAboveShelf); } - updateBackgroundTint(); + if (notificationRowTransparency()) { + updateBackgroundTint(); + } } /** @@ -2684,30 +2686,58 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } /** + * Whether to allow dismissal with the whole-row translation animation. + * + * If true, either animation is permissible. + * If false, usingRTX behavior is forbidden, only clipping animation should be used. + * + * Usually either is OK, except for promoted notifications, where we always need to + * dismiss with content clipping/partial translation animation instead, so that we + * can show the demotion options. + * @return + */ + private boolean allowDismissUsingRowTranslationX() { + if (Flags.permissionHelperInlineUiRichOngoing()) { + return !isPromotedOngoing(); + } else { + // Don't change behavior unless the flag is on. + return true; + } + } + + /** * Set the dismiss behavior of the view. * * @param usingRowTranslationX {@code true} if the view should translate using regular * translationX, otherwise the contents will be * translated. + * @param forceUpdateChildren {@code true} to force initialization, {@code false} if lazy + * behavior is OK. */ @Override - public void setDismissUsingRowTranslationX(boolean usingRowTranslationX) { - if (usingRowTranslationX != mDismissUsingRowTranslationX) { + public void setDismissUsingRowTranslationX(boolean usingRowTranslationX, + boolean forceUpdateChildren) { + // Before updating dismiss behavior, make sure this is an allowable configuration for this + // notification. + usingRowTranslationX = usingRowTranslationX && allowDismissUsingRowTranslationX(); + + if (forceUpdateChildren || (usingRowTranslationX != mDismissUsingRowTranslationX)) { // In case we were already transitioning, let's switch over! float previousTranslation = getTranslation(); if (previousTranslation != 0) { setTranslation(0); } - super.setDismissUsingRowTranslationX(usingRowTranslationX); + super.setDismissUsingRowTranslationX(usingRowTranslationX, forceUpdateChildren); if (previousTranslation != 0) { setTranslation(previousTranslation); } + if (mChildrenContainer != null) { List<ExpandableNotificationRow> notificationChildren = mChildrenContainer.getAttachedChildren(); for (int i = 0; i < notificationChildren.size(); i++) { ExpandableNotificationRow child = notificationChildren.get(i); - child.setDismissUsingRowTranslationX(usingRowTranslationX); + child.setDismissUsingRowTranslationX(usingRowTranslationX, forceUpdateChildren); } } } @@ -3164,7 +3194,9 @@ public class ExpandableNotificationRow extends ActivatableNotificationView mChildrenContainer.setOnKeyguard(onKeyguard); } } - updateBackgroundTint(); + if (notificationRowTransparency()) { + updateBackgroundTint(); + } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java index 80cf818e985f..6c990df5d05e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableOutlineView.java @@ -292,7 +292,8 @@ public abstract class ExpandableOutlineView extends ExpandableView { * translationX, otherwise the contents will be * translated. */ - public void setDismissUsingRowTranslationX(boolean usingRowTranslationX) { + public void setDismissUsingRowTranslationX(boolean usingRowTranslationX, + boolean forceUpdateChildren) { mDismissUsingRowTranslationX = usingRowTranslationX; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java index d97e25fdfa22..57ceafcd15c6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationContentInflater.java @@ -60,6 +60,7 @@ import com.android.systemui.statusbar.notification.NotificationUtils; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.statusbar.notification.promoted.PromotedNotificationContentExtractor; import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel; +import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModels; import com.android.systemui.statusbar.notification.row.shared.AsyncGroupHeaderViewInflation; import com.android.systemui.statusbar.notification.row.shared.AsyncHybridViewInflation; import com.android.systemui.statusbar.notification.row.shared.ImageModelProvider; @@ -1003,7 +1004,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder row.mImageModelIndex = result.mRowImageInflater.getNewImageIndex(); if (PromotedNotificationContentModel.featureFlagEnabled()) { - entry.setPromotedNotificationContentModel(result.mPromotedContent); + entry.setPromotedNotificationContentModels(result.mPromotedContent); } boolean setRepliesAndActions = true; @@ -1387,9 +1388,9 @@ public class NotificationContentInflater implements NotificationRowContentBinder mLogger.logAsyncTaskProgress(logKey, "extracting promoted notification content"); final ImageModelProvider imageModelProvider = result.mRowImageInflater.useForContentModel(); - final PromotedNotificationContentModel promotedContent = + final PromotedNotificationContentModels promotedContent = mPromotedNotificationContentExtractor.extractContent(mEntry, - recoveredBuilder, imageModelProvider); + recoveredBuilder, mBindParams.redactionType, imageModelProvider); mLogger.logAsyncTaskProgress(logKey, "extracted promoted notification content: " + promotedContent); @@ -1503,7 +1504,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder static class InflationProgress { RowImageInflater mRowImageInflater; - PromotedNotificationContentModel mPromotedContent; + PromotedNotificationContentModels mPromotedContent; private RemoteViews newContentView; private RemoteViews newHeadsUpView; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java index cdb78d99538b..f4e01bf718d9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationGutsManager.java @@ -29,6 +29,7 @@ import android.content.pm.ShortcutManager; import android.net.Uri; import android.os.Bundle; import android.os.Handler; +import android.os.RemoteException; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; @@ -309,6 +310,7 @@ public class NotificationGutsManager implements NotifGutsViewManager, CoreStarta }); View gutsView = item.getGutsView(); + try { if (gutsView instanceof NotificationSnooze) { initializeSnoozeView(row, (NotificationSnooze) gutsView); @@ -322,6 +324,8 @@ public class NotificationGutsManager implements NotifGutsViewManager, CoreStarta (PartialConversationInfo) gutsView); } else if (gutsView instanceof FeedbackInfo) { initializeFeedbackInfo(row, (FeedbackInfo) gutsView); + } else if (gutsView instanceof PromotedPermissionGutsContent) { + initializeDemoteView(row, (PromotedPermissionGutsContent) gutsView); } return true; } catch (Exception e) { @@ -351,6 +355,31 @@ public class NotificationGutsManager implements NotifGutsViewManager, CoreStarta } /** + * Sets up the {@link NotificationSnooze} inside the notification row's guts. + * + * @param row view to set up the guts for + * @param demoteGuts view to set up/bind within {@code row} + */ + private void initializeDemoteView( + final ExpandableNotificationRow row, + PromotedPermissionGutsContent demoteGuts) { + StatusBarNotification sbn = row.getEntry().getSbn(); + demoteGuts.setStatusBarNotification(sbn); + demoteGuts.setOnDemoteAction(new View.OnClickListener() { + @Override + public void onClick(View v) { + try { + // TODO(b/391661009): Signal AutomaticPromotionCoordinator here + mNotificationManager.setCanBePromoted( + sbn.getPackageName(), sbn.getUid(), false, true); + } catch (RemoteException e) { + Log.e(TAG, "Couldn't revoke live update permission", e); + } + } + }); + } + + /** * Sets up the {@link FeedbackInfo} inside the notification row's guts. * * @param row view to set up the guts for diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java index c03dc279888f..f494a4ce40dd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationMenuRow.java @@ -272,6 +272,7 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl } else if (personNotifType >= PeopleNotificationIdentifier.TYPE_FULL_PERSON) { mInfoItem = createConversationItem(mContext); } else if (android.app.Flags.uiRichOngoing() + && android.app.Flags.apiRichOngoing() && Flags.permissionHelperUiRichOngoing() && sbn.getNotification().isPromotedOngoing()) { mInfoItem = createPromotedItem(mContext); @@ -284,6 +285,15 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl } mRightMenuItems.add(mInfoItem); mRightMenuItems.add(mFeedbackItem); + boolean isPromotedOngoing = NotificationBundleUi.isEnabled() + ? mParent.getEntryAdapter().isPromotedOngoing() + : mParent.getEntryLegacy().isPromotedOngoing(); + if (android.app.Flags.uiRichOngoing() && Flags.permissionHelperInlineUiRichOngoing() + && isPromotedOngoing) { + mRightMenuItems.add(createDemoteItem(mContext)); + } + + mLeftMenuItems.addAll(mRightMenuItems); populateMenuViews(); @@ -305,15 +315,19 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl } else { mMenuContainer = new FrameLayout(mContext); } + final int showDismissSetting = Settings.Global.getInt(mContext.getContentResolver(), Settings.Global.SHOW_NEW_NOTIF_DISMISS, /* default = */ 1); final boolean newFlowHideShelf = showDismissSetting == 1; - if (newFlowHideShelf) { - return; - } - List<MenuItem> menuItems = mOnLeft ? mLeftMenuItems : mRightMenuItems; - for (int i = 0; i < menuItems.size(); i++) { - addMenuView(menuItems.get(i), mMenuContainer); + + // Populate menu items if we are using the new permission helper (U+) or if we are using + // the very old dismiss setting (SC-). + // TODO: SHOW_NEW_NOTIF_DISMISS==0 case can likely be removed. + if (Flags.permissionHelperInlineUiRichOngoing() || !newFlowHideShelf) { + List<MenuItem> menuItems = mOnLeft ? mLeftMenuItems : mRightMenuItems; + for (int i = 0; i < menuItems.size(); i++) { + addMenuView(menuItems.get(i), mMenuContainer); + } } } @@ -679,6 +693,15 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl return snooze; } + static MenuItem createDemoteItem(Context context) { + PromotedPermissionGutsContent demoteContent = + (PromotedPermissionGutsContent) LayoutInflater.from(context).inflate( + R.layout.promoted_permission_guts, null, false); + MenuItem info = new NotificationMenuItem(context, null, demoteContent, + R.drawable.unpin_icon); + return info; + } + static NotificationMenuItem createConversationItem(Context context) { Resources res = context.getResources(); String infoDescription = res.getString(R.string.notification_menu_gear_description); @@ -686,7 +709,7 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl (NotificationConversationInfo) LayoutInflater.from(context).inflate( R.layout.notification_conversation_info, null, false); return new NotificationMenuItem(context, infoDescription, infoContent, - R.drawable.ic_settings); + NotificationMenuItem.OMIT_FROM_SWIPE_MENU); } static NotificationMenuItem createPromotedItem(Context context) { @@ -696,7 +719,7 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl (PromotedNotificationInfo) LayoutInflater.from(context).inflate( R.layout.promoted_notification_info, null, false); return new NotificationMenuItem(context, infoDescription, infoContent, - R.drawable.ic_settings); + NotificationMenuItem.OMIT_FROM_SWIPE_MENU); } static NotificationMenuItem createPartialConversationItem(Context context) { @@ -706,7 +729,7 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl (PartialConversationInfo) LayoutInflater.from(context).inflate( R.layout.partial_conversation_info, null, false); return new NotificationMenuItem(context, infoDescription, infoContent, - R.drawable.ic_settings); + NotificationMenuItem.OMIT_FROM_SWIPE_MENU); } static NotificationMenuItem createInfoItem(Context context) { @@ -718,14 +741,14 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl NotificationInfo infoContent = (NotificationInfo) LayoutInflater.from(context).inflate( layoutId, null, false); return new NotificationMenuItem(context, infoDescription, infoContent, - R.drawable.ic_settings); + NotificationMenuItem.OMIT_FROM_SWIPE_MENU); } static MenuItem createFeedbackItem(Context context) { FeedbackInfo feedbackContent = (FeedbackInfo) LayoutInflater.from(context).inflate( R.layout.feedback_info, null, false); MenuItem info = new NotificationMenuItem(context, null, feedbackContent, - -1 /*don't show in slow swipe menu */); + NotificationMenuItem.OMIT_FROM_SWIPE_MENU); return info; } @@ -762,6 +785,10 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl @Override public boolean isWithinSnapMenuThreshold() { + if (getSpaceForMenu() == 0) { + // don't snap open if there are no items + return false; + } float translation = getTranslation(); float snapBackThreshold = getSnapBackThreshold(); float targetRight = getDismissThreshold(); @@ -803,6 +830,10 @@ public class NotificationMenuRow implements NotificationMenuRowPlugin, View.OnCl } public static class NotificationMenuItem implements MenuItem { + + // Constant signaling that this MenuItem should not appear in slow swipe. + public static final int OMIT_FROM_SWIPE_MENU = -1; + View mMenuView; GutsContent mGutsContent; String mContentDescription; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt index ae52db88358a..4f1b90544403 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowContentBinderImpl.kt @@ -53,6 +53,7 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.logKey import com.android.systemui.statusbar.notification.promoted.PromotedNotificationContentExtractor import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel +import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModels import com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_CONTRACTED import com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_EXPANDED import com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_HEADSUP @@ -595,7 +596,7 @@ constructor( val rowImageInflater: RowImageInflater, val remoteViews: NewRemoteViews, val contentModel: NotificationContentModel, - val promotedContent: PromotedNotificationContentModel?, + val promotedContent: PromotedNotificationContentModels?, ) { var inflatedContentView: View? = null @@ -700,7 +701,12 @@ constructor( ) val imageModelProvider = rowImageInflater.useForContentModel() promotedNotificationContentExtractor - .extractContent(entry, builder, imageModelProvider) + .extractContent( + entry, + builder, + bindParams.redactionType, + imageModelProvider, + ) .also { logger.logAsyncTaskProgress( entry.logKey, @@ -1519,7 +1525,7 @@ constructor( entry.setContentModel(result.contentModel) if (PromotedNotificationContentModel.featureFlagEnabled()) { - entry.promotedNotificationContentModel = result.promotedContent + entry.promotedNotificationContentModels = result.promotedContent } result.inflatedSmartReplyState?.let { row.privateLayout.setInflatedSmartReplyState(it) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PromotedNotificationInfo.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PromotedNotificationInfo.java index 01ee788f7fd7..769f0b5a4fa4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PromotedNotificationInfo.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PromotedNotificationInfo.java @@ -80,6 +80,7 @@ public class PromotedNotificationInfo extends NotificationInfo { assistantFeedbackController, metricsLogger, onCloseClick); mNotificationManager = iNotificationManager; + mPackageDemotionInteractor = packageDemotionInteractor; bindDemote(entry.getSbn(), pkg); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PromotedPermissionGutsContent.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PromotedPermissionGutsContent.java new file mode 100644 index 000000000000..222a1f4d8adf --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/PromotedPermissionGutsContent.java @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.row; + +import android.content.Context; +import android.os.Bundle; +import android.service.notification.StatusBarNotification; +import android.util.AttributeSet; +import android.view.View; +import android.widget.LinearLayout; +import android.widget.TextView; + +import com.android.internal.logging.MetricsLogger; +import com.android.systemui.res.R; + +/** + * This GutsContent shows an explanatory interstitial telling the user they've just revoked this + * app's permission to post Promoted/Live notifications. + * If the guts are dismissed without further action, the revocation is committed. + * If the user hits undo, the permission is not revoked. + */ +public class PromotedPermissionGutsContent extends LinearLayout + implements NotificationGuts.GutsContent, View.OnClickListener { + + private static final String TAG = "SnoozyPromotedGuts"; + + private NotificationGuts mGutsContainer; + private StatusBarNotification mSbn; + + private TextView mUndoButton; + + private MetricsLogger mMetricsLogger = new MetricsLogger(); + private OnClickListener mDemoteAction; + + public PromotedPermissionGutsContent(Context context, AttributeSet attrs) { + super(context, attrs); + } + + + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + mUndoButton = (TextView) findViewById(R.id.undo); + mUndoButton.setOnClickListener(this); + mUndoButton.setContentDescription( + getContext().getString(R.string.snooze_undo_content_description)); + + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + dispatchConfigurationChanged(getResources().getConfiguration()); + } + + /** + * Update the content description of the snooze view based on the snooze option and whether the + * snooze options are expanded or not. + * For example, this will be something like "Collapsed\u2029Snooze for 1 hour". The paragraph + * separator is added to introduce a break in speech, to match what TalkBack does by default + * when you e.g. press on a notification. + */ + private void updateContentDescription() { + // + } + + + @Override + public boolean performAccessibilityActionInternal(int action, Bundle arguments) { + if (super.performAccessibilityActionInternal(action, arguments)) { + return true; + } + if (action == R.id.action_snooze_undo) { + undoDemote(mUndoButton); + return true; + } + return false; + } + + /** + * TODO docs + * @param sbn + */ + public void setStatusBarNotification(StatusBarNotification sbn) { + mSbn = sbn; + TextView demoteExplanation = (TextView) findViewById(R.id.demote_explain); + demoteExplanation.setText(mContext.getResources().getString(R.string.demote_explain_text, + mSbn.getPackageName())); + } + + @Override + public void onClick(View v) { + if (mGutsContainer != null) { + mGutsContainer.resetFalsingCheck(); + } + final int id = v.getId(); + if (id == R.id.undo) { + undoDemote(v); + } + + } + + private void undoDemote(View v) { + // Don't commit the demote action, instead log the undo and dismiss the view. + mGutsContainer.closeControls(v, /* save= */ false); + } + + @Override + public int getActualHeight() { + return getHeight(); + } + + @Override + public boolean willBeRemoved() { + return false; + } + + @Override + public View getContentView() { + return this; + } + + @Override + public void setGutsParent(NotificationGuts guts) { + mGutsContainer = guts; + } + + @Override + public boolean handleCloseControls(boolean save, boolean force) { + if (!save) { + // Undo changes and let the guts handle closing the view + return false; + } else { + // Commit demote action. + mDemoteAction.onClick(this); + return false; + } + } + + @Override + public boolean isLeavebehind() { + return true; + } + + @Override + public boolean shouldBeSavedOnClose() { + return true; + } + + @Override + public boolean needsFalsingProtection() { + return false; + } + + public void setOnDemoteAction(OnClickListener demoteAction) { + mDemoteAction = demoteAction; + } + +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/ActiveNotificationModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/ActiveNotificationModel.kt index 53728c7da62d..96527389b5fe 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/ActiveNotificationModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/shared/ActiveNotificationModel.kt @@ -20,6 +20,7 @@ import android.graphics.drawable.Icon import android.util.Log import com.android.systemui.statusbar.StatusBarIconView import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel +import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModels import com.android.systemui.statusbar.notification.stack.PriorityBucket /** @@ -38,6 +39,10 @@ data class ActiveNotificationModel( val groupKey: String?, /** When this notification was posted. */ val whenTime: Long, + /** True if this is a foreground service notification. */ + val isForegroundService: Boolean, + /** True if this notification is for an ongoing event. */ + val isOngoingEvent: Boolean, /** Is this entry in the ambient / minimized section (lowest priority)? */ val isAmbient: Boolean, /** @@ -84,7 +89,7 @@ data class ActiveNotificationModel( * The content needed to render this as a promoted notification on various surfaces, or null if * this notification cannot be rendered as a promoted notification. */ - val promotedContent: PromotedNotificationContentModel?, + val promotedContent: PromotedNotificationContentModels?, ) : ActiveNotificationEntryModel() { init { if (!PromotedNotificationContentModel.featureFlagEnabled()) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManagerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManagerImpl.kt index 6e8b2226b4f6..5c52500b7f70 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManagerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MagneticNotificationRowManagerImpl.kt @@ -169,8 +169,7 @@ constructor( } private fun pullDismissibleRow(translation: Float) { - val targetTranslation = swipedRowMultiplier * translation - val crossedThreshold = abs(targetTranslation) >= magneticDetachThreshold + val crossedThreshold = abs(translation) >= magneticDetachThreshold if (crossedThreshold) { snapNeighborsBack() currentMagneticListeners.swipedListener()?.let { detach(it, translation) } @@ -247,8 +246,7 @@ constructor( } private fun translateDetachedRow(translation: Float) { - val targetTranslation = swipedRowMultiplier * translation - val crossedThreshold = abs(targetTranslation) <= magneticAttachThreshold + val crossedThreshold = abs(translation) <= magneticAttachThreshold if (crossedThreshold) { translationOffset += translation updateRoundness(translation = 0f, animate = true) @@ -270,6 +268,7 @@ constructor( translationOffset = 0f if (row.isSwipedTarget()) { when (currentState) { + State.TARGETS_SET -> currentState = State.IDLE State.PULLING -> { snapNeighborsBack(velocity) currentState = State.IDLE 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 9fea75048e3e..503256accff0 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 @@ -3220,8 +3220,7 @@ public class NotificationStackScrollLayout updateAnimationState(child); updateChronometerForChild(child); if (child instanceof ExpandableNotificationRow row) { - row.setDismissUsingRowTranslationX(mDismissUsingRowTranslationX); - + row.setDismissUsingRowTranslationX(mDismissUsingRowTranslationX, /* force= */ true); } } @@ -6157,7 +6156,7 @@ public class NotificationStackScrollLayout View child = getChildAt(i); if (child instanceof ExpandableNotificationRow) { ((ExpandableNotificationRow) child).setDismissUsingRowTranslationX( - dismissUsingRowTranslationX); + dismissUsingRowTranslationX, /* force= */ false); } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java index e4e56c5de65b..8a5b22183563 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java @@ -527,7 +527,8 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp break; case MODE_SHOW_BOUNCER: Trace.beginSection("MODE_SHOW_BOUNCER"); - mKeyguardViewController.showPrimaryBouncer(true); + mKeyguardViewController.showPrimaryBouncer(true, + "BiometricUnlockController#MODE_SHOW_BOUNCER"); Trace.endSection(); break; case MODE_WAKE_AND_UNLOCK_FROM_DREAM: diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java index 9d9f01b571a7..e617254fa288 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java @@ -2407,11 +2407,12 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { } if (needsBouncer) { - Log.d(TAG, "showBouncerOrLockScreenIfKeyguard, showingBouncer"); + var reason = "CentralSurfacesImpl#showBouncerOrLockScreenIfKeyguard"; if (SceneContainerFlag.isEnabled()) { - mStatusBarKeyguardViewManager.showPrimaryBouncer(true /* scrimmed */); + mStatusBarKeyguardViewManager.showPrimaryBouncer(true /* scrimmed */, + reason); } else { - mStatusBarKeyguardViewManager.showBouncer(true /* scrimmed */); + mStatusBarKeyguardViewManager.showBouncer(true /* scrimmed */, reason); } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java index 0ba4aabcb0e2..34c193d18814 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/DemoStatusIcons.java @@ -27,13 +27,19 @@ import android.view.View; import android.view.ViewGroup; import android.widget.LinearLayout; +import androidx.collection.MutableIntObjectMap; + import com.android.internal.statusbar.StatusBarIcon; +import com.android.systemui.Flags; import com.android.systemui.demomode.DemoMode; +import com.android.systemui.kairos.ExperimentalKairosApi; +import com.android.systemui.kairos.KairosNetwork; import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver; import com.android.systemui.res.R; import com.android.systemui.statusbar.StatusBarIconView; import com.android.systemui.statusbar.StatusIconDisplayable; +import com.android.systemui.statusbar.pipeline.mobile.ui.MobileUiAdapterKairos; import com.android.systemui.statusbar.pipeline.mobile.ui.MobileViewLogger; import com.android.systemui.statusbar.pipeline.mobile.ui.view.ModernStatusBarMobileView; import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsViewModel; @@ -41,10 +47,20 @@ import com.android.systemui.statusbar.pipeline.shared.ui.view.ModernStatusBarVie import com.android.systemui.statusbar.pipeline.wifi.ui.view.ModernStatusBarWifiView; import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.LocationBasedWifiViewModel; +import dagger.Lazy; + +import kotlin.OptIn; +import kotlin.Pair; + +import kotlinx.coroutines.CoroutineScope; +import kotlinx.coroutines.Job; + import java.util.ArrayList; import java.util.List; +import java.util.concurrent.CancellationException; //TODO: This should be a controller, not its own view +@OptIn(markerClass = ExperimentalKairosApi.class) public class DemoStatusIcons extends StatusIconContainer implements DemoMode, DarkReceiver { private static final String TAG = "DemoStatusIcons"; @@ -60,15 +76,27 @@ public class DemoStatusIcons extends StatusIconContainer implements DemoMode, Da private final MobileIconsViewModel mMobileIconsViewModel; private final StatusBarLocation mLocation; + private final Lazy<MobileUiAdapterKairos> mMobileUiAdapterKairos; + private final KairosNetwork mKairosNetwork; + private final CoroutineScope mAppScope; + + private final MutableIntObjectMap<Job> mBindingJobs = new MutableIntObjectMap<>(); + public DemoStatusIcons( LinearLayout statusIcons, MobileIconsViewModel mobileIconsViewModel, StatusBarLocation location, - int iconSize + int iconSize, + Lazy<MobileUiAdapterKairos> mobileUiAdapterKairos, + KairosNetwork kairosNetwork, + CoroutineScope appScope ) { super(statusIcons.getContext()); mStatusIcons = statusIcons; mIconSize = iconSize; + mMobileUiAdapterKairos = mobileUiAdapterKairos; + mKairosNetwork = kairosNetwork; + mAppScope = appScope; mColor = DarkIconDispatcher.DEFAULT_ICON_TINT; mContrastColor = DarkIconDispatcher.DEFAULT_INVERSE_ICON_TINT; mMobileIconsViewModel = mobileIconsViewModel; @@ -236,24 +264,46 @@ public class DemoStatusIcons extends StatusIconContainer implements DemoMode, Da /** * Add a {@link ModernStatusBarMobileView} + * * @param mobileContext possibly mcc/mnc overridden mobile context - * @param subId the subscriptionId for this mobile view + * @param subId the subscriptionId for this mobile view */ public void addModernMobileView( Context mobileContext, MobileViewLogger mobileViewLogger, int subId) { Log.d(TAG, "addModernMobileView (subId=" + subId + ")"); - ModernStatusBarMobileView view = ModernStatusBarMobileView.constructAndBind( - mobileContext, - mobileViewLogger, - "mobile", - mMobileIconsViewModel.viewModelForSub(subId, mLocation) - ); - - // mobile always goes at the end - mModernMobileViews.add(view); - addView(view, getChildCount(), createLayoutParams()); + if (Flags.statusBarMobileIconKairos()) { + Pair<ModernStatusBarMobileView, Job> viewAndJob = + ModernStatusBarMobileView.constructAndBind( + mobileContext, + mobileViewLogger, + "mobile", + mMobileUiAdapterKairos.get().getMobileIconsViewModel().viewModelForSub( + subId, mLocation), + mAppScope, + subId, + mLocation, + mKairosNetwork + ); + ModernStatusBarMobileView view = viewAndJob.getFirst(); + mBindingJobs.put(subId, viewAndJob.getSecond()); + // mobile always goes at the end + mModernMobileViews.add(view); + addView(view, getChildCount(), createLayoutParams()); + } else { + ModernStatusBarMobileView view = + ModernStatusBarMobileView.constructAndBind( + mobileContext, + mobileViewLogger, + "mobile", + mMobileIconsViewModel.viewModelForSub(subId, mLocation) + ); + + // mobile always goes at the end + mModernMobileViews.add(view); + addView(view, getChildCount(), createLayoutParams()); + } } /** @@ -299,6 +349,12 @@ public class DemoStatusIcons extends StatusIconContainer implements DemoMode, Da ModernStatusBarMobileView mobileView = matchingModernMobileView( (ModernStatusBarMobileView) view); if (mobileView != null) { + if (Flags.statusBarMobileIconKairos()) { + Job job = mBindingJobs.remove(mobileView.getSubId()); + if (job != null) { + job.cancel(new CancellationException()); + } + } removeView(mobileView); mModernMobileViews.remove(mobileView); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java index f3d72027238f..d68f7df79cd5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java @@ -538,10 +538,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump private void handleBlurSupportedChanged(boolean isBlurSupported) { this.mIsBlurSupported = isBlurSupported; if (Flags.bouncerUiRevamp()) { - // TODO: animate blur fallback when the bouncer is pulled up. - for (ScrimState state : ScrimState.values()) { - state.setDefaultScrimAlpha(getDefaultScrimAlpha(true)); - } + updateDefaultScrimAlphas(); if (isBlurSupported) { ScrimState.BOUNCER_SCRIMMED.setNotifBlurRadius(mBlurConfig.getMaxBlurRadiusPx()); } else { @@ -549,17 +546,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump } } if (Flags.notificationShadeBlur()) { - float inFrontAlpha = mInFrontAlpha; - float behindAlpha = mBehindAlpha; - float notifAlpha = mNotificationsAlpha; - mState.prepare(mState); - applyState(); - startScrimAnimation(mScrimBehind, behindAlpha); - startScrimAnimation(mNotificationsScrim, notifAlpha); - startScrimAnimation(mScrimInFront, inFrontAlpha); - dispatchBackScrimState(mScrimBehind.getViewAlpha()); - } else if (Flags.bouncerUiRevamp()) { applyAndDispatchState(); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index 8c44fe56d269..512340913de2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -669,7 +669,8 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb * show if any subsequent events are to be handled. */ if (!SceneContainerFlag.isEnabled() && beginShowingBouncer(event)) { - mPrimaryBouncerInteractor.show(/* isScrimmed= */false); + mPrimaryBouncerInteractor.show(/* isScrimmed= */false, + TAG + "#onPanelExpansionChanged"); } if (!primaryBouncerIsOrWillBeShowing()) { @@ -714,7 +715,8 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb * Shows the notification keyguard or the bouncer depending on * {@link #needsFullscreenBouncer()}. */ - protected void showBouncerOrKeyguard(boolean hideBouncerWhenShowing, boolean isFalsingReset) { + protected void showBouncerOrKeyguard(boolean hideBouncerWhenShowing, boolean isFalsingReset, + String reason) { boolean showBouncer = needsFullscreenBouncer() && !mDozing; if (Flags.simPinRaceConditionOnRestart()) { showBouncer = showBouncer && !mIsSleeping; @@ -726,11 +728,11 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb mCentralSurfaces.hideKeyguard(); mSceneInteractorLazy.get().showOverlay( Overlays.Bouncer, - "StatusBarKeyguardViewManager.showBouncerOrKeyguard" + TAG + "#showBouncerOrKeyguard" ); } else { if (Flags.simPinRaceConditionOnRestart()) { - if (mPrimaryBouncerInteractor.show(/* isScrimmed= */ true)) { + if (mPrimaryBouncerInteractor.show(/* isScrimmed= */ true, reason)) { mAttemptsToShowBouncer = 0; mCentralSurfaces.hideKeyguard(); } else { @@ -744,19 +746,19 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb + mAttemptsToShowBouncer++); mExecutor.executeDelayed(() -> showBouncerOrKeyguard(hideBouncerWhenShowing, - isFalsingReset), + isFalsingReset, reason), 500); } } } else { mCentralSurfaces.hideKeyguard(); - mPrimaryBouncerInteractor.show(/* isScrimmed= */ true); + mPrimaryBouncerInteractor.show(/* isScrimmed= */ true, reason); } } } else if (!isFalsingReset) { // Falsing resets can cause this to flicker, so don't reset in this case Log.i(TAG, "Sim bouncer is already showing, issuing a refresh"); - mPrimaryBouncerInteractor.show(/* isScrimmed= */ true); + mPrimaryBouncerInteractor.show(/* isScrimmed= */ true, reason); } } else { @@ -776,7 +778,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb * false when the user will be dragging it and translation should be deferred * {@see KeyguardBouncer#show(boolean, boolean)} */ - public void showBouncer(boolean scrimmed) { + public void showBouncer(boolean scrimmed, String reason) { if (SceneContainerFlag.isEnabled()) { mDeviceEntryInteractorLazy.get().attemptDeviceEntry(); return; @@ -787,7 +789,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb mAlternateBouncerInteractor.forceShow(); updateAlternateBouncerShowing(mAlternateBouncerInteractor.isVisibleState()); } else { - showPrimaryBouncer(scrimmed); + showPrimaryBouncer(scrimmed, reason); } } @@ -810,8 +812,10 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb * * @param scrimmed true when the bouncer should show scrimmed, false when the user will be * dragging it and translation should be deferred {@see KeyguardBouncer#show(boolean, boolean)} + * @param reason string description for what is causing the bouncer to be requested */ - public void showPrimaryBouncer(boolean scrimmed) { + @Override + public void showPrimaryBouncer(boolean scrimmed, String reason) { hideAlternateBouncer( /* updateScrim= */ false, // When the scene framework is on, don't ever clear the pending dismiss action from @@ -823,7 +827,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb "primary bouncer requested" ); } else { - mPrimaryBouncerInteractor.show(scrimmed); + mPrimaryBouncerInteractor.show(scrimmed, reason); } } updateStates(); @@ -870,7 +874,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb ); } - showBouncer(true); + showBouncer(true, TAG + "#dismissWithAction"); Trace.endSection(); return; } @@ -919,10 +923,11 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb if (SceneContainerFlag.isEnabled()) { mSceneInteractorLazy.get().showOverlay( Overlays.Bouncer, - "StatusBarKeyguardViewManager.dismissWithAction" + TAG + "#dismissWithAction" ); } else { - mPrimaryBouncerInteractor.show(/* isScrimmed= */ true); + mPrimaryBouncerInteractor.show(/* isScrimmed= */ true, + TAG + "#dismissWithAction, afterKeyguardGone"); } } else { // after authentication success, run dismiss action with the option to defer @@ -932,10 +937,11 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb if (SceneContainerFlag.isEnabled()) { mSceneInteractorLazy.get().showOverlay( Overlays.Bouncer, - "StatusBarKeyguardViewManager.dismissWithAction" + TAG + "#dismissWithAction" ); } else { - mPrimaryBouncerInteractor.show(/* isScrimmed= */ true); + mPrimaryBouncerInteractor.show(/* isScrimmed= */ true, + TAG + "#dismissWithAction"); } // bouncer will handle the dismiss action, so we no longer need to track it here mAfterKeyguardGoneAction = null; @@ -992,7 +998,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb } } } else { - showBouncerOrKeyguard(hideBouncerWhenShowing, isFalsingReset); + showBouncerOrKeyguard(hideBouncerWhenShowing, isFalsingReset, "reset"); } if (!SceneContainerFlag.isEnabled() && hideBouncerWhenShowing && isBouncerShowing()) { hideAlternateBouncer(true); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java index 8389aab4aac8..85fc9d4589c0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarRemoteInputCallback.java @@ -156,7 +156,8 @@ public class StatusBarRemoteInputCallback implements Callback, Callbacks, if (!row.isPinned()) { mStatusBarStateController.setLeaveOpenOnKeyguardHide(true); } - mStatusBarKeyguardViewManager.showBouncer(true /* scrimmed */); + mStatusBarKeyguardViewManager.showBouncer(true /* scrimmed */, + "StatusBarRemoteInputCallback#onLockedRemoteInput"); mPendingRemoteInputView = clicked; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt index 61b7d80a8c85..b7eada1c6804 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/OngoingCallController.kt @@ -38,7 +38,7 @@ import com.android.systemui.statusbar.chips.ui.view.ChipChronometer import com.android.systemui.statusbar.data.repository.StatusBarModeRepositoryStore import com.android.systemui.statusbar.gesture.SwipeStatusBarAwayGestureHandler import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor -import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel +import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModels import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel import com.android.systemui.statusbar.notification.shared.CallType import com.android.systemui.statusbar.phone.ongoingcall.data.repository.OngoingCallRepository @@ -347,7 +347,7 @@ constructor( * If the call notification also meets promoted notification criteria, this field is filled * in with the content related to promotion. Otherwise null. */ - val promotedContent: PromotedNotificationContentModel?, + val promotedContent: PromotedNotificationContentModels?, /** True if the call is currently ongoing (as opposed to incoming, screening, etc.). */ val isOngoing: Boolean, /** True if the user has swiped away the status bar while in this phone call. */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallModel.kt index 322dfff8f144..9546d374b595 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallModel.kt @@ -18,7 +18,7 @@ package com.android.systemui.statusbar.phone.ongoingcall.shared.model import android.app.PendingIntent import com.android.systemui.statusbar.StatusBarIconView -import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel +import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModels /** Represents the state of any ongoing calls. */ sealed interface OngoingCallModel { @@ -47,7 +47,7 @@ sealed interface OngoingCallModel { val intent: PendingIntent?, val notificationKey: String, val appName: String, - val promotedContent: PromotedNotificationContentModel?, + val promotedContent: PromotedNotificationContentModels?, val isAppVisible: Boolean, ) : OngoingCallModel } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/DarkIconManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/DarkIconManager.java index 8d314eeb8493..6dcc0ada8c65 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/DarkIconManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/DarkIconManager.java @@ -19,6 +19,9 @@ package com.android.systemui.statusbar.phone.ui; import android.widget.LinearLayout; import com.android.internal.statusbar.StatusBarIcon; +import com.android.systemui.dagger.qualifiers.Application; +import com.android.systemui.kairos.ExperimentalKairosApi; +import com.android.systemui.kairos.KairosNetwork; import com.android.systemui.plugins.DarkIconDispatcher; import com.android.systemui.statusbar.StatusIconDisplayable; import com.android.systemui.statusbar.connectivity.ui.MobileContextProvider; @@ -26,13 +29,20 @@ import com.android.systemui.statusbar.phone.DemoStatusIcons; import com.android.systemui.statusbar.phone.StatusBarIconHolder; import com.android.systemui.statusbar.phone.StatusBarLocation; import com.android.systemui.statusbar.pipeline.mobile.ui.MobileUiAdapter; +import com.android.systemui.statusbar.pipeline.mobile.ui.MobileUiAdapterKairos; import com.android.systemui.statusbar.pipeline.wifi.ui.WifiUiAdapter; +import dagger.Lazy; import dagger.assisted.Assisted; import dagger.assisted.AssistedFactory; import dagger.assisted.AssistedInject; +import kotlin.OptIn; + +import kotlinx.coroutines.CoroutineScope; + /** Version of {@link IconManager} that observes state from the {@link DarkIconDispatcher}. */ +@OptIn(markerClass = ExperimentalKairosApi.class) public class DarkIconManager extends IconManager { private final DarkIconDispatcher mDarkIconDispatcher; private final int mIconHorizontalMargin; @@ -43,13 +53,21 @@ public class DarkIconManager extends IconManager { @Assisted StatusBarLocation location, WifiUiAdapter wifiUiAdapter, MobileUiAdapter mobileUiAdapter, + Lazy<MobileUiAdapterKairos> mobileUiAdapterKairos, MobileContextProvider mobileContextProvider, + KairosNetwork kairosNetwork, + @Application CoroutineScope appScope, @Assisted DarkIconDispatcher darkIconDispatcher) { - super(linearLayout, location, wifiUiAdapter, mobileUiAdapter, mobileContextProvider); - mIconHorizontalMargin = - mContext.getResources() - .getDimensionPixelSize( - com.android.systemui.res.R.dimen.status_bar_icon_horizontal_margin); + super(linearLayout, + location, + wifiUiAdapter, + mobileUiAdapter, + mobileUiAdapterKairos, + mobileContextProvider, + kairosNetwork, + appScope); + mIconHorizontalMargin = mContext.getResources().getDimensionPixelSize( + com.android.systemui.res.R.dimen.status_bar_icon_horizontal_margin); mDarkIconDispatcher = darkIconDispatcher; } @@ -104,7 +122,7 @@ public class DarkIconManager extends IconManager { super.exitDemoMode(); } - /** */ + /** */ @AssistedFactory public interface Factory { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/IconManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/IconManager.java index fd16c6090cb1..862931af7504 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/IconManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/IconManager.java @@ -24,12 +24,19 @@ import static com.android.systemui.statusbar.phone.StatusBarIconHolder.TYPE_WIFI import android.annotation.Nullable; import android.content.Context; import android.os.Bundle; +import android.view.View; import android.view.ViewGroup; import android.widget.LinearLayout; +import androidx.annotation.OptIn; +import androidx.collection.MutableIntObjectMap; + import com.android.internal.statusbar.StatusBarIcon; import com.android.internal.statusbar.StatusBarIcon.Shape; +import com.android.systemui.Flags; import com.android.systemui.demomode.DemoModeCommandReceiver; +import com.android.systemui.kairos.ExperimentalKairosApi; +import com.android.systemui.kairos.KairosNetwork; import com.android.systemui.modes.shared.ModesUiIcons; import com.android.systemui.statusbar.BaseStatusBarFrameLayout; import com.android.systemui.statusbar.StatusBarIconView; @@ -40,6 +47,7 @@ import com.android.systemui.statusbar.phone.StatusBarIconHolder; import com.android.systemui.statusbar.phone.StatusBarIconHolder.BindableIconHolder; import com.android.systemui.statusbar.phone.StatusBarLocation; import com.android.systemui.statusbar.pipeline.mobile.ui.MobileUiAdapter; +import com.android.systemui.statusbar.pipeline.mobile.ui.MobileUiAdapterKairos; import com.android.systemui.statusbar.pipeline.mobile.ui.binder.MobileIconsBinder; import com.android.systemui.statusbar.pipeline.mobile.ui.view.ModernStatusBarMobileView; import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsViewModel; @@ -49,20 +57,34 @@ import com.android.systemui.statusbar.pipeline.wifi.ui.view.ModernStatusBarWifiV import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.LocationBasedWifiViewModel; import com.android.systemui.util.Assert; +import dagger.Lazy; + +import kotlin.Pair; + +import kotlinx.coroutines.CoroutineScope; +import kotlinx.coroutines.Job; + import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.CancellationException; /** * Turns info from StatusBarIconController into ImageViews in a ViewGroup. */ +@OptIn(markerClass = ExperimentalKairosApi.class) public class IconManager implements DemoModeCommandReceiver { protected final ViewGroup mGroup; private final MobileContextProvider mMobileContextProvider; private final LocationBasedWifiViewModel mWifiViewModel; private final MobileIconsViewModel mMobileIconsViewModel; + private final Lazy<MobileUiAdapterKairos> mMobileUiAdapterKairos; + private final KairosNetwork mKairosNetwork; + private final CoroutineScope mAppScope; + private final MutableIntObjectMap<Job> mBindingJobs = new MutableIntObjectMap<>(); + /** * Stores the list of bindable icons that have been added, keyed on slot name. This ensures * we don't accidentally add the same bindable icon twice. @@ -87,12 +109,17 @@ public class IconManager implements DemoModeCommandReceiver { StatusBarLocation location, WifiUiAdapter wifiUiAdapter, MobileUiAdapter mobileUiAdapter, - MobileContextProvider mobileContextProvider + Lazy<MobileUiAdapterKairos> mobileUiAdapterKairos, + MobileContextProvider mobileContextProvider, + KairosNetwork kairosNetwork, + CoroutineScope appScope ) { mGroup = group; mMobileContextProvider = mobileContextProvider; mContext = group.getContext(); mLocation = location; + mKairosNetwork = kairosNetwork; + mAppScope = appScope; reloadDimens(); @@ -101,6 +128,9 @@ public class IconManager implements DemoModeCommandReceiver { mMobileIconsViewModel = mobileUiAdapter.getMobileIconsViewModel(); MobileIconsBinder.bind(mGroup, mMobileIconsViewModel); + + mMobileUiAdapterKairos = mobileUiAdapterKairos; + mWifiViewModel = wifiUiAdapter.bindGroup(mGroup, mLocation); } @@ -150,7 +180,7 @@ public class IconManager implements DemoModeCommandReceiver { case TYPE_MOBILE_NEW -> addNewMobileIcon(index, slot, holder.getTag()); case TYPE_BINDABLE -> // Safe cast, since only BindableIconHolders can set this tag on themselves - addBindableIcon((BindableIconHolder) holder, index); + addBindableIcon((BindableIconHolder) holder, index); default -> null; }; } @@ -223,13 +253,30 @@ public class IconManager implements DemoModeCommandReceiver { private ModernStatusBarMobileView onCreateModernStatusBarMobileView( String slot, int subId) { Context mobileContext = mMobileContextProvider.getMobileContextForSub(subId, mContext); - return ModernStatusBarMobileView - .constructAndBind( - mobileContext, - mMobileIconsViewModel.getLogger(), - slot, - mMobileIconsViewModel.viewModelForSub(subId, mLocation) - ); + if (Flags.statusBarMobileIconKairos()) { + Pair<ModernStatusBarMobileView, Job> viewAndJob = + ModernStatusBarMobileView.constructAndBind( + mobileContext, + mMobileUiAdapterKairos.get().getMobileIconsViewModel().getLogger(), + slot, + mMobileUiAdapterKairos.get().getMobileIconsViewModel() + .viewModelForSub(subId, mLocation), + mAppScope, + subId, + mLocation, + mKairosNetwork + ); + mBindingJobs.put(subId, viewAndJob.getSecond()); + return viewAndJob.getFirst(); + } else { + return ModernStatusBarMobileView + .constructAndBind( + mobileContext, + mMobileIconsViewModel.getLogger(), + slot, + mMobileIconsViewModel.viewModelForSub(subId, mLocation) + ); + } } protected LinearLayout.LayoutParams onCreateLayoutParams(Shape shape) { @@ -253,6 +300,15 @@ public class IconManager implements DemoModeCommandReceiver { if (mIsInDemoMode) { mDemoStatusIcons.onRemoveIcon((StatusIconDisplayable) mGroup.getChildAt(viewIndex)); } + if (Flags.statusBarMobileIconKairos()) { + View view = mGroup.getChildAt(viewIndex); + if (view instanceof ModernStatusBarMobileView) { + Job bindingJob = mBindingJobs.remove(((ModernStatusBarMobileView) view).getSubId()); + if (bindingJob != null) { + bindingJob.cancel(new CancellationException()); + } + } + } mGroup.removeViewAt(viewIndex); } @@ -326,7 +382,10 @@ public class IconManager implements DemoModeCommandReceiver { (LinearLayout) mGroup, mMobileIconsViewModel, mLocation, - mIconSize + mIconSize, + mMobileUiAdapterKairos, + mKairosNetwork, + mAppScope ); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/TintedIconManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/TintedIconManager.java index e520148e925a..da59fc073c86 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/TintedIconManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ui/TintedIconManager.java @@ -20,19 +20,30 @@ import android.view.View; import android.view.ViewGroup; import com.android.systemui.dagger.SysUISingleton; +import com.android.systemui.dagger.qualifiers.Application; +import com.android.systemui.kairos.ExperimentalKairosApi; +import com.android.systemui.kairos.KairosNetwork; import com.android.systemui.statusbar.StatusIconDisplayable; import com.android.systemui.statusbar.connectivity.ui.MobileContextProvider; import com.android.systemui.statusbar.phone.DemoStatusIcons; import com.android.systemui.statusbar.phone.StatusBarIconHolder; import com.android.systemui.statusbar.phone.StatusBarLocation; import com.android.systemui.statusbar.pipeline.mobile.ui.MobileUiAdapter; +import com.android.systemui.statusbar.pipeline.mobile.ui.MobileUiAdapterKairos; import com.android.systemui.statusbar.pipeline.wifi.ui.WifiUiAdapter; +import dagger.Lazy; + +import kotlin.OptIn; + +import kotlinx.coroutines.CoroutineScope; + import javax.inject.Inject; /** * Version of {@link IconManager} that can tint the icons to a particular color. */ +@OptIn(markerClass = ExperimentalKairosApi.class) public class TintedIconManager extends IconManager { // The main tint, used as the foreground in non layer drawables private int mColor; @@ -44,13 +55,17 @@ public class TintedIconManager extends IconManager { StatusBarLocation location, WifiUiAdapter wifiUiAdapter, MobileUiAdapter mobileUiAdapter, - MobileContextProvider mobileContextProvider + Lazy<MobileUiAdapterKairos> mobileUiAdapterKairos, + MobileContextProvider mobileContextProvider, + KairosNetwork kairosNetwork, + CoroutineScope appScope ) { super(group, location, wifiUiAdapter, mobileUiAdapter, - mobileContextProvider); + mobileUiAdapterKairos, + mobileContextProvider, kairosNetwork, appScope); } @Override @@ -99,16 +114,25 @@ public class TintedIconManager extends IconManager { private final WifiUiAdapter mWifiUiAdapter; private final MobileContextProvider mMobileContextProvider; private final MobileUiAdapter mMobileUiAdapter; + private final Lazy<MobileUiAdapterKairos> mMobileUiAdapterKairos; + private final KairosNetwork mKairosNetwork; + private final CoroutineScope mAppScope; @Inject public Factory( WifiUiAdapter wifiUiAdapter, MobileUiAdapter mobileUiAdapter, - MobileContextProvider mobileContextProvider + MobileContextProvider mobileContextProvider, + Lazy<MobileUiAdapterKairos> mobileUiAdapterKairos, + KairosNetwork kairosNetwork, + @Application CoroutineScope appScope ) { mWifiUiAdapter = wifiUiAdapter; mMobileUiAdapter = mobileUiAdapter; mMobileContextProvider = mobileContextProvider; + mMobileUiAdapterKairos = mobileUiAdapterKairos; + mKairosNetwork = kairosNetwork; + mAppScope = appScope; } /** Creates a new {@link TintedIconManager} for the given view group and location. */ @@ -118,7 +142,10 @@ public class TintedIconManager extends IconManager { location, mWifiUiAdapter, mMobileUiAdapter, - mMobileContextProvider); + mMobileUiAdapterKairos, + mMobileContextProvider, + mKairosNetwork, + mAppScope); } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt index db1977b3ff45..93489e90fb85 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt @@ -49,6 +49,7 @@ import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIc import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractorKairosImpl import com.android.systemui.statusbar.pipeline.mobile.ui.MobileUiAdapter import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsViewModel +import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsViewModelKairos import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxy import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxyImpl import com.android.systemui.statusbar.pipeline.mobile.util.SubscriptionManagerProxy @@ -94,6 +95,7 @@ import kotlinx.coroutines.flow.Flow MobileRepositorySwitcherKairos.Module::class, MobileConnectionsRepositoryKairosImpl.Module::class, MobileIconsInteractorKairosImpl.Module::class, + MobileIconsViewModelKairos.Module::class, MobileConnectionRepositoryKairosFactoryImpl.Module::class, MobileConnectionsRepositoryKairosAdapter.Module::class, MobileIconsInteractorKairosAdapter.Module::class, @@ -217,6 +219,7 @@ abstract class StatusBarPipelineModule { fun provideFirstMobileSubShowingNetworkTypeIconProvider( mobileIconsViewModel: MobileIconsViewModel ): Supplier<Flow<Boolean>> { + // TODO: kairos-ify return Supplier<Flow<Boolean>> { mobileIconsViewModel.firstMobileSubShowingNetworkTypeIcon } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorKairos.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorKairos.kt index a9399593973b..3d58f84e1f91 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorKairos.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorKairos.kt @@ -48,6 +48,8 @@ interface MobileIconInteractorKairos { /** The table log created for this connection */ val tableLogBuffer: TableLogBuffer + val subscriptionId: Int + /** The current mobile data activity */ val activity: State<DataActivityModel> @@ -146,6 +148,9 @@ class MobileIconInteractorKairosImpl( private val carrierIdOverrides: MobileIconCarrierIdOverrides = MobileIconCarrierIdOverridesImpl(), ) : MobileIconInteractorKairos, KairosBuilder by kairosBuilder() { + override val subscriptionId: Int + get() = connectionRepository.subId + override val tableLogBuffer: TableLogBuffer get() = connectionRepository.tableLogBuffer diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileUiAdapterKairos.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileUiAdapterKairos.kt new file mode 100644 index 000000000000..9881b354d8d9 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileUiAdapterKairos.kt @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.pipeline.mobile.ui + +import com.android.systemui.Dumpable +import com.android.systemui.KairosActivatable +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dump.DumpManager +import com.android.systemui.kairos.BuildScope +import com.android.systemui.kairos.ExperimentalKairosApi +import com.android.systemui.kairos.awaitClose +import com.android.systemui.kairos.combine +import com.android.systemui.kairos.launchEffect +import com.android.systemui.shade.carrier.ShadeCarrierGroupController +import com.android.systemui.statusbar.phone.ui.StatusBarIconController +import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractorKairos +import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsViewModelKairos +import java.io.PrintWriter +import javax.inject.Inject + +/** + * This class is intended to provide a context to collect on the + * [MobileIconsInteractorKairos.filteredSubscriptions] data source and supply a state flow that can + * control [StatusBarIconController] to keep the old UI in sync with the new data source. + * + * It also provides a mechanism to create a top-level view model for each IconManager to know about + * the list of available mobile lines of service for which we want to show icons. + */ +@ExperimentalKairosApi +@SysUISingleton +class MobileUiAdapterKairos +@Inject +constructor( + private val iconController: StatusBarIconController, + val mobileIconsViewModel: MobileIconsViewModelKairos, + private val logger: MobileViewLogger, + dumpManager: DumpManager, +) : KairosActivatable, Dumpable { + + init { + dumpManager.registerNormalDumpable(this) + } + + private var isCollecting: Boolean = false + private var lastValue: List<Int>? = null + + private var shadeCarrierGroupController: ShadeCarrierGroupController? = null + + override fun BuildScope.activate() { + launchEffect { + isCollecting = true + awaitClose { isCollecting = false } + } + // Start notifying the icon controller of subscriptions + combine(mobileIconsViewModel.subscriptionIds, mobileIconsViewModel.isStackable) { a, b -> + Pair(a, b) + } + .observe { (subIds, isStackable) -> + logger.logUiAdapterSubIdsSentToIconController(subIds, isStackable) + lastValue = subIds + if (isStackable) { + // Passing an empty list to remove pre-existing mobile icons. + // StackedMobileBindableIcon will show the stacked icon instead. + iconController.setNewMobileIconSubIds(emptyList()) + } else { + iconController.setNewMobileIconSubIds(subIds) + } + shadeCarrierGroupController?.updateModernMobileIcons(subIds) + } + } + + /** Set the [ShadeCarrierGroupController] to notify of subscription updates */ + fun setShadeCarrierGroupController(controller: ShadeCarrierGroupController) { + shadeCarrierGroupController = controller + } + + override fun dump(pw: PrintWriter, args: Array<out String>) { + pw.println("isCollecting=$isCollecting") + pw.println("Last values sent to icon controller: $lastValue") + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileViewLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileViewLogger.kt index 4c2849de34ee..dec26886e063 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileViewLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileViewLogger.kt @@ -20,10 +20,12 @@ import android.view.View import com.android.systemui.Dumpable import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dump.DumpManager +import com.android.systemui.kairos.ExperimentalKairosApi import com.android.systemui.log.LogBuffer import com.android.systemui.log.core.LogLevel import com.android.systemui.statusbar.pipeline.dagger.MobileViewLog import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.LocationBasedMobileViewModel +import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.LocationBasedMobileViewModelKairos import java.io.PrintWriter import javax.inject.Inject @@ -52,14 +54,17 @@ constructor(@MobileViewLog private val buffer: LogBuffer, dumpManager: DumpManag ) } - fun logNewViewBinding(view: View, viewModel: LocationBasedMobileViewModel) { + fun logNewViewBinding(view: View, viewModel: LocationBasedMobileViewModel) = + logNewViewBinding(view, viewModel, viewModel.location.name) + + fun logNewViewBinding(view: View, viewModel: Any, location: String) { buffer.log( TAG, LogLevel.INFO, { str1 = view.getIdForLogging() str2 = viewModel.getIdForLogging() - str3 = viewModel.location.name + str3 = location }, { "New view binding. viewId=$str1, viewModelId=$str2, viewModelLocation=$str3" }, ) @@ -93,6 +98,36 @@ constructor(@MobileViewLog private val buffer: LogBuffer, dumpManager: DumpManag ) } + @OptIn(ExperimentalKairosApi::class) + fun logCollectionStarted(view: View, viewModel: LocationBasedMobileViewModelKairos) { + collectionStatuses[view.getIdForLogging()] = true + buffer.log( + TAG, + LogLevel.INFO, + { + str1 = view.getIdForLogging() + str2 = viewModel.getIdForLogging() + str3 = viewModel.location.name + }, + { "Collection started. viewId=$str1, viewModelId=$str2, viewModelLocation=$str3" }, + ) + } + + @OptIn(ExperimentalKairosApi::class) + fun logCollectionStopped(view: View, viewModel: LocationBasedMobileViewModelKairos) { + collectionStatuses[view.getIdForLogging()] = false + buffer.log( + TAG, + LogLevel.INFO, + { + str1 = view.getIdForLogging() + str2 = viewModel.getIdForLogging() + str3 = viewModel.location.name + }, + { "Collection stopped. viewId=$str1, viewModelId=$str2, viewModelLocation=$str3" }, + ) + } + override fun dump(pw: PrintWriter, args: Array<out String>) { pw.println("Collection statuses per view:---") collectionStatuses.forEach { viewId, isCollecting -> diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/StackedMobileBindableIcon.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/StackedMobileBindableIcon.kt index fa9fa4c1366f..bda76b72c08a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/StackedMobileBindableIcon.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/StackedMobileBindableIcon.kt @@ -19,22 +19,28 @@ package com.android.systemui.statusbar.pipeline.mobile.ui import android.content.Context import com.android.settingslib.flags.Flags import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.kairos.ExperimentalKairosApi +import com.android.systemui.kairos.KairosNetwork import com.android.systemui.statusbar.core.StatusBarRootModernization import com.android.systemui.statusbar.pipeline.icons.shared.model.BindableIcon import com.android.systemui.statusbar.pipeline.icons.shared.model.ModernStatusBarViewCreator import com.android.systemui.statusbar.pipeline.mobile.ui.binder.StackedMobileIconBinder import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsViewModel -import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.StackedMobileIconViewModel +import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.StackedMobileIconViewModelImpl +import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.StackedMobileIconViewModelKairos import com.android.systemui.statusbar.pipeline.shared.ui.view.SingleBindableStatusBarComposeIconView import javax.inject.Inject +@OptIn(ExperimentalKairosApi::class) @SysUISingleton class StackedMobileBindableIcon @Inject constructor( context: Context, mobileIconsViewModel: MobileIconsViewModel, - viewModelFactory: StackedMobileIconViewModel.Factory, + viewModelFactory: StackedMobileIconViewModelImpl.Factory, + kairosViewModelFactory: StackedMobileIconViewModelKairos.Factory, + kairosNetwork: KairosNetwork, ) : BindableIcon { override val slot: String = context.getString(com.android.internal.R.string.status_bar_stacked_mobile) @@ -42,7 +48,13 @@ constructor( override val initializer = ModernStatusBarViewCreator { context -> SingleBindableStatusBarComposeIconView.createView(context).also { view -> view.initView(slot) { - StackedMobileIconBinder.bind(view, mobileIconsViewModel, viewModelFactory) + StackedMobileIconBinder.bind( + view, + mobileIconsViewModel, + viewModelFactory, + kairosViewModelFactory, + kairosNetwork, + ) } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt index 0eef2e1ca685..0abd6d8d66b3 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt @@ -50,7 +50,7 @@ import kotlinx.coroutines.awaitCancellation import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.distinctUntilChanged -private data class Colors(@ColorInt val tint: Int, @ColorInt val contrast: Int) +data class MobileIconColors(@ColorInt val tint: Int, @ColorInt val contrast: Int) object MobileIconBinder { /** Binds the view to the view-model, continuing to update the former based on the latter */ @@ -80,9 +80,9 @@ object MobileIconBinder { @StatusBarIconView.VisibleState val visibilityState: MutableStateFlow<Int> = MutableStateFlow(initialVisibilityState) - val iconTint: MutableStateFlow<Colors> = + val iconTint: MutableStateFlow<MobileIconColors> = MutableStateFlow( - Colors( + MobileIconColors( tint = DarkIconDispatcher.DEFAULT_ICON_TINT, contrast = DarkIconDispatcher.DEFAULT_INVERSE_ICON_TINT, ) @@ -291,7 +291,7 @@ object MobileIconBinder { } override fun onIconTintChanged(newTint: Int, contrastTint: Int) { - iconTint.value = Colors(tint = newTint, contrast = contrastTint) + iconTint.value = MobileIconColors(tint = newTint, contrast = contrastTint) } override fun onDecorTintChanged(newTint: Int) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinderKairos.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinderKairos.kt new file mode 100644 index 000000000000..1078ae343572 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinderKairos.kt @@ -0,0 +1,290 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.pipeline.mobile.ui.binder + +import android.content.res.ColorStateList +import android.graphics.Color +import android.view.View +import android.view.ViewGroup +import android.widget.FrameLayout +import android.widget.ImageView +import android.widget.Space +import androidx.core.view.isVisible +import com.android.settingslib.graph.SignalDrawable +import com.android.systemui.Flags +import com.android.systemui.common.ui.binder.IconViewBinder +import com.android.systemui.kairos.BuildScope +import com.android.systemui.kairos.BuildSpec +import com.android.systemui.kairos.ExperimentalKairosApi +import com.android.systemui.kairos.KairosNetwork +import com.android.systemui.kairos.MutableState +import com.android.systemui.kairos.effect +import com.android.systemui.lifecycle.repeatWhenAttachedToWindow +import com.android.systemui.lifecycle.repeatWhenWindowIsVisible +import com.android.systemui.plugins.DarkIconDispatcher +import com.android.systemui.res.R +import com.android.systemui.statusbar.StatusBarIconView +import com.android.systemui.statusbar.pipeline.mobile.domain.model.SignalIconModel +import com.android.systemui.statusbar.pipeline.mobile.ui.MobileViewLogger +import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.LocationBasedMobileViewModelKairos +import com.android.systemui.statusbar.pipeline.shared.ui.binder.ModernStatusBarViewBinding +import com.android.systemui.statusbar.pipeline.shared.ui.binder.ModernStatusBarViewVisibilityHelper +import com.android.systemui.statusbar.pipeline.shared.ui.binder.StatusBarViewBinderConstants +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Job +import kotlinx.coroutines.awaitCancellation +import kotlinx.coroutines.launch + +object MobileIconBinderKairos { + + @ExperimentalKairosApi + fun bind( + view: ViewGroup, + viewModel: BuildSpec<LocationBasedMobileViewModelKairos>, + @StatusBarIconView.VisibleState + initialVisibilityState: Int = StatusBarIconView.STATE_HIDDEN, + logger: MobileViewLogger, + scope: CoroutineScope, + kairosNetwork: KairosNetwork, + ): Pair<ModernStatusBarViewBinding, Job> { + val binding = ModernStatusBarViewBindingKairosImpl(kairosNetwork, initialVisibilityState) + return binding to + scope.launch { + view.repeatWhenAttachedToWindow { + kairosNetwork.activateSpec { + bind( + view = view, + viewModel = viewModel.applySpec(), + logger = logger, + binding = binding, + ) + } + } + } + } + + @ExperimentalKairosApi + private class ModernStatusBarViewBindingKairosImpl( + kairosNetwork: KairosNetwork, + initialVisibilityState: Int, + ) : ModernStatusBarViewBinding { + + @JvmField var shouldIconBeVisible: Boolean = false + @JvmField var isCollecting: Boolean = false + + // TODO(b/238425913): We should log this visibility state. + val visibility = MutableState(kairosNetwork, initialVisibilityState) + val iconTint = + MutableState( + kairosNetwork, + MobileIconColors( + tint = DarkIconDispatcher.DEFAULT_ICON_TINT, + contrast = DarkIconDispatcher.DEFAULT_INVERSE_ICON_TINT, + ), + ) + val decorTint = MutableState(kairosNetwork, Color.WHITE) + + override fun getShouldIconBeVisible(): Boolean = shouldIconBeVisible + + override fun onVisibilityStateChanged(state: Int) { + visibility.setValue(state) + } + + override fun onIconTintChanged(newTint: Int, contrastTint: Int) { + iconTint.setValue(MobileIconColors(tint = newTint, contrast = contrastTint)) + } + + override fun onDecorTintChanged(newTint: Int) { + decorTint.setValue(newTint) + } + + override fun isCollecting(): Boolean = isCollecting + } + + @ExperimentalKairosApi + private fun BuildScope.bind( + view: ViewGroup, + viewModel: LocationBasedMobileViewModelKairos, + logger: MobileViewLogger, + binding: ModernStatusBarViewBindingKairosImpl, + ) { + viewModel.isVisible.observe { binding.shouldIconBeVisible = it } + + val mobileGroupView = view.requireViewById<ViewGroup>(R.id.mobile_group) + val activityContainer = view.requireViewById<View>(R.id.inout_container) + val activityIn = view.requireViewById<ImageView>(R.id.mobile_in) + val activityOut = view.requireViewById<ImageView>(R.id.mobile_out) + val networkTypeView = view.requireViewById<ImageView>(R.id.mobile_type) + val networkTypeContainer = view.requireViewById<FrameLayout>(R.id.mobile_type_container) + val iconView = view.requireViewById<ImageView>(R.id.mobile_signal) + val mobileDrawable = SignalDrawable(view.context) + val roamingView = view.requireViewById<ImageView>(R.id.mobile_roaming) + val roamingSpace = view.requireViewById<Space>(R.id.mobile_roaming_space) + val dotView = view.requireViewById<StatusBarIconView>(R.id.status_bar_dot) + + effect { + view.isVisible = viewModel.isVisible.sample() + iconView.isVisible = true + launch { + view.repeatWhenAttachedToWindow { + // isVisible controls the visibility state of the outer group, and thus it needs + // to run in the CREATED lifecycle so it can continue to watch while invisible + // See (b/291031862) for details + kairosNetwork.activateSpec { + viewModel.isVisible.observe { isVisible -> + viewModel.verboseLogger?.logBinderReceivedVisibility( + view, + viewModel.subscriptionId, + isVisible, + ) + view.isVisible = isVisible + // [StatusIconContainer] can get out of sync sometimes. Make sure to + // request another layout when this changes. + view.requestLayout() + } + } + } + } + launch { + view.repeatWhenWindowIsVisible { + logger.logCollectionStarted(view, viewModel) + binding.isCollecting = true + kairosNetwork.activateSpec { + binding.visibility.observe { state -> + ModernStatusBarViewVisibilityHelper.setVisibilityState( + state, + mobileGroupView, + dotView, + ) + view.requestLayout() + } + + // Set the icon for the triangle + viewModel.icon.observe { icon -> + viewModel.verboseLogger?.logBinderReceivedSignalIcon( + view, + viewModel.subscriptionId, + icon, + ) + if (icon is SignalIconModel.Cellular) { + iconView.setImageDrawable(mobileDrawable) + mobileDrawable.level = icon.toSignalDrawableState() + } else if (icon is SignalIconModel.Satellite) { + IconViewBinder.bind(icon.icon, iconView) + } + } + + viewModel.contentDescription.observe { + MobileContentDescriptionViewBinder.bind(it, view) + } + + // Set the network type icon + viewModel.networkTypeIcon.observe { dataTypeId -> + viewModel.verboseLogger?.logBinderReceivedNetworkTypeIcon( + view, + viewModel.subscriptionId, + dataTypeId, + ) + dataTypeId?.let { IconViewBinder.bind(dataTypeId, networkTypeView) } + val prevVis = networkTypeContainer.visibility + networkTypeContainer.visibility = + if (dataTypeId != null) View.VISIBLE else View.GONE + + if (prevVis != networkTypeContainer.visibility) { + view.requestLayout() + } + } + + // Set the network type background + viewModel.networkTypeBackground.observe { background -> + networkTypeContainer.setBackgroundResource(background?.res ?: 0) + + // Tint will invert when this bit changes + if (background?.res != null) { + networkTypeContainer.backgroundTintList = + ColorStateList.valueOf(binding.iconTint.sample().tint) + networkTypeView.imageTintList = + ColorStateList.valueOf(binding.iconTint.sample().contrast) + } else { + networkTypeView.imageTintList = + ColorStateList.valueOf(binding.iconTint.sample().tint) + } + } + + // Set the roaming indicator + viewModel.roaming.observe { isRoaming -> + roamingView.isVisible = isRoaming + roamingSpace.isVisible = isRoaming + } + + if (Flags.statusBarStaticInoutIndicators()) { + // Set the opacity of the activity indicators + viewModel.activityInVisible.observe { visible -> + activityIn.imageAlpha = + (if (visible) StatusBarViewBinderConstants.ALPHA_ACTIVE + else StatusBarViewBinderConstants.ALPHA_INACTIVE) + } + viewModel.activityOutVisible.observe { visible -> + activityOut.imageAlpha = + (if (visible) StatusBarViewBinderConstants.ALPHA_ACTIVE + else StatusBarViewBinderConstants.ALPHA_INACTIVE) + } + } else { + // Set the activity indicators + viewModel.activityInVisible.observe { activityIn.isVisible = it } + viewModel.activityOutVisible.observe { activityOut.isVisible = it } + } + + viewModel.activityContainerVisible.observe { + activityContainer.isVisible = it + } + + // Set the tint + binding.iconTint.observe { colors -> + val tint = ColorStateList.valueOf(colors.tint) + val contrast = ColorStateList.valueOf(colors.contrast) + + iconView.imageTintList = tint + + // If the bg is visible, tint it and use the contrast for the fg + if (viewModel.networkTypeBackground.sample() != null) { + networkTypeContainer.backgroundTintList = tint + networkTypeView.imageTintList = contrast + } else { + networkTypeView.imageTintList = tint + } + + roamingView.imageTintList = tint + activityIn.imageTintList = tint + activityOut.imageTintList = tint + dotView.setDecorColor(colors.tint) + } + + binding.decorTint.observe { tint -> dotView.setDecorColor(tint) } + } + + try { + awaitCancellation() + } finally { + binding.isCollecting = false + logger.logCollectionStopped(view, viewModel) + } + } + } + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/ShadeCarrierBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/ShadeCarrierBinder.kt index 5c80fda72373..5238f3ec800a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/ShadeCarrierBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/ShadeCarrierBinder.kt @@ -19,10 +19,10 @@ package com.android.systemui.statusbar.pipeline.mobile.ui.binder import androidx.core.view.isVisible import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle +import com.android.app.tracing.coroutines.launchTraced as launch import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.ShadeCarrierGroupMobileIconViewModel import com.android.systemui.util.AutoMarqueeTextView -import com.android.app.tracing.coroutines.launchTraced as launch object ShadeCarrierBinder { /** Binds the view to the view-model, continuing to update the former based on the latter */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/ShadeCarrierBinderKairos.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/ShadeCarrierBinderKairos.kt new file mode 100644 index 000000000000..a782116e669c --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/ShadeCarrierBinderKairos.kt @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.pipeline.mobile.ui.binder + +import androidx.core.view.isVisible +import com.android.systemui.kairos.BuildSpec +import com.android.systemui.kairos.ExperimentalKairosApi +import com.android.systemui.kairos.KairosNetwork +import com.android.systemui.lifecycle.repeatWhenWindowIsVisible +import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.ShadeCarrierGroupMobileIconViewModelKairos +import com.android.systemui.util.AutoMarqueeTextView + +object ShadeCarrierBinderKairos { + /** Binds the view to the view-model, continuing to update the former based on the latter */ + @ExperimentalKairosApi + suspend fun bind( + carrierTextView: AutoMarqueeTextView, + viewModel: BuildSpec<ShadeCarrierGroupMobileIconViewModelKairos>, + kairosNetwork: KairosNetwork, + ) { + carrierTextView.isVisible = true + carrierTextView.repeatWhenWindowIsVisible { + kairosNetwork.activateSpec { + viewModel.applySpec().carrierName.observe { carrierTextView.text = it } + } + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/StackedMobileIconBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/StackedMobileIconBinder.kt index c9fc53ecadc0..54cd8e3c46e4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/StackedMobileIconBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/StackedMobileIconBinder.kt @@ -22,19 +22,28 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.ViewCompositionStrategy import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle +import com.android.systemui.Flags +import com.android.systemui.kairos.ExperimentalKairosApi +import com.android.systemui.kairos.KairosNetwork import com.android.systemui.lifecycle.rememberViewModel import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsViewModel import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.StackedMobileIconViewModel +import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.StackedMobileIconViewModelImpl +import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.StackedMobileIconViewModelKairos import com.android.systemui.statusbar.pipeline.shared.ui.binder.ModernStatusBarViewBinding import com.android.systemui.statusbar.pipeline.shared.ui.composable.StackedMobileIcon import com.android.systemui.statusbar.pipeline.shared.ui.view.SingleBindableStatusBarComposeIconView +import com.android.systemui.util.composable.kairos.rememberKairosActivatable object StackedMobileIconBinder { + @OptIn(ExperimentalKairosApi::class) fun bind( view: SingleBindableStatusBarComposeIconView, mobileIconsViewModel: MobileIconsViewModel, - viewModelFactory: StackedMobileIconViewModel.Factory, + viewModelFactory: StackedMobileIconViewModelImpl.Factory, + kairosViewModelFactory: StackedMobileIconViewModelKairos.Factory, + kairosNetwork: KairosNetwork, ): ModernStatusBarViewBinding { return SingleBindableStatusBarComposeIconView.withDefaultBinding( view = view, @@ -47,9 +56,15 @@ object StackedMobileIconBinder { ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed ) setContent { - val viewModel = - rememberViewModel("StackedMobileIconBinder") { - viewModelFactory.create() + val viewModel: StackedMobileIconViewModel = + if (Flags.statusBarMobileIconKairos()) { + rememberKairosActivatable(kairosNetwork) { + kairosViewModelFactory.create() + } + } else { + rememberViewModel("StackedMobileIconBinder") { + viewModelFactory.create() + } } if (viewModel.isIconVisible) { CompositionLocalProvider(LocalContentColor provides Color(tint())) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernShadeCarrierGroupMobileView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernShadeCarrierGroupMobileView.kt index fbd074d5b003..f1c5fee808cf 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernShadeCarrierGroupMobileView.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernShadeCarrierGroupMobileView.kt @@ -20,22 +20,30 @@ import android.content.Context import android.util.AttributeSet import android.view.LayoutInflater import android.widget.LinearLayout +import com.android.systemui.kairos.BuildSpec +import com.android.systemui.kairos.ExperimentalKairosApi +import com.android.systemui.kairos.KairosNetwork import com.android.systemui.res.R import com.android.systemui.statusbar.StatusBarIconView.STATE_ICON +import com.android.systemui.statusbar.phone.StatusBarLocation import com.android.systemui.statusbar.pipeline.mobile.ui.MobileViewLogger import com.android.systemui.statusbar.pipeline.mobile.ui.binder.MobileIconBinder +import com.android.systemui.statusbar.pipeline.mobile.ui.binder.MobileIconBinderKairos import com.android.systemui.statusbar.pipeline.mobile.ui.binder.ShadeCarrierBinder +import com.android.systemui.statusbar.pipeline.mobile.ui.binder.ShadeCarrierBinderKairos import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.ShadeCarrierGroupMobileIconViewModel +import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.ShadeCarrierGroupMobileIconViewModelKairos import com.android.systemui.util.AutoMarqueeTextView +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Job +import kotlinx.coroutines.launch /** * ViewGroup containing a mobile carrier name and icon in the Shade Header. Can be multiple * instances as children under [ShadeCarrierGroup] */ -class ModernShadeCarrierGroupMobileView( - context: Context, - attrs: AttributeSet?, -) : LinearLayout(context, attrs) { +class ModernShadeCarrierGroupMobileView(context: Context, attrs: AttributeSet?) : + LinearLayout(context, attrs) { var subId: Int = -1 @@ -73,5 +81,49 @@ class ModernShadeCarrierGroupMobileView( ShadeCarrierBinder.bind(textView, viewModel) } } + + /** + * Inflates a new instance of [ModernShadeCarrierGroupMobileView], binds it to [viewModel], + * and returns it. + */ + @ExperimentalKairosApi + @JvmStatic + fun constructAndBind( + context: Context, + logger: MobileViewLogger, + slot: String, + viewModel: BuildSpec<ShadeCarrierGroupMobileIconViewModelKairos>, + scope: CoroutineScope, + subscriptionId: Int, + location: StatusBarLocation, + kairosNetwork: KairosNetwork, + ): Pair<ModernShadeCarrierGroupMobileView, Job> { + val view = + (LayoutInflater.from(context).inflate(R.layout.shade_carrier_new, null) + as ModernShadeCarrierGroupMobileView) + .apply { subId = subscriptionId } + return view to + scope.launch { + val iconView = + view.requireViewById<ModernStatusBarMobileView>(R.id.mobile_combo) + iconView.initView(slot) { + val (binding, _) = + MobileIconBinderKairos.bind( + view = iconView, + viewModel = viewModel, + initialVisibilityState = STATE_ICON, + logger = logger, + scope = this, + kairosNetwork = kairosNetwork, + ) + binding + } + logger.logNewViewBinding(view, viewModel, location.name) + + val textView = + view.requireViewById<AutoMarqueeTextView>(R.id.mobile_carrier_text) + launch { ShadeCarrierBinderKairos.bind(textView, viewModel, kairosNetwork) } + } + } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernStatusBarMobileView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernStatusBarMobileView.kt index 7eda87f8418d..382af7e135ef 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernStatusBarMobileView.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/view/ModernStatusBarMobileView.kt @@ -21,13 +21,22 @@ import android.util.AttributeSet import android.view.LayoutInflater import android.widget.FrameLayout import android.widget.ImageView +import com.android.settingslib.flags.Flags.newStatusBarIcons +import com.android.systemui.kairos.BuildSpec +import com.android.systemui.kairos.ExperimentalKairosApi +import com.android.systemui.kairos.KairosNetwork import com.android.systemui.res.R import com.android.systemui.statusbar.StatusBarIconView.getVisibleStateString import com.android.systemui.statusbar.core.NewStatusBarIcons +import com.android.systemui.statusbar.phone.StatusBarLocation import com.android.systemui.statusbar.pipeline.mobile.ui.MobileViewLogger import com.android.systemui.statusbar.pipeline.mobile.ui.binder.MobileIconBinder +import com.android.systemui.statusbar.pipeline.mobile.ui.binder.MobileIconBinderKairos import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.LocationBasedMobileViewModel +import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.LocationBasedMobileViewModelKairos import com.android.systemui.statusbar.pipeline.shared.ui.view.ModernStatusBarView +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Job class ModernStatusBarMobileView(context: Context, attrs: AttributeSet?) : ModernStatusBarView(context, attrs) { @@ -98,5 +107,58 @@ class ModernStatusBarMobileView(context: Context, attrs: AttributeSet?) : logger.logNewViewBinding(it, viewModel) } } + + /** + * Inflates a new instance of [ModernStatusBarMobileView], binds it to [viewModel], and + * returns it. + */ + @ExperimentalKairosApi + @JvmStatic + fun constructAndBind( + context: Context, + logger: MobileViewLogger, + slot: String, + viewModel: BuildSpec<LocationBasedMobileViewModelKairos>, + scope: CoroutineScope, + subscriptionId: Int, + location: StatusBarLocation, + kairosNetwork: KairosNetwork, + ): Pair<ModernStatusBarMobileView, Job> { + val view = + (LayoutInflater.from(context) + .inflate(R.layout.status_bar_mobile_signal_group_new, null) + as ModernStatusBarMobileView) + .apply { + // Flag-specific configuration + if (newStatusBarIcons()) { + // New icon (with no embedded whitespace) is slightly shorter + // (but actually taller) + val iconView = requireViewById<ImageView>(R.id.mobile_signal) + val lp = iconView.layoutParams + lp.height = + context.resources.getDimensionPixelSize( + R.dimen.status_bar_mobile_signal_size_updated + ) + } + + subId = subscriptionId + } + + lateinit var jobResult: Job + view.initView(slot) { + val (binding, job) = + MobileIconBinderKairos.bind( + view = view, + viewModel = viewModel, + logger = logger, + scope = scope, + kairosNetwork = kairosNetwork, + ) + jobResult = job + binding + } + logger.logNewViewBinding(view, viewModel, location.name) + return view to jobResult + } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileViewModelKairos.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileViewModelKairos.kt index fce8c85338f3..d2e3a1489040 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileViewModelKairos.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/LocationBasedMobileViewModelKairos.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 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. @@ -17,14 +17,12 @@ package com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel import android.graphics.Color +import com.android.systemui.kairos.ExperimentalKairosApi +import com.android.systemui.kairos.State +import com.android.systemui.kairos.combine import com.android.systemui.statusbar.phone.StatusBarLocation -import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconInteractor +import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconInteractorKairos import com.android.systemui.statusbar.pipeline.mobile.ui.VerboseMobileViewLogger -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.stateIn /** * A view model for an individual mobile icon that embeds the notion of a [StatusBarLocation]. This @@ -35,45 +33,47 @@ import kotlinx.coroutines.flow.stateIn * @property location the [StatusBarLocation] of this VM. * @property verboseLogger an optional logger to log extremely verbose view updates. */ +@ExperimentalKairosApi abstract class LocationBasedMobileViewModelKairos( - val commonImpl: MobileIconViewModelCommonKairos, + val commonImpl: MobileIconViewModelKairosCommon, val location: StatusBarLocation, val verboseLogger: VerboseMobileViewLogger?, -) : MobileIconViewModelCommonKairos by commonImpl { +) : MobileIconViewModelKairosCommon by commonImpl { val defaultColor: Int = Color.WHITE companion object { fun viewModelForLocation( - commonImpl: MobileIconViewModelCommon, - interactor: MobileIconInteractor, + commonImpl: MobileIconViewModelKairosCommon, + interactor: MobileIconInteractorKairos, verboseMobileViewLogger: VerboseMobileViewLogger, location: StatusBarLocation, - scope: CoroutineScope, - ): LocationBasedMobileViewModel = + ): LocationBasedMobileViewModelKairos = when (location) { StatusBarLocation.HOME -> - HomeMobileIconViewModel(commonImpl, verboseMobileViewLogger) - StatusBarLocation.KEYGUARD -> KeyguardMobileIconViewModel(commonImpl) - StatusBarLocation.QS -> QsMobileIconViewModel(commonImpl) + HomeMobileIconViewModelKairos(commonImpl, verboseMobileViewLogger) + StatusBarLocation.KEYGUARD -> KeyguardMobileIconViewModelKairos(commonImpl) + StatusBarLocation.QS -> QsMobileIconViewModelKairos(commonImpl) StatusBarLocation.SHADE_CARRIER_GROUP -> - ShadeCarrierGroupMobileIconViewModel(commonImpl, interactor, scope) + ShadeCarrierGroupMobileIconViewModelKairos(commonImpl, interactor) } } } +@ExperimentalKairosApi class HomeMobileIconViewModelKairos( - commonImpl: MobileIconViewModelCommonKairos, + commonImpl: MobileIconViewModelKairosCommon, verboseMobileViewLogger: VerboseMobileViewLogger, ) : - MobileIconViewModelCommonKairos, + MobileIconViewModelKairosCommon, LocationBasedMobileViewModelKairos( commonImpl, location = StatusBarLocation.HOME, verboseMobileViewLogger, ) -class QsMobileIconViewModelKairos(commonImpl: MobileIconViewModelCommonKairos) : - MobileIconViewModelCommonKairos, +@ExperimentalKairosApi +class QsMobileIconViewModelKairos(commonImpl: MobileIconViewModelKairosCommon) : + MobileIconViewModelKairosCommon, LocationBasedMobileViewModelKairos( commonImpl, location = StatusBarLocation.QS, @@ -81,30 +81,34 @@ class QsMobileIconViewModelKairos(commonImpl: MobileIconViewModelCommonKairos) : verboseLogger = null, ) +@ExperimentalKairosApi class ShadeCarrierGroupMobileIconViewModelKairos( - commonImpl: MobileIconViewModelCommonKairos, - interactor: MobileIconInteractor, - scope: CoroutineScope, + commonImpl: MobileIconViewModelKairosCommon, + private val interactor: MobileIconInteractorKairos, ) : - MobileIconViewModelCommonKairos, + MobileIconViewModelKairosCommon, LocationBasedMobileViewModelKairos( commonImpl, location = StatusBarLocation.SHADE_CARRIER_GROUP, // Only do verbose logging for the Home location. verboseLogger = null, ) { - private val isSingleCarrier = interactor.isSingleCarrier - val carrierName = interactor.carrierName - override val isVisible: StateFlow<Boolean> = + private val isSingleCarrier: State<Boolean> + get() = interactor.isSingleCarrier + + val carrierName: State<String> + get() = interactor.carrierName + + override val isVisible: State<Boolean> = combine(super.isVisible, isSingleCarrier) { isVisible, isSingleCarrier -> - if (isSingleCarrier) false else isVisible - } - .stateIn(scope, SharingStarted.WhileSubscribed(), super.isVisible.value) + !isSingleCarrier && isVisible + } } -class KeyguardMobileIconViewModelKairos(commonImpl: MobileIconViewModelCommonKairos) : - MobileIconViewModelCommonKairos, +@ExperimentalKairosApi +class KeyguardMobileIconViewModelKairos(commonImpl: MobileIconViewModelKairosCommon) : + MobileIconViewModelKairosCommon, LocationBasedMobileViewModelKairos( commonImpl, location = StatusBarLocation.KEYGUARD, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelKairos.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelKairos.kt index cc7fc0964dae..bd42d5bf401f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelKairos.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModelKairos.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 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. @@ -17,206 +17,187 @@ package com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel import com.android.systemui.Flags.statusBarStaticInoutIndicators +import com.android.systemui.KairosBuilder +import com.android.systemui.activated import com.android.systemui.common.shared.model.ContentDescription import com.android.systemui.common.shared.model.Icon +import com.android.systemui.flags.FeatureFlagsClassic +import com.android.systemui.kairos.ExperimentalKairosApi +import com.android.systemui.kairos.State as KairosState +import com.android.systemui.kairos.State +import com.android.systemui.kairos.combine +import com.android.systemui.kairos.flatMap +import com.android.systemui.kairos.map +import com.android.systemui.kairos.stateOf +import com.android.systemui.kairosBuilder import com.android.systemui.log.table.logDiffsForTable import com.android.systemui.res.R import com.android.systemui.statusbar.core.NewStatusBarIcons import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor -import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconInteractor -import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractor +import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconInteractorKairos import com.android.systemui.statusbar.pipeline.mobile.domain.model.SignalIconModel import com.android.systemui.statusbar.pipeline.mobile.ui.model.MobileContentDescription import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.distinctUntilChanged -import kotlinx.coroutines.flow.flatMapLatest -import kotlinx.coroutines.flow.flowOf -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.mapLatest -import kotlinx.coroutines.flow.stateIn /** Common interface for all of the location-based mobile icon view models. */ -interface MobileIconViewModelCommonKairos : MobileIconViewModelCommon { - override val subscriptionId: Int +@ExperimentalKairosApi +interface MobileIconViewModelKairosCommon { + val subscriptionId: Int + val iconInteractor: MobileIconInteractorKairos /** True if this view should be visible at all. */ - override val isVisible: StateFlow<Boolean> - override val icon: Flow<SignalIconModel> - override val contentDescription: Flow<MobileContentDescription?> - override val roaming: Flow<Boolean> + val isVisible: KairosState<Boolean> + val icon: KairosState<SignalIconModel> + val contentDescription: KairosState<MobileContentDescription?> + val roaming: KairosState<Boolean> /** The RAT icon (LTE, 3G, 5G, etc) to be displayed. Null if we shouldn't show anything */ - override val networkTypeIcon: Flow<Icon.Resource?> + val networkTypeIcon: KairosState<Icon.Resource?> /** The slice attribution. Drawn as a background layer */ - override val networkTypeBackground: StateFlow<Icon.Resource?> - override val activityInVisible: Flow<Boolean> - override val activityOutVisible: Flow<Boolean> - override val activityContainerVisible: Flow<Boolean> + val networkTypeBackground: KairosState<Icon.Resource?> + val activityInVisible: KairosState<Boolean> + val activityOutVisible: KairosState<Boolean> + val activityContainerVisible: KairosState<Boolean> } /** - * View model for the state of a single mobile icon. Each [MobileIconViewModel] will keep watch over - * a single line of service via [MobileIconInteractor] and update the UI based on that - * subscription's information. + * View model for the state of a single mobile icon. Each [MobileIconViewModelKairos] will keep + * watch over a single line of service via [MobileIconInteractorKairos] and update the UI based on + * that subscription's information. * - * There will be exactly one [MobileIconViewModel] per filtered subscription offered from - * [MobileIconsInteractor.filteredSubscriptions]. - * - * For the sake of keeping log spam in check, every flow funding the [MobileIconViewModelCommon] - * interface is implemented as a [StateFlow]. This ensures that each location-based mobile icon view - * model gets the exact same information, as well as allows us to log that unified state only once - * per icon. + * There will be exactly one [MobileIconViewModelKairos] per filtered subscription offered from + * [MobileIconsInteractorKairos.filteredSubscriptions]. */ +@ExperimentalKairosApi class MobileIconViewModelKairos( override val subscriptionId: Int, - iconInteractor: MobileIconInteractor, - airplaneModeInteractor: AirplaneModeInteractor, - constants: ConnectivityConstants, - scope: CoroutineScope, -) : MobileIconViewModelCommonKairos { - private val cellProvider by lazy { - CellularIconViewModelKairos( - subscriptionId, - iconInteractor, - airplaneModeInteractor, - constants, - scope, - ) + override val iconInteractor: MobileIconInteractorKairos, + private val airplaneModeInteractor: AirplaneModeInteractor, + private val constants: ConnectivityConstants, + private val flags: FeatureFlagsClassic, +) : MobileIconViewModelKairosCommon, KairosBuilder by kairosBuilder() { + + private val isAirplaneMode: State<Boolean> = buildState { + airplaneModeInteractor.isAirplaneMode.toState() } private val satelliteProvider by lazy { - CarrierBasedSatelliteViewModelKairosImpl( - subscriptionId, - airplaneModeInteractor, - iconInteractor, - scope, - ) + CarrierBasedSatelliteViewModelKairosImpl(subscriptionId, iconInteractor, isAirplaneMode) } /** * Similar to repository switching, this allows us to split up the logic of satellite/cellular * states, since they are different by nature */ - private val vmProvider: Flow<MobileIconViewModelCommon> = - iconInteractor.isNonTerrestrial - .mapLatest { nonTerrestrial -> - if (nonTerrestrial) { - satelliteProvider - } else { - cellProvider + private val vmProvider: KairosState<MobileIconViewModelKairosCommon> = buildState { + iconInteractor.isNonTerrestrial.mapLatestBuild { nonTerrestrial -> + if (nonTerrestrial) { + satelliteProvider + } else { + activated { + CellularIconViewModelKairos( + subscriptionId, + iconInteractor, + airplaneModeInteractor, + constants, + flags, + ) } } - .stateIn(scope, SharingStarted.WhileSubscribed(), cellProvider) + } + } - override val isVisible: StateFlow<Boolean> = - vmProvider - .flatMapLatest { it.isVisible } - .stateIn(scope, SharingStarted.WhileSubscribed(), false) + override val isVisible: KairosState<Boolean> = vmProvider.flatMap { it.isVisible } - override val icon: Flow<SignalIconModel> = vmProvider.flatMapLatest { it.icon } + override val icon: KairosState<SignalIconModel> = vmProvider.flatMap { it.icon } - override val contentDescription: Flow<MobileContentDescription?> = - vmProvider.flatMapLatest { it.contentDescription } + override val contentDescription: KairosState<MobileContentDescription?> = + vmProvider.flatMap { it.contentDescription } - override val roaming: Flow<Boolean> = vmProvider.flatMapLatest { it.roaming } + override val roaming: KairosState<Boolean> = vmProvider.flatMap { it.roaming } - override val networkTypeIcon: Flow<Icon.Resource?> = - vmProvider.flatMapLatest { it.networkTypeIcon } + override val networkTypeIcon: KairosState<Icon.Resource?> = + vmProvider.flatMap { it.networkTypeIcon } - override val networkTypeBackground: StateFlow<Icon.Resource?> = - vmProvider - .flatMapLatest { it.networkTypeBackground } - .stateIn(scope, SharingStarted.WhileSubscribed(), null) + override val networkTypeBackground: KairosState<Icon.Resource?> = + vmProvider.flatMap { it.networkTypeBackground } - override val activityInVisible: Flow<Boolean> = - vmProvider.flatMapLatest { it.activityInVisible } + override val activityInVisible: KairosState<Boolean> = + vmProvider.flatMap { it.activityInVisible } - override val activityOutVisible: Flow<Boolean> = - vmProvider.flatMapLatest { it.activityOutVisible } + override val activityOutVisible: KairosState<Boolean> = + vmProvider.flatMap { it.activityOutVisible } - override val activityContainerVisible: Flow<Boolean> = - vmProvider.flatMapLatest { it.activityContainerVisible } + override val activityContainerVisible: KairosState<Boolean> = + vmProvider.flatMap { it.activityContainerVisible } } /** Representation of this network when it is non-terrestrial (e.g., satellite) */ +@ExperimentalKairosApi private class CarrierBasedSatelliteViewModelKairosImpl( override val subscriptionId: Int, - airplaneModeInteractor: AirplaneModeInteractor, - interactor: MobileIconInteractor, - scope: CoroutineScope, -) : MobileIconViewModelCommon, MobileIconViewModelCommonKairos { - override val isVisible: StateFlow<Boolean> = - airplaneModeInteractor.isAirplaneMode - .map { !it } - .stateIn(scope, SharingStarted.WhileSubscribed(), false) + override val iconInteractor: MobileIconInteractorKairos, + isAirplaneMode: KairosState<Boolean>, +) : MobileIconViewModelKairosCommon { + override val isVisible: KairosState<Boolean> = isAirplaneMode.map { !it } + override val icon: KairosState<SignalIconModel> + get() = iconInteractor.signalLevelIcon - override val icon: Flow<SignalIconModel> = interactor.signalLevelIcon - - override val contentDescription: Flow<MobileContentDescription?> = MutableStateFlow(null) + override val contentDescription: KairosState<MobileContentDescription?> = stateOf(null) /** These fields are not used for satellite icons currently */ - override val roaming: Flow<Boolean> = flowOf(false) - override val networkTypeIcon: Flow<Icon.Resource?> = flowOf(null) - override val networkTypeBackground: StateFlow<Icon.Resource?> = MutableStateFlow(null) - override val activityInVisible: Flow<Boolean> = flowOf(false) - override val activityOutVisible: Flow<Boolean> = flowOf(false) - override val activityContainerVisible: Flow<Boolean> = flowOf(false) + override val roaming: KairosState<Boolean> = stateOf(false) + override val networkTypeIcon: KairosState<Icon.Resource?> = stateOf(null) + override val networkTypeBackground: KairosState<Icon.Resource?> = stateOf(null) + override val activityInVisible: KairosState<Boolean> = stateOf(false) + override val activityOutVisible: KairosState<Boolean> = stateOf(false) + override val activityContainerVisible: KairosState<Boolean> = stateOf(false) } /** Terrestrial (cellular) icon. */ -@Suppress("EXPERIMENTAL_IS_NOT_ENABLED") +@ExperimentalKairosApi private class CellularIconViewModelKairos( override val subscriptionId: Int, - iconInteractor: MobileIconInteractor, + override val iconInteractor: MobileIconInteractorKairos, airplaneModeInteractor: AirplaneModeInteractor, constants: ConnectivityConstants, - scope: CoroutineScope, -) : MobileIconViewModelCommon, MobileIconViewModelCommonKairos { - override val isVisible: StateFlow<Boolean> = + flags: FeatureFlagsClassic, +) : MobileIconViewModelKairosCommon, KairosBuilder by kairosBuilder() { + + override val isVisible: KairosState<Boolean> = if (!constants.hasDataCapabilities) { - flowOf(false) - } else { + stateOf(false) + } else { + buildState { combine( - airplaneModeInteractor.isAirplaneMode, - iconInteractor.isAllowedDuringAirplaneMode, - iconInteractor.isForceHidden, - ) { isAirplaneMode, isAllowedDuringAirplaneMode, isForceHidden -> - if (isForceHidden) { - false - } else if (isAirplaneMode) { - isAllowedDuringAirplaneMode - } else { - true + airplaneModeInteractor.isAirplaneMode.toState(), + iconInteractor.isAllowedDuringAirplaneMode, + iconInteractor.isForceHidden, + ) { isAirplaneMode, isAllowedDuringAirplaneMode, isForceHidden -> + if (isForceHidden) { + false + } else if (isAirplaneMode) { + isAllowedDuringAirplaneMode + } else { + true + } + } + .also { + logDiffsForTable(it, iconInteractor.tableLogBuffer, columnName = "visible") } - } } - .distinctUntilChanged() - .logDiffsForTable( - iconInteractor.tableLogBuffer, - columnName = "visible", - initialValue = false, - ) - .stateIn(scope, SharingStarted.WhileSubscribed(), false) + } - override val icon: Flow<SignalIconModel> = iconInteractor.signalLevelIcon + override val icon: KairosState<SignalIconModel> + get() = iconInteractor.signalLevelIcon - override val contentDescription: Flow<MobileContentDescription?> = + override val contentDescription: KairosState<MobileContentDescription?> = combine(iconInteractor.signalLevelIcon, iconInteractor.networkName) { icon, nameModel -> - when (icon) { - is SignalIconModel.Cellular -> - MobileContentDescription.Cellular( - nameModel.name, - icon.levelDescriptionRes(), - ) - else -> null - } + when (icon) { + is SignalIconModel.Cellular -> + MobileContentDescription.Cellular(nameModel.name, icon.levelDescriptionRes()) + else -> null } - .stateIn(scope, SharingStarted.WhileSubscribed(), null) + } private fun SignalIconModel.Cellular.levelDescriptionRes() = when (level) { @@ -241,7 +222,7 @@ private class CellularIconViewModelKairos( else -> R.string.accessibility_no_signal } - private val showNetworkTypeIcon: Flow<Boolean> = + private val showNetworkTypeIcon: KairosState<Boolean> = combine( iconInteractor.isDataConnected, iconInteractor.isDataEnabled, @@ -252,77 +233,72 @@ private class CellularIconViewModelKairos( alwaysShow || (!carrierNetworkChange && (dataEnabled && dataConnected && mobileIsDefault)) } - .distinctUntilChanged() - .logDiffsForTable( - iconInteractor.tableLogBuffer, - columnName = "showNetworkTypeIcon", - initialValue = false, - ) - .stateIn(scope, SharingStarted.WhileSubscribed(), false) - - override val networkTypeIcon: Flow<Icon.Resource?> = - combine(iconInteractor.networkTypeIconGroup, showNetworkTypeIcon) { - networkTypeIconGroup, - shouldShow -> - val desc = - if (networkTypeIconGroup.contentDescription != 0) - ContentDescription.Resource(networkTypeIconGroup.contentDescription) - else null - val icon = - if (networkTypeIconGroup.iconId != 0) - Icon.Resource(networkTypeIconGroup.iconId, desc) - else null - return@combine when { - !shouldShow -> null - else -> icon + .also { + onActivated { + logDiffsForTable( + it, + iconInteractor.tableLogBuffer, + columnName = "showNetworkTypeIcon", + ) } } - .distinctUntilChanged() - .stateIn(scope, SharingStarted.WhileSubscribed(), null) - - override val networkTypeBackground = - iconInteractor.showSliceAttribution - .map { - when { - it && NewStatusBarIcons.isEnabled -> - Icon.Resource(R.drawable.mobile_network_type_background_updated, null) - it -> Icon.Resource(R.drawable.mobile_network_type_background, null) - else -> null + + override val networkTypeIcon: KairosState<Icon.Resource?> = + combine(iconInteractor.networkTypeIconGroup, showNetworkTypeIcon) { + networkTypeIconGroup, + shouldShow -> + val desc = + if (networkTypeIconGroup.contentDescription != 0) { + ContentDescription.Resource(networkTypeIconGroup.contentDescription) + } else { + null } + val icon = + if (networkTypeIconGroup.iconId != 0) { + Icon.Resource(networkTypeIconGroup.iconId, desc) + } else { + null + } + when { + !shouldShow -> null + else -> icon + } + } + + override val networkTypeBackground: KairosState<Icon.Resource?> = + iconInteractor.showSliceAttribution.map { + when { + it && NewStatusBarIcons.isEnabled -> + Icon.Resource(R.drawable.mobile_network_type_background_updated, null) + it -> Icon.Resource(R.drawable.mobile_network_type_background, null) + else -> null } - .stateIn(scope, SharingStarted.WhileSubscribed(), null) - - override val roaming: StateFlow<Boolean> = - iconInteractor.isRoaming - .logDiffsForTable( - iconInteractor.tableLogBuffer, - columnName = "roaming", - initialValue = false, - ) - .stateIn(scope, SharingStarted.WhileSubscribed(), false) - - private val activity: Flow<DataActivityModel?> = + } + + override val roaming: KairosState<Boolean> = + iconInteractor.isRoaming.also { + onActivated { + logDiffsForTable(it, iconInteractor.tableLogBuffer, columnName = "roaming") + } + } + + private val activity: KairosState<DataActivityModel?> = if (!constants.shouldShowActivityConfig) { - flowOf(null) + stateOf(null) } else { iconInteractor.activity } - override val activityInVisible: Flow<Boolean> = - activity - .map { it?.hasActivityIn ?: false } - .stateIn(scope, SharingStarted.WhileSubscribed(), false) + override val activityInVisible: KairosState<Boolean> = + activity.map { it?.hasActivityIn ?: false } - override val activityOutVisible: Flow<Boolean> = - activity - .map { it?.hasActivityOut ?: false } - .stateIn(scope, SharingStarted.WhileSubscribed(), false) + override val activityOutVisible: KairosState<Boolean> = + activity.map { it?.hasActivityOut ?: false } - override val activityContainerVisible: Flow<Boolean> = + override val activityContainerVisible: KairosState<Boolean> = if (statusBarStaticInoutIndicators()) { - flowOf(constants.shouldShowActivityConfig) - } else { - activity.map { it != null && (it.hasActivityIn || it.hasActivityOut) } - } - .stateIn(scope, SharingStarted.WhileSubscribed(), false) + stateOf(constants.shouldShowActivityConfig) + } else { + activity.map { it != null && (it.hasActivityIn || it.hasActivityOut) } + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelKairos.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelKairos.kt index a65540738828..41c72bee9e52 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelKairos.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelKairos.kt @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 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. @@ -16,137 +16,145 @@ package com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel -import androidx.annotation.VisibleForTesting -import com.android.app.tracing.coroutines.launchTraced as launch -import com.android.systemui.coroutines.newTracingContext +import androidx.compose.runtime.State as ComposeState +import androidx.compose.runtime.getValue +import com.android.systemui.Flags +import com.android.systemui.KairosActivatable +import com.android.systemui.KairosBuilder +import com.android.systemui.activated import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.flags.FeatureFlagsClassic +import com.android.systemui.kairos.BuildScope +import com.android.systemui.kairos.BuildSpec +import com.android.systemui.kairos.ExperimentalKairosApi +import com.android.systemui.kairos.Incremental +import com.android.systemui.kairos.State as KairosState +import com.android.systemui.kairos.State +import com.android.systemui.kairos.buildSpec +import com.android.systemui.kairos.combine +import com.android.systemui.kairos.flatten +import com.android.systemui.kairos.map +import com.android.systemui.kairos.mapValues +import com.android.systemui.kairos.stateOf +import com.android.systemui.kairosBuilder import com.android.systemui.statusbar.phone.StatusBarLocation import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor -import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractor +import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconInteractorKairos +import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractorKairos import com.android.systemui.statusbar.pipeline.mobile.ui.MobileViewLogger import com.android.systemui.statusbar.pipeline.mobile.ui.VerboseMobileViewLogger import com.android.systemui.statusbar.pipeline.mobile.ui.view.ModernStatusBarMobileView import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants -import java.util.concurrent.ConcurrentHashMap +import com.android.systemui.util.composable.kairos.toComposeState +import dagger.Provides +import dagger.multibindings.ElementsIntoSet import javax.inject.Inject -import kotlin.coroutines.CoroutineContext -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Job -import kotlinx.coroutines.cancel -import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.flatMapLatest -import kotlinx.coroutines.flow.flowOf -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.mapLatest -import kotlinx.coroutines.flow.stateIn +import javax.inject.Provider /** * View model for describing the system's current mobile cellular connections. The result is a list - * of [MobileIconViewModel]s which describe the individual icons and can be bound to + * of [MobileIconViewModelKairos]s which describe the individual icons and can be bound to * [ModernStatusBarMobileView]. */ +@ExperimentalKairosApi @SysUISingleton class MobileIconsViewModelKairos @Inject constructor( val logger: MobileViewLogger, private val verboseLogger: VerboseMobileViewLogger, - private val interactor: MobileIconsInteractor, + private val interactor: MobileIconsInteractorKairos, private val airplaneModeInteractor: AirplaneModeInteractor, private val constants: ConnectivityConstants, - @Background private val scope: CoroutineScope, -) { - @VisibleForTesting - val reuseCache = ConcurrentHashMap<Int, Pair<MobileIconViewModel, CoroutineScope>>() - - val activeMobileDataSubscriptionId: StateFlow<Int?> = interactor.activeMobileDataSubscriptionId + private val flags: FeatureFlagsClassic, +) : KairosBuilder by kairosBuilder() { - val subscriptionIdsFlow: StateFlow<List<Int>> = - interactor.filteredSubscriptions - .mapLatest { subscriptions -> - subscriptions.map { subscriptionModel -> subscriptionModel.subscriptionId } - } - .stateIn(scope, SharingStarted.WhileSubscribed(), listOf()) + val activeSubscriptionId: State<Int?> + get() = interactor.activeDataIconInteractor.map { it?.subscriptionId } - val mobileSubViewModels: StateFlow<List<MobileIconViewModelCommon>> = - subscriptionIdsFlow - .map { ids -> ids.map { commonViewModelForSub(it) } } - .stateIn(scope, SharingStarted.WhileSubscribed(), emptyList()) + val subscriptionIds: KairosState<List<Int>> = + interactor.filteredSubscriptions.map { subscriptions -> + subscriptions.map { it.subscriptionId } + } - private val firstMobileSubViewModel: StateFlow<MobileIconViewModelCommon?> = - mobileSubViewModels - .map { - if (it.isEmpty()) { - null - } else { - // Mobile icons get reversed by [StatusBarIconController], so the last element - // in this list will show up visually first. - it.last() - } - } - .stateIn(scope, SharingStarted.WhileSubscribed(), null) + val icons: Incremental<Int, MobileIconViewModelKairos> = buildIncremental { + interactor.icons + .mapValues { (subId, icon) -> buildSpec { commonViewModel(subId, icon) } } + .applyLatestSpecForKey() + } - /** - * A flow that emits `true` if the mobile sub that's displayed first visually is showing its - * network type icon and `false` otherwise. - */ - val firstMobileSubShowingNetworkTypeIcon: StateFlow<Boolean> = - firstMobileSubViewModel - .flatMapLatest { firstMobileSubViewModel -> - firstMobileSubViewModel?.networkTypeIcon?.map { it != null } ?: flowOf(false) + /** Whether the mobile sub that's displayed first visually is showing its network type icon. */ + val firstMobileSubShowingNetworkTypeIcon: KairosState<Boolean> = buildState { + combine(subscriptionIds.map { it.lastOrNull() }, icons) { lastId, icons -> + icons[lastId]?.networkTypeIcon?.map { it != null } ?: stateOf(false) } - .stateIn(scope, SharingStarted.WhileSubscribed(), false) - - val isStackable: StateFlow<Boolean> = interactor.isStackable - - init { - scope.launch { subscriptionIdsFlow.collect { invalidateCaches(it) } } + .flatten() } - fun viewModelForSub(subId: Int, location: StatusBarLocation): LocationBasedMobileViewModel { - val common = commonViewModelForSub(subId) - return LocationBasedMobileViewModel.viewModelForLocation( - common, - interactor.getMobileConnectionInteractorForSubId(subId), + val isStackable: KairosState<Boolean> + get() = interactor.isStackable + + fun viewModelForSub( + subId: Int, + location: StatusBarLocation, + ): BuildSpec<LocationBasedMobileViewModelKairos> = buildSpec { + val iconInteractor = + interactor.icons.sample().getOrElse(subId) { error("Unknown subscription id: $subId") } + val commonViewModel = + icons.sample().getOrElse(subId) { error("Unknown subscription id: $subId") } + LocationBasedMobileViewModelKairos.viewModelForLocation( + commonViewModel, + iconInteractor, verboseLogger, location, - scope, ) } - private fun commonViewModelForSub(subId: Int): MobileIconViewModelCommon { - return reuseCache.getOrPut(subId) { createViewModel(subId) }.first - } - - private fun createViewModel(subId: Int): Pair<MobileIconViewModel, CoroutineScope> { - // Create a child scope so we can cancel it - val vmScope = scope.createChildScope(newTracingContext("MobileIconViewModel")) - val vm = - MobileIconViewModel( - subId, - interactor.getMobileConnectionInteractorForSubId(subId), - airplaneModeInteractor, - constants, - vmScope, + fun shadeCarrierGroupIcon(subId: Int): BuildSpec<ShadeCarrierGroupMobileIconViewModelKairos> = + buildSpec { + val iconInteractor = + interactor.icons.sample().getOrElse(subId) { + error("Unknown subscription id: $subId") + } + val commonViewModel = + icons.sample().getOrElse(subId) { error("Unknown subscription id: $subId") } + ShadeCarrierGroupMobileIconViewModelKairos(commonViewModel, iconInteractor) + } + + private fun BuildScope.commonViewModel(subId: Int, iconInteractor: MobileIconInteractorKairos) = + activated { + MobileIconViewModelKairos( + subscriptionId = subId, + iconInteractor = iconInteractor, + airplaneModeInteractor = airplaneModeInteractor, + constants = constants, + flags = flags, ) - - return Pair(vm, vmScope) + } + + @dagger.Module + object Module { + @Provides + @ElementsIntoSet + fun bindKairosActivatable( + impl: Provider<MobileIconsViewModelKairos> + ): Set<@JvmSuppressWildcards KairosActivatable> = + if (Flags.statusBarMobileIconKairos()) setOf(impl.get()) else emptySet() } +} - private fun CoroutineScope.createChildScope(extraContext: CoroutineContext) = - CoroutineScope(coroutineContext + Job(coroutineContext[Job]) + extraContext) +@ExperimentalKairosApi +class MobileIconsViewModelKairosComposeWrapper( + icons: ComposeState<Map<Int, MobileIconViewModelKairos>>, + val logger: MobileViewLogger, +) { + val icons: Map<Int, MobileIconViewModelKairos> by icons +} - private fun invalidateCaches(subIds: List<Int>) { - reuseCache.keys - .filter { !subIds.contains(it) } - .forEach { id -> - reuseCache - .remove(id) - // Cancel the view model's scope after removing it - ?.second - ?.cancel() - } - } +@ExperimentalKairosApi +fun MobileIconsViewModelKairos.composeWrapper(): BuildSpec<MobileIconsViewModelKairosComposeWrapper> = buildSpec { + MobileIconsViewModelKairosComposeWrapper( + icons = toComposeState(icons), + logger = logger, + ) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModel.kt index 2c85a5150575..060454c2b1c5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModel.kt @@ -22,6 +22,7 @@ import com.android.systemui.common.shared.model.Icon import com.android.systemui.lifecycle.ExclusiveActivatable import com.android.systemui.lifecycle.Hydrator import com.android.systemui.statusbar.pipeline.mobile.domain.model.SignalIconModel +import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.StackedMobileIconViewModel.DualSim import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -30,10 +31,22 @@ import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf +interface StackedMobileIconViewModel { + val dualSim: DualSim? + val networkTypeIcon: Icon.Resource? + val isIconVisible: Boolean + + data class DualSim( + val primary: SignalIconModel.Cellular, + val secondary: SignalIconModel.Cellular, + ) +} + @OptIn(ExperimentalCoroutinesApi::class) -class StackedMobileIconViewModel +class StackedMobileIconViewModelImpl @AssistedInject -constructor(mobileIconsViewModel: MobileIconsViewModel) : ExclusiveActivatable() { +constructor(mobileIconsViewModel: MobileIconsViewModel) : + ExclusiveActivatable(), StackedMobileIconViewModel { private val hydrator = Hydrator("StackedMobileIconViewModel") private val isStackable: Boolean by @@ -52,7 +65,7 @@ constructor(mobileIconsViewModel: MobileIconsViewModel) : ExclusiveActivatable() viewModels.sortedByDescending { it.subscriptionId == activeSubId } } - val dualSim: DualSim? by + override val dualSim: DualSim? by hydrator.hydratedStateOf( traceName = "dualSim", source = @@ -68,7 +81,7 @@ constructor(mobileIconsViewModel: MobileIconsViewModel) : ExclusiveActivatable() initialValue = null, ) - val networkTypeIcon: Icon.Resource? by + override val networkTypeIcon: Icon.Resource? by hydrator.hydratedStateOf( traceName = "networkTypeIcon", source = @@ -78,7 +91,7 @@ constructor(mobileIconsViewModel: MobileIconsViewModel) : ExclusiveActivatable() initialValue = null, ) - val isIconVisible: Boolean by derivedStateOf { isStackable && dualSim != null } + override val isIconVisible: Boolean by derivedStateOf { isStackable && dualSim != null } override suspend fun onActivated(): Nothing { hydrator.activate() @@ -86,11 +99,6 @@ constructor(mobileIconsViewModel: MobileIconsViewModel) : ExclusiveActivatable() @AssistedFactory interface Factory { - fun create(): StackedMobileIconViewModel + fun create(): StackedMobileIconViewModelImpl } - - data class DualSim( - val primary: SignalIconModel.Cellular, - val secondary: SignalIconModel.Cellular, - ) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModelKairos.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModelKairos.kt index 2dbb02c8f095..402fdf03941d 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModelKairos.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModelKairos.kt @@ -16,81 +16,71 @@ package com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel -import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue +import com.android.systemui.KairosBuilder import com.android.systemui.common.shared.model.Icon -import com.android.systemui.lifecycle.ExclusiveActivatable -import com.android.systemui.lifecycle.Hydrator +import com.android.systemui.kairos.ExperimentalKairosApi +import com.android.systemui.kairos.State as KairosState +import com.android.systemui.kairos.combine +import com.android.systemui.kairos.flatMap +import com.android.systemui.kairos.stateOf +import com.android.systemui.kairosBuilder import com.android.systemui.statusbar.pipeline.mobile.domain.model.SignalIconModel +import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.StackedMobileIconViewModel.DualSim +import com.android.systemui.util.composable.kairos.hydratedComposeStateOf import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.flatMapLatest -import kotlinx.coroutines.flow.flowOf -@OptIn(ExperimentalCoroutinesApi::class) +@OptIn(ExperimentalKairosApi::class) class StackedMobileIconViewModelKairos @AssistedInject -constructor(mobileIconsViewModel: MobileIconsViewModel) : ExclusiveActivatable() { - private val hydrator = Hydrator("StackedMobileIconViewModel") +constructor(mobileIcons: MobileIconsViewModelKairos) : + KairosBuilder by kairosBuilder(), StackedMobileIconViewModel { private val isStackable: Boolean by - hydrator.hydratedStateOf( - traceName = "isStackable", - source = mobileIconsViewModel.isStackable, - initialValue = false, - ) + hydratedComposeStateOf(mobileIcons.isStackable, initialValue = false) - private val iconViewModelFlow: Flow<List<MobileIconViewModelCommon>> = - combine( - mobileIconsViewModel.mobileSubViewModels, - mobileIconsViewModel.activeMobileDataSubscriptionId, - ) { viewModels, activeSubId -> - // Sort to get the active subscription first, if it's set - viewModels.sortedByDescending { it.subscriptionId == activeSubId } + private val iconList: KairosState<List<MobileIconViewModelKairos>> = + combine(mobileIcons.icons, mobileIcons.activeSubscriptionId) { iconsBySubId, activeSubId -> + buildList { + activeSubId?.let { iconsBySubId[activeSubId]?.let { add(it) } } + addAll(iconsBySubId.values.asSequence().filter { it.subscriptionId != activeSubId }) + } } - val dualSim: DualSim? by - hydrator.hydratedStateOf( - traceName = "dualSim", - source = - iconViewModelFlow.flatMapLatest { viewModels -> - combine(viewModels.map { it.icon }) { icons -> - icons - .toList() - .filterIsInstance<SignalIconModel.Cellular>() - .takeIf { it.size == 2 } - ?.let { DualSim(it[0], it[1]) } - } - }, + override val dualSim: DualSim? by + hydratedComposeStateOf( + iconList.flatMap { icons -> + icons.map { it.icon }.combine { signalIcons -> tryParseDualSim(signalIcons) } + }, initialValue = null, ) - val networkTypeIcon: Icon.Resource? by - hydrator.hydratedStateOf( - traceName = "networkTypeIcon", - source = - iconViewModelFlow.flatMapLatest { viewModels -> - viewModels.firstOrNull()?.networkTypeIcon ?: flowOf(null) - }, + override val networkTypeIcon: Icon.Resource? by + hydratedComposeStateOf( + iconList.flatMap { icons -> icons.firstOrNull()?.networkTypeIcon ?: stateOf(null) }, initialValue = null, ) - val isIconVisible: Boolean by derivedStateOf { isStackable && dualSim != null } + override val isIconVisible: Boolean + get() = isStackable && dualSim != null - override suspend fun onActivated(): Nothing { - hydrator.activate() + private fun tryParseDualSim(icons: List<SignalIconModel>): DualSim? { + var first: SignalIconModel.Cellular? = null + var second: SignalIconModel.Cellular? = null + for (icon in icons) { + when { + icon !is SignalIconModel.Cellular -> continue + first == null -> first = icon + second == null -> second = icon + else -> return null + } + } + return first?.let { second?.let { DualSim(first, second) } } } @AssistedFactory interface Factory { fun create(): StackedMobileIconViewModelKairos } - - data class DualSim( - val primary: SignalIconModel.Cellular, - val secondary: SignalIconModel.Cellular, - ) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/util/FakeMobileMappingsProxy.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/util/FakeMobileMappingsProxy.kt index a052008d4832..cba06e3caa58 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/util/FakeMobileMappingsProxy.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/util/FakeMobileMappingsProxy.kt @@ -33,17 +33,20 @@ class FakeMobileMappingsProxy : MobileMappingsProxy { fun setIconMap(map: Map<String, MobileIconGroup>) { iconMap = map } + override fun mapIconSets(config: Config): Map<String, MobileIconGroup> { if (useRealImpl) { return realImpl.mapIconSets(config) } return iconMap } + fun getIconMap() = iconMap fun setDefaultIcons(group: MobileIconGroup) { defaultIcons = group } + override fun getDefaultIcons(config: Config): MobileIconGroup { if (useRealImpl) { return realImpl.getDefaultIcons(config) @@ -72,3 +75,6 @@ class FakeMobileMappingsProxy : MobileMappingsProxy { return toIconKey(networkType) + "_override" } } + +val MobileMappingsProxy.fake + get() = this as FakeMobileMappingsProxy diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseUserSwitcherAdapter.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseUserSwitcherAdapter.kt index 4c6374b75f82..efab21fd9364 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseUserSwitcherAdapter.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/BaseUserSwitcherAdapter.kt @@ -37,7 +37,7 @@ protected constructor( protected open val users: List<UserRecord> get() = controller.users.filter { (!controller.isKeyguardShowing || !it.isRestricted) && - (controller.isUserSwitcherEnabled || it.isCurrent) + (controller.isUserSwitcherEnabled || it.isCurrent || it.isSignOut) } init { @@ -109,6 +109,7 @@ protected constructor( item.isAddUser, item.isGuest, item.isAddSupervisedUser, + item.isSignOut, isTablet, item.isManageUsers, ) diff --git a/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyLogger.kt b/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyLogger.kt index 1cc7a3185a5d..5541c50fb650 100644 --- a/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyLogger.kt @@ -50,7 +50,7 @@ class DisplaySwitchLatencyLogger { onScreenTurningOnToOnDrawnMs, onDrawnToOnScreenTurnedOnMs, trackingResult, - screenWakelockstatus + screenWakelockStatus, ) } } diff --git a/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyTracker.kt b/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyTracker.kt index 5800d5ed41c6..336e8d172ad4 100644 --- a/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyTracker.kt +++ b/packages/SystemUI/src/com/android/systemui/unfold/DisplaySwitchLatencyTracker.kt @@ -36,11 +36,11 @@ import com.android.systemui.power.shared.model.WakeSleepReason import com.android.systemui.power.shared.model.WakefulnessModel import com.android.systemui.power.shared.model.WakefulnessState import com.android.systemui.shared.system.SysUiStatsLog -import com.android.systemui.unfold.DisplaySwitchLatencyTracker.DisplaySwitchLatencyEvent import com.android.systemui.unfold.DisplaySwitchLatencyTracker.TrackingResult.CORRUPTED import com.android.systemui.unfold.DisplaySwitchLatencyTracker.TrackingResult.SUCCESS import com.android.systemui.unfold.DisplaySwitchLatencyTracker.TrackingResult.TIMED_OUT import com.android.systemui.unfold.dagger.UnfoldSingleThreadBg +import com.android.systemui.unfold.data.repository.ScreenTimeoutPolicyRepository import com.android.systemui.unfold.data.repository.UnfoldTransitionStatus.TransitionStarted import com.android.systemui.unfold.domain.interactor.UnfoldTransitionInteractor import com.android.systemui.util.Compile @@ -80,6 +80,7 @@ constructor( private val context: Context, private val deviceStateRepository: DeviceStateRepository, private val powerInteractor: PowerInteractor, + private val screenTimeoutPolicyRepository: ScreenTimeoutPolicyRepository, private val unfoldTransitionInteractor: UnfoldTransitionInteractor, private val animationStatusRepository: AnimationStatusRepository, private val keyguardInteractor: KeyguardInteractor, @@ -287,7 +288,18 @@ constructor( log { "fromFoldableDeviceState=$fromFoldableDeviceState" } instantForTrack(TAG) { "fromFoldableDeviceState=$fromFoldableDeviceState" } - return copy(fromFoldableDeviceState = fromFoldableDeviceState) + val screenTimeoutActive = screenTimeoutPolicyRepository.screenTimeoutActive.value + val screenWakelockStatus = + if (screenTimeoutActive) { + NO_SCREEN_WAKELOCKS + } else { + HAS_SCREEN_WAKELOCKS + } + + return copy( + fromFoldableDeviceState = fromFoldableDeviceState, + screenWakelockStatus = screenWakelockStatus + ) } private fun DisplaySwitchLatencyEvent.withAfterFields( @@ -344,7 +356,7 @@ constructor( val onDrawnToOnScreenTurnedOnMs: Int = VALUE_UNKNOWN, val trackingResult: Int = SysUiStatsLog.DISPLAY_SWITCH_LATENCY_TRACKED__TRACKING_RESULT__UNKNOWN_RESULT, - val screenWakelockstatus: Int = + val screenWakelockStatus: Int = SysUiStatsLog.DISPLAY_SWITCH_LATENCY_TRACKED__SCREEN_WAKELOCK_STATUS__SCREEN_WAKELOCK_STATUS_UNKNOWN, ) @@ -372,5 +384,10 @@ constructor( SysUiStatsLog.DISPLAY_SWITCH_LATENCY_TRACKED__FROM_FOLDABLE_DEVICE_STATE__STATE_OPENED private const val FOLDABLE_DEVICE_STATE_FLIPPED = SysUiStatsLog.DISPLAY_SWITCH_LATENCY_TRACKED__FROM_FOLDABLE_DEVICE_STATE__STATE_FLIPPED + + private const val HAS_SCREEN_WAKELOCKS = + SysUiStatsLog.DISPLAY_SWITCH_LATENCY_TRACKED__SCREEN_WAKELOCK_STATUS__SCREEN_WAKELOCK_STATUS_HAS_SCREEN_WAKELOCKS + private const val NO_SCREEN_WAKELOCKS = + SysUiStatsLog.DISPLAY_SWITCH_LATENCY_TRACKED__SCREEN_WAKELOCK_STATUS__SCREEN_WAKELOCK_STATUS_NO_WAKELOCKS } } diff --git a/packages/SystemUI/src/com/android/systemui/unfold/data/repository/ScreenTimeoutPolicyRepository.kt b/packages/SystemUI/src/com/android/systemui/unfold/data/repository/ScreenTimeoutPolicyRepository.kt new file mode 100644 index 000000000000..797939447464 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/unfold/data/repository/ScreenTimeoutPolicyRepository.kt @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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.data.repository + +import android.os.PowerManager +import android.view.Display +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow +import java.util.concurrent.Executor +import javax.inject.Inject +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.stateIn + +/** Repository to get screen timeout updates */ +@SysUISingleton +class ScreenTimeoutPolicyRepository +@Inject +constructor( + private val powerManager: PowerManager, + @Background private val executor: Executor, + @Background private val scope: CoroutineScope, +) { + + /** Stores true if there is an active screen timeout */ + val screenTimeoutActive: StateFlow<Boolean> = + conflatedCallbackFlow { + val listener = + PowerManager.ScreenTimeoutPolicyListener { screenTimeoutPolicy -> + trySend(screenTimeoutPolicy == PowerManager.SCREEN_TIMEOUT_ACTIVE) + } + powerManager.addScreenTimeoutPolicyListener( + Display.DEFAULT_DISPLAY, + executor, + listener, + ) + awaitClose { + powerManager.removeScreenTimeoutPolicyListener( + Display.DEFAULT_DISPLAY, + listener, + ) + } + } + .stateIn(scope, started = SharingStarted.Eagerly, initialValue = true) +} diff --git a/packages/SystemUI/src/com/android/systemui/user/data/source/UserRecord.kt b/packages/SystemUI/src/com/android/systemui/user/data/source/UserRecord.kt index d4fb5634bd1d..e16a51a6f6fa 100644 --- a/packages/SystemUI/src/com/android/systemui/user/data/source/UserRecord.kt +++ b/packages/SystemUI/src/com/android/systemui/user/data/source/UserRecord.kt @@ -42,6 +42,8 @@ data class UserRecord( @JvmField val isSwitchToEnabled: Boolean = false, /** Whether this record represents an option to add another supervised user to the device. */ @JvmField val isAddSupervisedUser: Boolean = false, + /** Whether this record represents an option to sign out of the current user. */ + @JvmField val isSignOut: Boolean = false, /** * An enforcing admin, if the user action represented by this record is disabled by the admin. * If not disabled, this is `null`. @@ -49,7 +51,7 @@ data class UserRecord( @JvmField val enforcedAdmin: RestrictedLockUtils.EnforcedAdmin? = null, /** Whether this record is to go to the Settings page to manage users. */ - @JvmField val isManageUsers: Boolean = false + @JvmField val isManageUsers: Boolean = false, ) { /** Returns a new instance of [UserRecord] with its [isCurrent] set to the given value. */ fun copyWithIsCurrent(isCurrent: Boolean): UserRecord { diff --git a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractor.kt b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractor.kt index 163288b25b28..b82aefc1ac1c 100644 --- a/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractor.kt @@ -38,6 +38,7 @@ import com.android.internal.util.UserIcons import com.android.keyguard.KeyguardUpdateMonitor import com.android.keyguard.KeyguardUpdateMonitorCallback import com.android.systemui.Flags.switchUserOnBg +import com.android.systemui.Flags.userSwitcherAddSignOutOption import com.android.systemui.SystemUISecondaryUserService import com.android.systemui.animation.Expandable import com.android.systemui.broadcast.BroadcastDispatcher @@ -110,6 +111,7 @@ constructor( private val uiEventLogger: UiEventLogger, private val userRestrictionChecker: UserRestrictionChecker, private val processWrapper: ProcessWrapper, + private val userLogoutInteractor: UserLogoutInteractor, ) { /** * Defines interface for classes that can be notified when the state of users on the device is @@ -242,6 +244,12 @@ constructor( ) { add(UserActionModel.NAVIGATE_TO_USER_MANAGEMENT) } + if ( + userSwitcherAddSignOutOption() && + userLogoutInteractor.isLogoutEnabled.value + ) { + add(UserActionModel.SIGN_OUT) + } } } .flowOn(backgroundDispatcher) @@ -261,7 +269,8 @@ constructor( action = it, selectedUserId = selectedUserInfo.id, isRestricted = - it != UserActionModel.ENTER_GUEST_MODE && + it != UserActionModel.SIGN_OUT && + it != UserActionModel.ENTER_GUEST_MODE && it != UserActionModel.NAVIGATE_TO_USER_MANAGEMENT && !settings.isAddUsersFromLockscreen, ) @@ -499,6 +508,10 @@ constructor( Intent(Settings.ACTION_USER_SETTINGS), /* dismissShade= */ true, ) + UserActionModel.SIGN_OUT -> { + dismissDialog() + applicationScope.launch { userLogoutInteractor.logOut() } + } } } @@ -583,9 +596,10 @@ constructor( actionType = action, isRestricted = isRestricted, isSwitchToEnabled = - canSwitchUsers(selectedUserId = selectedUserId, isAction = true) && - // If the user is auto-created is must not be currently resetting. - !(isGuestUserAutoCreated && isGuestUserResetting), + action == UserActionModel.SIGN_OUT || + (canSwitchUsers(selectedUserId = selectedUserId, isAction = true) && + // If the user is auto-created is must not be currently resetting. + !(isGuestUserAutoCreated && isGuestUserResetting)), userRestrictionChecker = userRestrictionChecker, ) } diff --git a/packages/SystemUI/src/com/android/systemui/user/legacyhelper/data/LegacyUserDataHelper.kt b/packages/SystemUI/src/com/android/systemui/user/legacyhelper/data/LegacyUserDataHelper.kt index 80139bd6ac0c..23ca4ceda2de 100644 --- a/packages/SystemUI/src/com/android/systemui/user/legacyhelper/data/LegacyUserDataHelper.kt +++ b/packages/SystemUI/src/com/android/systemui/user/legacyhelper/data/LegacyUserDataHelper.kt @@ -74,6 +74,7 @@ object LegacyUserDataHelper { isGuest = actionType == UserActionModel.ENTER_GUEST_MODE, isAddUser = actionType == UserActionModel.ADD_USER, isAddSupervisedUser = actionType == UserActionModel.ADD_SUPERVISED_USER, + isSignOut = actionType == UserActionModel.SIGN_OUT, isRestricted = isRestricted, isSwitchToEnabled = isSwitchToEnabled, enforcedAdmin = @@ -94,6 +95,7 @@ object LegacyUserDataHelper { record.isAddSupervisedUser -> UserActionModel.ADD_SUPERVISED_USER record.isGuest -> UserActionModel.ENTER_GUEST_MODE record.isManageUsers -> UserActionModel.NAVIGATE_TO_USER_MANAGEMENT + record.isSignOut -> UserActionModel.SIGN_OUT else -> error("Not a known action: $record") } } @@ -105,15 +107,14 @@ object LegacyUserDataHelper { private fun getEnforcedAdmin( context: Context, selectedUserId: Int, - userRestrictionChecker: UserRestrictionChecker + userRestrictionChecker: UserRestrictionChecker, ): EnforcedAdmin? { val admin = userRestrictionChecker.checkIfRestrictionEnforced( context, UserManager.DISALLOW_ADD_USER, selectedUserId, - ) - ?: return null + ) ?: return null return if ( !userRestrictionChecker.hasBaseUserRestriction( @@ -145,11 +146,6 @@ object LegacyUserDataHelper { val unscaledOrNull = manager.getUserIcon(userInfo.id) ?: return null val avatarSize = context.resources.getDimensionPixelSize(R.dimen.max_avatar_size) - return Bitmap.createScaledBitmap( - unscaledOrNull, - avatarSize, - avatarSize, - /* filter= */ true, - ) + return Bitmap.createScaledBitmap(unscaledOrNull, avatarSize, avatarSize, /* filter= */ true) } } diff --git a/packages/SystemUI/src/com/android/systemui/user/legacyhelper/ui/LegacyUserUiHelper.kt b/packages/SystemUI/src/com/android/systemui/user/legacyhelper/ui/LegacyUserUiHelper.kt index 09cef1ed64fc..e7a3c23e9119 100644 --- a/packages/SystemUI/src/com/android/systemui/user/legacyhelper/ui/LegacyUserUiHelper.kt +++ b/packages/SystemUI/src/com/android/systemui/user/legacyhelper/ui/LegacyUserUiHelper.kt @@ -41,6 +41,7 @@ object LegacyUserUiHelper { isAddUser: Boolean, isGuest: Boolean, isAddSupervisedUser: Boolean, + isSignOut: Boolean, isTablet: Boolean = false, isManageUsers: Boolean, ): Int { @@ -52,6 +53,8 @@ object LegacyUserUiHelper { com.android.settingslib.R.drawable.ic_account_circle } else if (isAddSupervisedUser) { com.android.settingslib.R.drawable.ic_add_supervised_user + } else if (isSignOut) { + com.android.internal.R.drawable.ic_logout } else if (isManageUsers) { R.drawable.ic_manage_users } else { @@ -81,6 +84,7 @@ object LegacyUserUiHelper { isGuestUserResetting = isGuestUserResetting, isAddUser = record.isAddUser, isAddSupervisedUser = record.isAddSupervisedUser, + isSignOut = record.isSignOut, isTablet = isTablet, isManageUsers = record.isManageUsers, ) @@ -111,10 +115,11 @@ object LegacyUserUiHelper { isGuestUserResetting: Boolean, isAddUser: Boolean, isAddSupervisedUser: Boolean, + isSignOut: Boolean, isTablet: Boolean = false, isManageUsers: Boolean, ): Int { - check(isGuest || isAddUser || isAddSupervisedUser || isManageUsers) + check(isGuest || isAddUser || isAddSupervisedUser || isManageUsers || isSignOut) return when { isGuest && isGuestUserAutoCreated && isGuestUserResetting -> @@ -124,6 +129,7 @@ object LegacyUserUiHelper { isGuest -> com.android.internal.R.string.guest_name isAddUser -> com.android.settingslib.R.string.user_add_user isAddSupervisedUser -> R.string.add_user_supervised + isSignOut -> com.android.internal.R.string.global_action_logout isManageUsers -> R.string.manage_users else -> error("This should never happen!") } diff --git a/packages/SystemUI/src/com/android/systemui/user/shared/model/UserActionModel.kt b/packages/SystemUI/src/com/android/systemui/user/shared/model/UserActionModel.kt index 823bf74dc0f0..7f67d7691bf5 100644 --- a/packages/SystemUI/src/com/android/systemui/user/shared/model/UserActionModel.kt +++ b/packages/SystemUI/src/com/android/systemui/user/shared/model/UserActionModel.kt @@ -22,4 +22,5 @@ enum class UserActionModel { ADD_USER, ADD_SUPERVISED_USER, NAVIGATE_TO_USER_MANAGEMENT, + SIGN_OUT, } diff --git a/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModel.kt b/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModel.kt index 4089889f4b1e..2e3af1c7ad00 100644 --- a/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModel.kt @@ -129,9 +129,7 @@ constructor( cancelButtonClicked || executedActionFinish || userSwitched } - private fun toViewModel( - model: UserModel, - ): UserViewModel { + private fun toViewModel(model: UserModel): UserViewModel { return UserViewModel( viewKey = model.id, name = @@ -152,14 +150,13 @@ constructor( ) } - private fun toViewModel( - model: UserActionModel, - ): UserActionViewModel { + private fun toViewModel(model: UserActionModel): UserActionViewModel { return UserActionViewModel( viewKey = model.ordinal.toLong(), iconResourceId = LegacyUserUiHelper.getUserSwitcherActionIconResourceId( isAddSupervisedUser = model == UserActionModel.ADD_SUPERVISED_USER, + isSignOut = model == UserActionModel.SIGN_OUT, isAddUser = model == UserActionModel.ADD_USER, isGuest = model == UserActionModel.ENTER_GUEST_MODE, isManageUsers = model == UserActionModel.NAVIGATE_TO_USER_MANAGEMENT, @@ -171,6 +168,7 @@ constructor( isGuestUserAutoCreated = guestUserInteractor.isGuestUserAutoCreated, isGuestUserResetting = guestUserInteractor.isGuestUserResetting, isAddSupervisedUser = model == UserActionModel.ADD_SUPERVISED_USER, + isSignOut = model == UserActionModel.SIGN_OUT, isAddUser = model == UserActionModel.ADD_USER, isManageUsers = model == UserActionModel.NAVIGATE_TO_USER_MANAGEMENT, isTablet = true, diff --git a/packages/SystemUI/src/com/android/systemui/util/AlarmTimeout.java b/packages/SystemUI/src/com/android/systemui/util/AlarmTimeout.java index a791376a48d5..38ac5e6be2e1 100644 --- a/packages/SystemUI/src/com/android/systemui/util/AlarmTimeout.java +++ b/packages/SystemUI/src/com/android/systemui/util/AlarmTimeout.java @@ -26,7 +26,6 @@ import android.os.SystemClock; */ public class AlarmTimeout implements AlarmManager.OnAlarmListener { - public static final int MODE_CRASH_IF_SCHEDULED = 0; public static final int MODE_IGNORE_IF_SCHEDULED = 1; public static final int MODE_RESCHEDULE_IF_SCHEDULED = 2; @@ -48,17 +47,11 @@ public class AlarmTimeout implements AlarmManager.OnAlarmListener { * Schedules an alarm in {@code timeout} milliseconds in the future. * * @param timeout How long to wait from now. - * @param mode {@link #MODE_CRASH_IF_SCHEDULED}, {@link #MODE_IGNORE_IF_SCHEDULED} or - * {@link #MODE_RESCHEDULE_IF_SCHEDULED}. + * @param mode {@link #MODE_IGNORE_IF_SCHEDULED} or {@link #MODE_RESCHEDULE_IF_SCHEDULED}. * @return {@code true} when scheduled successfully, {@code false} otherwise. */ public boolean schedule(long timeout, int mode) { switch (mode) { - case MODE_CRASH_IF_SCHEDULED: - if (mScheduled) { - throw new IllegalStateException(mTag + " timeout is already scheduled"); - } - break; case MODE_IGNORE_IF_SCHEDULED: if (mScheduled) { return false; diff --git a/packages/SystemUI/src/com/android/systemui/util/composable/kairos/ActivatedKairosSpec.kt b/packages/SystemUI/src/com/android/systemui/util/composable/kairos/ActivatedKairosSpec.kt new file mode 100644 index 000000000000..a108b1081cd2 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/util/composable/kairos/ActivatedKairosSpec.kt @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.util.composable.kairos + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import com.android.systemui.kairos.BuildSpec +import com.android.systemui.kairos.ExperimentalKairosApi +import com.android.systemui.kairos.KairosNetwork +import com.android.systemui.kairos.awaitClose +import com.android.systemui.kairos.launchEffect + +/** + * Activates the Kairos [buildSpec] within [kairosNetwork], bound to the current composition. + * + * [block] will be invoked with the result of activating the [buildSpec]. [buildSpec] will be + * deactivated automatically when [ActivatedKairosSpec] leaves the composition. + */ +@ExperimentalKairosApi +@Composable +fun <T> ActivatedKairosSpec( + buildSpec: BuildSpec<T>, + kairosNetwork: KairosNetwork, + block: @Composable (T) -> Unit, +) { + val uninit = Any() + var state by remember { mutableStateOf<Any?>(uninit) } + LaunchedEffect(key1 = Unit) { + kairosNetwork.activateSpec { + val v = buildSpec.applySpec() + launchEffect { + state = v + awaitClose { state = uninit } + } + } + } + state.let { + if (it !== uninit) { + @Suppress("UNCHECKED_CAST") block(it as T) + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/util/composable/kairos/HydratedComposeStateOf.kt b/packages/SystemUI/src/com/android/systemui/util/composable/kairos/HydratedComposeStateOf.kt new file mode 100644 index 000000000000..0d53a001f3f4 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/util/composable/kairos/HydratedComposeStateOf.kt @@ -0,0 +1,31 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.util.composable.kairos + +import androidx.compose.runtime.mutableStateOf +import com.android.systemui.KairosBuilder +import com.android.systemui.kairos.ExperimentalKairosApi +import com.android.systemui.kairos.State + +@ExperimentalKairosApi +fun <T> KairosBuilder.hydratedComposeStateOf( + source: State<T>, + initialValue: T, +): androidx.compose.runtime.State<T> = + mutableStateOf(initialValue).also { state -> + onActivated { source.observe { state.value = it } } + } diff --git a/packages/SystemUI/src/com/android/systemui/util/composable/kairos/RememberKairosActivatable.kt b/packages/SystemUI/src/com/android/systemui/util/composable/kairos/RememberKairosActivatable.kt new file mode 100644 index 000000000000..03a60bc002e8 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/util/composable/kairos/RememberKairosActivatable.kt @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.util.composable.kairos + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.remember +import com.android.systemui.KairosActivatable +import com.android.systemui.kairos.ExperimentalKairosApi +import com.android.systemui.kairos.KairosNetwork + +@ExperimentalKairosApi +@Composable +fun <T : KairosActivatable> rememberKairosActivatable( + kairosNetwork: KairosNetwork, + key: Any = Unit, + factory: () -> T, +): T { + val instance = remember(key, factory) { factory() } + LaunchedEffect(instance, kairosNetwork) { + kairosNetwork.activateSpec { instance.run { activate() } } + } + return instance +} diff --git a/packages/SystemUI/src/com/android/systemui/util/composable/kairos/ToComposeState.kt b/packages/SystemUI/src/com/android/systemui/util/composable/kairos/ToComposeState.kt new file mode 100644 index 000000000000..b3accb66c179 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/util/composable/kairos/ToComposeState.kt @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.util.composable.kairos + +import androidx.compose.runtime.State as ComposeState +import androidx.compose.runtime.mutableStateOf +import com.android.systemui.kairos.BuildScope +import com.android.systemui.kairos.ExperimentalKairosApi +import com.android.systemui.kairos.State as KairosState +import com.android.systemui.kairos.changes + +/** + * Returns a [ComposeState] that is kept hydrated with the current value of [state] within this + * [BuildScope]. + */ +@ExperimentalKairosApi +fun <T> BuildScope.toComposeState(state: KairosState<T>): ComposeState<T> { + val initial = state.sample() + val cState = mutableStateOf(initial) + state.changes.observe { cState.value = it } + return cState +} diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt index 32f784f17bb7..db4b8ef5aef7 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/VolumeDialogSliderViewBinder.kt @@ -17,42 +17,37 @@ package com.android.systemui.volume.dialog.sliders.ui import android.view.View -import androidx.compose.animation.AnimatedVisibility -import androidx.compose.animation.core.tween -import androidx.compose.animation.fadeIn -import androidx.compose.animation.fadeOut import androidx.compose.foundation.gestures.Orientation import androidx.compose.foundation.interaction.DragInteraction import androidx.compose.foundation.interaction.MutableInteractionSource -import androidx.compose.foundation.layout.BoxScope -import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi import androidx.compose.material3.MaterialTheme import androidx.compose.material3.SliderDefaults import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.remember -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.platform.ComposeView +import androidx.compose.ui.unit.DpSize import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.android.compose.theme.PlatformTheme -import com.android.systemui.common.shared.model.Icon import com.android.systemui.common.ui.compose.Icon import com.android.systemui.haptics.slider.SliderHapticFeedbackFilter import com.android.systemui.haptics.slider.compose.ui.SliderHapticsViewModel import com.android.systemui.res.R import com.android.systemui.volume.dialog.sliders.dagger.VolumeDialogSliderScope -import com.android.systemui.volume.dialog.sliders.ui.compose.VolumeDialogSliderTrack +import com.android.systemui.volume.dialog.sliders.ui.compose.SliderTrack import com.android.systemui.volume.dialog.sliders.ui.viewmodel.VolumeDialogOverscrollViewModel import com.android.systemui.volume.dialog.sliders.ui.viewmodel.VolumeDialogSliderViewModel -import com.android.systemui.volume.ui.slider.AccessibilityParams -import com.android.systemui.volume.ui.slider.Haptics -import com.android.systemui.volume.ui.slider.Slider +import com.android.systemui.volume.ui.compose.slider.AccessibilityParams +import com.android.systemui.volume.ui.compose.slider.Haptics +import com.android.systemui.volume.ui.compose.slider.Slider +import com.android.systemui.volume.ui.compose.slider.SliderIcon import javax.inject.Inject import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.currentCoroutineContext @@ -85,7 +80,7 @@ constructor( } } -@OptIn(ExperimentalMaterial3Api::class) +@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class) @Composable private fun VolumeDialogSlider( viewModel: VolumeDialogSliderViewModel, @@ -95,10 +90,7 @@ private fun VolumeDialogSlider( ) { val colors = SliderDefaults.colors( - thumbColor = MaterialTheme.colorScheme.primary, activeTickColor = MaterialTheme.colorScheme.surfaceContainerHighest, - inactiveTickColor = MaterialTheme.colorScheme.primary, - activeTrackColor = MaterialTheme.colorScheme.primary, inactiveTrackColor = MaterialTheme.colorScheme.surfaceContainerHighest, ) val collectedSliderStateModel by viewModel.state.collectAsStateWithLifecycle(null) @@ -142,18 +134,38 @@ private fun VolumeDialogSlider( } ?: Haptics.Disabled, stepDistance = 1f, track = { sliderState -> - VolumeDialogSliderTrack( + SliderTrack( sliderState, colors = colors, isEnabled = !sliderStateModel.isDisabled, - activeTrackEndIcon = { iconsState -> - VolumeIcon(sliderStateModel.icon, iconsState.isActiveTrackEndIconVisible) + isVertical = true, + activeTrackStartIcon = { iconsState -> + SliderIcon( + icon = { + Icon(icon = sliderStateModel.icon, modifier = Modifier.size(20.dp)) + }, + isVisible = iconsState.isActiveTrackStartIconVisible, + ) }, - inactiveTrackEndIcon = { iconsState -> - VolumeIcon(sliderStateModel.icon, !iconsState.isActiveTrackEndIconVisible) + inactiveTrackStartIcon = { iconsState -> + SliderIcon( + icon = { + Icon(icon = sliderStateModel.icon, modifier = Modifier.size(20.dp)) + }, + isVisible = !iconsState.isActiveTrackStartIconVisible, + ) }, ) }, + thumb = { sliderState, interactions -> + SliderDefaults.Thumb( + sliderState = sliderState, + interactionSource = interactions, + enabled = !sliderStateModel.isDisabled, + colors = colors, + thumbSize = DpSize(52.dp, 4.dp), + ) + }, accessibilityParams = AccessibilityParams(contentDescription = sliderStateModel.label), modifier = modifier.pointerInput(Unit) { @@ -168,19 +180,3 @@ private fun VolumeDialogSlider( }, ) } - -@Composable -private fun BoxScope.VolumeIcon( - icon: Icon.Loaded, - isVisible: Boolean, - modifier: Modifier = Modifier, -) { - AnimatedVisibility( - visible = isVisible, - enter = fadeIn(animationSpec = tween(delayMillis = 33, durationMillis = 100)), - exit = fadeOut(animationSpec = tween(durationMillis = 50)), - modifier = modifier.align(Alignment.Center).size(40.dp).padding(10.dp), - ) { - Icon(icon) - } -} diff --git a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/compose/VolumeDialogSliderTrack.kt b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/compose/VolumeDialogSliderTrack.kt index 1dd9ddac79be..fb8de45bfad1 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/compose/VolumeDialogSliderTrack.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/dialog/sliders/ui/compose/VolumeDialogSliderTrack.kt @@ -19,6 +19,7 @@ package com.android.systemui.volume.dialog.sliders.ui.compose import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.BoxScope import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.width import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @@ -32,38 +33,47 @@ import androidx.compose.runtime.MutableState import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.Layout import androidx.compose.ui.layout.Measurable import androidx.compose.ui.layout.MeasurePolicy import androidx.compose.ui.layout.MeasureResult import androidx.compose.ui.layout.MeasureScope -import androidx.compose.ui.layout.Placeable import androidx.compose.ui.layout.layoutId +import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.unit.Constraints import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.LayoutDirection import androidx.compose.ui.unit.dp -import androidx.compose.ui.util.fastFilter import androidx.compose.ui.util.fastFirst import kotlin.math.min @Composable @OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class) -fun VolumeDialogSliderTrack( +fun SliderTrack( sliderState: SliderState, - colors: SliderColors, isEnabled: Boolean, modifier: Modifier = Modifier, + colors: SliderColors = SliderDefaults.colors(), thumbTrackGapSize: Dp = 6.dp, trackCornerSize: Dp = 12.dp, trackInsideCornerSize: Dp = 2.dp, trackSize: Dp = 40.dp, + isVertical: Boolean = false, activeTrackStartIcon: (@Composable BoxScope.(iconsState: SliderIconsState) -> Unit)? = null, activeTrackEndIcon: (@Composable BoxScope.(iconsState: SliderIconsState) -> Unit)? = null, inactiveTrackStartIcon: (@Composable BoxScope.(iconsState: SliderIconsState) -> Unit)? = null, inactiveTrackEndIcon: (@Composable BoxScope.(iconsState: SliderIconsState) -> Unit)? = null, ) { - val measurePolicy = remember(sliderState) { TrackMeasurePolicy(sliderState) } + val isRtl = LocalLayoutDirection.current == LayoutDirection.Rtl + val measurePolicy = + remember(sliderState) { + TrackMeasurePolicy( + sliderState = sliderState, + shouldMirrorIcons = !isVertical && isRtl || isVertical, + isVertical = isVertical, + gapSize = thumbTrackGapSize, + ) + } Layout( measurePolicy = measurePolicy, content = { @@ -76,33 +86,41 @@ fun VolumeDialogSliderTrack( drawStopIndicator = null, thumbTrackGapSize = thumbTrackGapSize, drawTick = { _, _ -> }, - modifier = Modifier.width(trackSize).layoutId(Contents.Track), + modifier = + Modifier.then( + if (isVertical) { + Modifier.width(trackSize) + } else { + Modifier.height(trackSize) + } + ) + .layoutId(Contents.Track), ) TrackIcon( icon = activeTrackStartIcon, - contentsId = Contents.Active.TrackStartIcon, + contents = Contents.Active.TrackStartIcon, isEnabled = isEnabled, colors = colors, state = measurePolicy, ) TrackIcon( icon = activeTrackEndIcon, - contentsId = Contents.Active.TrackEndIcon, + contents = Contents.Active.TrackEndIcon, isEnabled = isEnabled, colors = colors, state = measurePolicy, ) TrackIcon( icon = inactiveTrackStartIcon, - contentsId = Contents.Inactive.TrackStartIcon, + contents = Contents.Inactive.TrackStartIcon, isEnabled = isEnabled, colors = colors, state = measurePolicy, ) TrackIcon( icon = inactiveTrackEndIcon, - contentsId = Contents.Inactive.TrackEndIcon, + contents = Contents.Inactive.TrackEndIcon, isEnabled = isEnabled, colors = colors, state = measurePolicy, @@ -116,24 +134,47 @@ fun VolumeDialogSliderTrack( private fun TrackIcon( icon: (@Composable BoxScope.(sliderIconsState: SliderIconsState) -> Unit)?, isEnabled: Boolean, - contentsId: Contents, + contents: Contents, state: SliderIconsState, colors: SliderColors, modifier: Modifier = Modifier, ) { icon ?: return - Box(modifier = modifier.layoutId(contentsId).fillMaxSize()) { - CompositionLocalProvider( - LocalContentColor provides contentsId.getColor(colors, isEnabled) - ) { - icon(state) + /* + ignore icons mirroring for the rtl layouts here because icons positioning is handled by the + TrackMeasurePolicy. It ensures that active icons are always above the active track and the + same for inactive + */ + val iconColor = + when (contents) { + is Contents.Inactive -> + if (isEnabled) { + colors.inactiveTickColor + } else { + colors.disabledInactiveTickColor + } + is Contents.Active -> + if (isEnabled) { + colors.activeTickColor + } else { + colors.disabledActiveTickColor + } + is Contents.Track -> { + error("$contents is unsupported by the TrackIcon") + } } + Box(modifier = modifier.layoutId(contents).fillMaxSize()) { + CompositionLocalProvider(LocalContentColor provides iconColor) { icon(state) } } } @OptIn(ExperimentalMaterial3Api::class) -private class TrackMeasurePolicy(private val sliderState: SliderState) : - MeasurePolicy, SliderIconsState { +private class TrackMeasurePolicy( + private val sliderState: SliderState, + private val shouldMirrorIcons: Boolean, + private val gapSize: Dp, + private val isVertical: Boolean, +) : MeasurePolicy, SliderIconsState { private val isVisible: Map<Contents, MutableState<Boolean>> = mutableMapOf( @@ -144,16 +185,16 @@ private class TrackMeasurePolicy(private val sliderState: SliderState) : ) override val isActiveTrackStartIconVisible: Boolean - get() = isVisible.getValue(Contents.Active.TrackStartIcon).value + get() = isVisible.getValue(Contents.Active.TrackStartIcon.resolve()).value override val isActiveTrackEndIconVisible: Boolean - get() = isVisible.getValue(Contents.Active.TrackEndIcon).value + get() = isVisible.getValue(Contents.Active.TrackEndIcon.resolve()).value override val isInactiveTrackStartIconVisible: Boolean - get() = isVisible.getValue(Contents.Inactive.TrackStartIcon).value + get() = isVisible.getValue(Contents.Inactive.TrackStartIcon.resolve()).value override val isInactiveTrackEndIconVisible: Boolean - get() = isVisible.getValue(Contents.Inactive.TrackEndIcon).value + get() = isVisible.getValue(Contents.Inactive.TrackEndIcon.resolve()).value override fun MeasureScope.measure( measurables: List<Measurable>, @@ -164,178 +205,196 @@ private class TrackMeasurePolicy(private val sliderState: SliderState) : val iconSize = min(track.width, track.height) val iconConstraints = constraints.copy(maxWidth = iconSize, maxHeight = iconSize) - val icons = - measurables - .fastFilter { it.layoutId != Contents.Track } - .associateBy( - keySelector = { it.layoutId as Contents }, - valueTransform = { it.measure(iconConstraints) }, - ) - - return layout(track.width, track.height) { - with(Contents.Track) { - performPlacing( - placeable = track, - width = track.width, - height = track.height, - sliderState = sliderState, - ) + val components = buildMap { + put(Contents.Track, track) + for (measurable in measurables) { + // don't measure track a second time + if (measurable.layoutId != Contents.Track) { + put( + (measurable.layoutId as Contents).resolve(), + measurable.measure(iconConstraints), + ) + } } + } - for (iconLayoutId in icons.keys) { - with(iconLayoutId) { - performPlacing( - placeable = icons.getValue(iconLayoutId), - width = track.width, - height = track.height, - sliderState = sliderState, + return layout(track.width, track.height) { + val gapSizePx = gapSize.roundToPx() + val coercedValueAsFraction = + if (shouldMirrorIcons) { + 1 - sliderState.coercedValueAsFraction + } else { + sliderState.coercedValueAsFraction + } + for (iconLayoutId in components.keys) { + val iconPlaceable = components.getValue(iconLayoutId) + if (isVertical) { + iconPlaceable.place( + 0, + iconLayoutId.calculatePosition( + placeableDimension = iconPlaceable.height, + containerDimension = track.height, + gapSize = gapSizePx, + coercedValueAsFraction = coercedValueAsFraction, + ), + ) + } else { + iconPlaceable.place( + iconLayoutId.calculatePosition( + placeableDimension = iconPlaceable.width, + containerDimension = track.width, + gapSize = gapSizePx, + coercedValueAsFraction = coercedValueAsFraction, + ), + 0, ) + } - isVisible.getValue(iconLayoutId).value = - isVisible( - placeable = icons.getValue(iconLayoutId), - width = track.width, - height = track.height, - sliderState = sliderState, + // isVisible is only relevant for the icons + if (iconLayoutId != Contents.Track) { + val isVisibleState = isVisible.getValue(iconLayoutId) + val newIsVisible = + iconLayoutId.isVisible( + placeableDimension = + if (isVertical) iconPlaceable.height else iconPlaceable.width, + containerDimension = if (isVertical) track.height else track.width, + gapSize = gapSizePx, + coercedValueAsFraction = coercedValueAsFraction, ) + if (isVisibleState.value != newIsVisible) { + isVisibleState.value = newIsVisible + } } } } } + + private fun Contents.resolve(): Contents { + return if (shouldMirrorIcons) { + mirrored + } else { + this + } + } } -@OptIn(ExperimentalMaterial3Api::class) private sealed interface Contents { data object Track : Contents { - override fun Placeable.PlacementScope.performPlacing( - placeable: Placeable, - width: Int, - height: Int, - sliderState: SliderState, - ) = placeable.place(x = 0, y = 0) + + override val mirrored: Contents + get() = error("unsupported for Track") + + override fun calculatePosition( + placeableDimension: Int, + containerDimension: Int, + gapSize: Int, + coercedValueAsFraction: Float, + ): Int = 0 override fun isVisible( - placeable: Placeable, - width: Int, - height: Int, - sliderState: SliderState, - ) = true - - override fun getColor(sliderColors: SliderColors, isEnabled: Boolean): Color = - error("Unsupported") + placeableDimension: Int, + containerDimension: Int, + gapSize: Int, + coercedValueAsFraction: Float, + ): Boolean = true } interface Active : Contents { - override fun getColor(sliderColors: SliderColors, isEnabled: Boolean): Color { - return if (isEnabled) { - sliderColors.activeTickColor - } else { - sliderColors.disabledActiveTickColor - } - } + + override fun isVisible( + placeableDimension: Int, + containerDimension: Int, + gapSize: Int, + coercedValueAsFraction: Float, + ): Boolean = + (containerDimension * coercedValueAsFraction - gapSize).toInt() > placeableDimension data object TrackStartIcon : Active { - override fun Placeable.PlacementScope.performPlacing( - placeable: Placeable, - width: Int, - height: Int, - sliderState: SliderState, - ) = - placeable.place( - x = 0, - y = (height * (1 - sliderState.coercedValueAsFraction)).toInt(), - ) - - override fun isVisible( - placeable: Placeable, - width: Int, - height: Int, - sliderState: SliderState, - ): Boolean = (height * (sliderState.coercedValueAsFraction)).toInt() > placeable.height + + override val mirrored: Contents + get() = Inactive.TrackEndIcon + + override fun calculatePosition( + placeableDimension: Int, + containerDimension: Int, + gapSize: Int, + coercedValueAsFraction: Float, + ): Int = 0 } data object TrackEndIcon : Active { - override fun Placeable.PlacementScope.performPlacing( - placeable: Placeable, - width: Int, - height: Int, - sliderState: SliderState, - ) = placeable.place(x = 0, y = (height - placeable.height)) - - override fun isVisible( - placeable: Placeable, - width: Int, - height: Int, - sliderState: SliderState, - ): Boolean = (height * (sliderState.coercedValueAsFraction)).toInt() > placeable.height + + override val mirrored: Contents + get() = Inactive.TrackStartIcon + + override fun calculatePosition( + placeableDimension: Int, + containerDimension: Int, + gapSize: Int, + coercedValueAsFraction: Float, + ): Int = + (containerDimension * coercedValueAsFraction - placeableDimension - gapSize).toInt() } } interface Inactive : Contents { - override fun getColor(sliderColors: SliderColors, isEnabled: Boolean): Color { - return if (isEnabled) { - sliderColors.inactiveTickColor - } else { - sliderColors.disabledInactiveTickColor - } - } + override fun isVisible( + placeableDimension: Int, + containerDimension: Int, + gapSize: Int, + coercedValueAsFraction: Float, + ): Boolean = + containerDimension - (containerDimension * coercedValueAsFraction + gapSize) > + placeableDimension data object TrackStartIcon : Inactive { - override fun Placeable.PlacementScope.performPlacing( - placeable: Placeable, - width: Int, - height: Int, - sliderState: SliderState, - ) { - placeable.place(x = 0, y = 0) - } - override fun isVisible( - placeable: Placeable, - width: Int, - height: Int, - sliderState: SliderState, - ): Boolean = - (height * (1 - sliderState.coercedValueAsFraction)).toInt() > placeable.height + override val mirrored: Contents + get() = Active.TrackEndIcon + + override fun calculatePosition( + placeableDimension: Int, + containerDimension: Int, + gapSize: Int, + coercedValueAsFraction: Float, + ): Int = (containerDimension * coercedValueAsFraction + gapSize).toInt() } data object TrackEndIcon : Inactive { - override fun Placeable.PlacementScope.performPlacing( - placeable: Placeable, - width: Int, - height: Int, - sliderState: SliderState, - ) { - placeable.place( - x = 0, - y = - (height * (1 - sliderState.coercedValueAsFraction)).toInt() - - placeable.height, - ) - } - override fun isVisible( - placeable: Placeable, - width: Int, - height: Int, - sliderState: SliderState, - ): Boolean = - (height * (1 - sliderState.coercedValueAsFraction)).toInt() > placeable.height + override val mirrored: Contents + get() = Active.TrackStartIcon + + override fun calculatePosition( + placeableDimension: Int, + containerDimension: Int, + gapSize: Int, + coercedValueAsFraction: Float, + ): Int = containerDimension - placeableDimension } } - fun Placeable.PlacementScope.performPlacing( - placeable: Placeable, - width: Int, - height: Int, - sliderState: SliderState, - ) - - fun isVisible(placeable: Placeable, width: Int, height: Int, sliderState: SliderState): Boolean - - fun getColor(sliderColors: SliderColors, isEnabled: Boolean): Color + fun calculatePosition( + placeableDimension: Int, + containerDimension: Int, + gapSize: Int, + coercedValueAsFraction: Float, + ): Int + + fun isVisible( + placeableDimension: Int, + containerDimension: Int, + gapSize: Int, + coercedValueAsFraction: Float, + ): Boolean + + /** + * [Contents] that is visually on the opposite side of the current one on the slider. This is + * handy when dealing with the rtl layouts + */ + val mirrored: Contents } /** Provides visibility state for each of the Slider's icons. */ diff --git a/packages/SystemUI/src/com/android/systemui/volume/ui/slider/Slider.kt b/packages/SystemUI/src/com/android/systemui/volume/ui/compose/slider/Slider.kt index 502b311f7b40..54d2f79509c3 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/ui/slider/Slider.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/ui/compose/slider/Slider.kt @@ -16,7 +16,7 @@ @file:OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class) -package com.android.systemui.volume.ui.slider +package com.android.systemui.volume.ui.compose.slider import androidx.compose.animation.core.Animatable import androidx.compose.animation.core.Spring @@ -61,8 +61,6 @@ import kotlinx.coroutines.launch private val defaultSpring = SpringSpec<Float>(dampingRatio = Spring.DampingRatioNoBouncy, stiffness = Spring.StiffnessHigh) -private val defaultTrack: @Composable (SliderState) -> Unit = - @Composable { SliderDefaults.Track(it) } @Composable fun Slider( @@ -79,7 +77,14 @@ fun Slider( haptics: Haptics = Haptics.Disabled, isVertical: Boolean = false, isReverseDirection: Boolean = false, - track: (@Composable (SliderState) -> Unit)? = null, + track: (@Composable (SliderState) -> Unit) = { SliderDefaults.Track(it) }, + thumb: (@Composable (SliderState, MutableInteractionSource) -> Unit) = { _, _ -> + SliderDefaults.Thumb( + interactionSource = interactionSource, + colors = colors, + enabled = isEnabled, + ) + }, ) { require(stepDistance >= 0) { "stepDistance must not be negative" } val coroutineScope = rememberCoroutineScope() @@ -139,7 +144,8 @@ fun Slider( reverseDirection = isReverseDirection, interactionSource = interactionSource, colors = colors, - track = track ?: defaultTrack, + track = track, + thumb = { thumb(it, interactionSource) }, modifier = modifier.clearAndSetSemantics(semantics), ) } else { @@ -148,7 +154,8 @@ fun Slider( enabled = isEnabled, interactionSource = interactionSource, colors = colors, - track = track ?: defaultTrack, + track = track, + thumb = { thumb(it, interactionSource) }, modifier = modifier.clearAndSetSemantics(semantics), ) } diff --git a/packages/SystemUI/src/com/android/systemui/volume/ui/compose/slider/SliderIcon.kt b/packages/SystemUI/src/com/android/systemui/volume/ui/compose/slider/SliderIcon.kt new file mode 100644 index 000000000000..fd8f47794fc0 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/volume/ui/compose/slider/SliderIcon.kt @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.volume.ui.compose.slider + +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.core.tween +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.BoxScope +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier + +@Composable +fun SliderIcon( + icon: @Composable BoxScope.() -> Unit, + isVisible: Boolean, + modifier: Modifier = Modifier, +) { + Box(contentAlignment = Alignment.Center, modifier = modifier.fillMaxSize()) { + AnimatedVisibility( + visible = isVisible, + enter = fadeIn(animationSpec = tween(delayMillis = 33, durationMillis = 100)), + exit = fadeOut(animationSpec = tween(durationMillis = 50)), + ) { + icon() + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/window/data/repository/NoopWindowRootViewBlurRepository.kt b/packages/SystemUI/src/com/android/systemui/window/data/repository/NoopWindowRootViewBlurRepository.kt index f1dd374d3e1d..1ae8bc9405dc 100644 --- a/packages/SystemUI/src/com/android/systemui/window/data/repository/NoopWindowRootViewBlurRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/window/data/repository/NoopWindowRootViewBlurRepository.kt @@ -16,13 +16,12 @@ package com.android.systemui.window.data.repository -import com.android.systemui.window.data.repository.WindowRootViewBlurRepository import javax.inject.Inject import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow class NoopWindowRootViewBlurRepository @Inject constructor() : WindowRootViewBlurRepository { - override val blurRadius: MutableStateFlow<Int> = MutableStateFlow(0) + override val blurRequestedByShade: MutableStateFlow<Int> = MutableStateFlow(0) override val isBlurOpaque: MutableStateFlow<Boolean> = MutableStateFlow(true) override val isBlurSupported: StateFlow<Boolean> = MutableStateFlow(false) override var blurAppliedListener: BlurAppliedListener? = null diff --git a/packages/SystemUI/src/com/android/systemui/window/data/repository/WindowRootViewBlurRepository.kt b/packages/SystemUI/src/com/android/systemui/window/data/repository/WindowRootViewBlurRepository.kt index f98efe1e3c33..9b934bc6c866 100644 --- a/packages/SystemUI/src/com/android/systemui/window/data/repository/WindowRootViewBlurRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/window/data/repository/WindowRootViewBlurRepository.kt @@ -39,7 +39,7 @@ typealias BlurAppliedListener = Consumer<Int> /** Repository that maintains state for the window blur effect. */ interface WindowRootViewBlurRepository { - val blurRadius: MutableStateFlow<Int> + val blurRequestedByShade: MutableStateFlow<Int> val isBlurOpaque: MutableStateFlow<Boolean> /** Is blur supported based on settings toggle and battery power saver mode. */ @@ -67,7 +67,7 @@ constructor( @Main private val executor: Executor, @Application private val scope: CoroutineScope, ) : WindowRootViewBlurRepository { - override val blurRadius = MutableStateFlow(0) + override val blurRequestedByShade = MutableStateFlow(0) override val isBlurOpaque = MutableStateFlow(false) diff --git a/packages/SystemUI/src/com/android/systemui/window/domain/interactor/WindowRootViewBlurInteractor.kt b/packages/SystemUI/src/com/android/systemui/window/domain/interactor/WindowRootViewBlurInteractor.kt index 5197242e8079..2994138f8181 100644 --- a/packages/SystemUI/src/com/android/systemui/window/domain/interactor/WindowRootViewBlurInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/window/domain/interactor/WindowRootViewBlurInteractor.kt @@ -87,7 +87,7 @@ constructor( val isBlurCurrentlySupported: StateFlow<Boolean> = repository.isBlurSupported /** Radius of blur to be applied on the window root view. */ - val blurRadius: StateFlow<Int> = repository.blurRadius.asStateFlow() + val blurRadiusRequestedByShade: StateFlow<Int> = repository.blurRequestedByShade.asStateFlow() /** Whether the blur applied is opaque or transparent. */ val isBlurOpaque: Flow<Boolean> = @@ -128,7 +128,7 @@ constructor( return false } Log.d(TAG, "requestingBlurForShade for $blurRadius $opaque") - repository.blurRadius.value = blurRadius + repository.blurRequestedByShade.value = blurRadius repository.isBlurOpaque.value = opaque return true } diff --git a/packages/SystemUI/src/com/android/systemui/window/ui/WindowRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/window/ui/WindowRootViewBinder.kt index 06532bc0cc2a..fd642f99ef54 100644 --- a/packages/SystemUI/src/com/android/systemui/window/ui/WindowRootViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/window/ui/WindowRootViewBinder.kt @@ -59,6 +59,15 @@ object WindowRootViewBinder { ) { viewModel -> try { Log.d(TAG, "Launching coroutines that update window root view state") + launchTraced("early-wakeup") { + viewModel.isPersistentEarlyWakeupRequired.collect { wakeupRequired -> + blurUtils.setPersistentEarlyWakeup( + wakeupRequired, + view.rootView?.viewRootImpl, + ) + } + } + launchTraced("WindowBlur") { var wasUpdateScheduledForThisFrame = false var lastScheduledBlurRadius = 0 diff --git a/packages/SystemUI/src/com/android/systemui/window/ui/viewmodel/WindowRootViewModel.kt b/packages/SystemUI/src/com/android/systemui/window/ui/viewmodel/WindowRootViewModel.kt index e60e85335f7a..89101c961031 100644 --- a/packages/SystemUI/src/com/android/systemui/window/ui/viewmodel/WindowRootViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/window/ui/viewmodel/WindowRootViewModel.kt @@ -19,13 +19,16 @@ package com.android.systemui.window.ui.viewmodel import android.os.Build import android.util.Log import com.android.systemui.Flags +import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor import com.android.systemui.keyguard.ui.transitions.GlanceableHubTransition import com.android.systemui.keyguard.ui.transitions.PrimaryBouncerTransition +import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.window.domain.interactor.WindowRootViewBlurInteractor import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf @@ -41,6 +44,8 @@ constructor( primaryBouncerTransitions: Set<@JvmSuppressWildcards PrimaryBouncerTransition>, glanceableHubTransitions: Set<@JvmSuppressWildcards GlanceableHubTransition>, private val blurInteractor: WindowRootViewBlurInteractor, + private val keyguardInteractor: KeyguardInteractor, + private val shadeInteractor: ShadeInteractor, ) { private val bouncerBlurRadiusFlows = @@ -57,7 +62,9 @@ constructor( listOf( *bouncerBlurRadiusFlows.toTypedArray(), *glanceableHubBlurRadiusFlows.toTypedArray(), - blurInteractor.blurRadius.map { it.toFloat() }.logIfPossible("ShadeBlur"), + blurInteractor.blurRadiusRequestedByShade + .map { it.toFloat() } + .logIfPossible("ShadeBlur"), ) .merge() @@ -70,6 +77,24 @@ constructor( } } + val isPersistentEarlyWakeupRequired = + blurInteractor.isBlurCurrentlySupported + .flatMapLatest { blurSupported -> + if (blurSupported) { + combine( + keyguardInteractor.isKeyguardShowing, + shadeInteractor.isUserInteracting, + shadeInteractor.isAnyExpanded, + ) { keyguardShowing, userDraggingShade, anyExpanded -> + keyguardShowing || userDraggingShade || anyExpanded + } + } else { + flowOf(false) + } + } + .distinctUntilChanged() + .logIfPossible("isPersistentEarlyWakeupRequired") + val isBlurOpaque = blurInteractor.isBlurCurrentlySupported.flatMapLatest { blurSupported -> if (blurSupported) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityTransitionAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityTransitionAnimatorTest.kt index 60345a358bac..6cc8238f2d09 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityTransitionAnimatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/animation/ActivityTransitionAnimatorTest.kt @@ -249,10 +249,12 @@ class ActivityTransitionAnimatorTest : SysuiTestCase() { var factory = controllerFactory(controller) underTest.register(factory.cookie, factory, testScope) assertEquals(2, testShellTransitions.remotes.size) + assertTrue(testShellTransitions.remotesForTakeover.isEmpty()) factory = controllerFactory(controller) underTest.register(factory.cookie, factory, testScope) assertEquals(4, testShellTransitions.remotes.size) + assertTrue(testShellTransitions.remotesForTakeover.isEmpty()) } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java index 8105ae0960ad..9b314f25e02b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java @@ -101,7 +101,6 @@ import com.android.systemui.dreams.ui.viewmodel.DreamViewModel; import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FakeFeatureFlags; import com.android.systemui.flags.SystemPropertiesHelper; -import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor; import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionBootInteractor; import com.android.systemui.kosmos.KosmosJavaAdapter; import com.android.systemui.log.SessionTracker; @@ -207,7 +206,6 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { private @Mock ShadeWindowLogger mShadeWindowLogger; private @Mock SelectedUserInteractor mSelectedUserInteractor; private @Mock UserTracker.Callback mUserTrackerCallback; - private @Mock KeyguardInteractor mKeyguardInteractor; private @Mock KeyguardTransitionBootInteractor mKeyguardTransitionBootInteractor; private @Captor ArgumentCaptor<KeyguardStateController.Callback> mKeyguardStateControllerCallback; @@ -1499,9 +1497,10 @@ public class KeyguardViewMediatorTest extends SysuiTestCase { mSystemPropertiesHelper, () -> mock(WindowManagerLockscreenVisibilityManager.class), mSelectedUserInteractor, - mKeyguardInteractor, + mKosmos.getKeyguardInteractor(), mKeyguardTransitionBootInteractor, mKosmos::getCommunalSceneInteractor, + mKosmos::getCommunalSettingsInteractor, mock(WindowManagerOcclusionManager.class)); mViewMediator.mUserChangedCallback = mUserTrackerCallback; mViewMediator.start(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTestKt.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTestKt.kt index 86f7966d4ada..d6b778fe2bc2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTestKt.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTestKt.kt @@ -35,8 +35,14 @@ import com.android.systemui.SysuiTestCase import com.android.systemui.animation.activityTransitionAnimator import com.android.systemui.broadcast.broadcastDispatcher import com.android.systemui.classifier.falsingCollector +import com.android.systemui.common.data.repository.batteryRepository +import com.android.systemui.common.data.repository.fake +import com.android.systemui.communal.data.model.FEATURE_AUTO_OPEN +import com.android.systemui.communal.data.model.SuppressionReason import com.android.systemui.communal.data.repository.communalSceneRepository import com.android.systemui.communal.domain.interactor.communalSceneInteractor +import com.android.systemui.communal.domain.interactor.communalSettingsInteractor +import com.android.systemui.communal.domain.interactor.setCommunalV2Enabled import com.android.systemui.communal.shared.model.CommunalScenes import com.android.systemui.communal.ui.viewmodel.communalTransitionViewModel import com.android.systemui.concurrency.fakeExecutor @@ -81,8 +87,11 @@ import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentMatchers.any import org.mockito.ArgumentMatchers.anyInt +import org.mockito.kotlin.anyOrNull import org.mockito.kotlin.doReturn +import org.mockito.kotlin.eq import org.mockito.kotlin.mock +import org.mockito.kotlin.verify /** Kotlin version of KeyguardViewMediatorTest to allow for coroutine testing. */ @SmallTest @@ -152,6 +161,7 @@ class KeyguardViewMediatorTestKt : SysuiTestCase() { keyguardInteractor, keyguardTransitionBootInteractor, { communalSceneInteractor }, + { communalSettingsInteractor }, mock<WindowManagerOcclusionManager>(), ) } @@ -164,6 +174,10 @@ class KeyguardViewMediatorTestKt : SysuiTestCase() { @Test fun doKeyguardTimeout_changesCommunalScene() = kosmos.runTest { + // Hub is enabled and hub condition is active. + setCommunalV2Enabled(true) + enableHubOnCharging() + // doKeyguardTimeout message received. val timeoutOptions = Bundle() timeoutOptions.putBoolean(KeyguardViewMediator.EXTRA_TRIGGER_HUB, true) @@ -174,4 +188,56 @@ class KeyguardViewMediatorTestKt : SysuiTestCase() { assertThat(communalSceneRepository.currentScene.value) .isEqualTo(CommunalScenes.Communal) } + + @Test + fun doKeyguardTimeout_communalNotAvailable_sleeps() = + kosmos.runTest { + // Hub disabled. + setCommunalV2Enabled(false) + + // doKeyguardTimeout message received. + val timeoutOptions = Bundle() + timeoutOptions.putBoolean(KeyguardViewMediator.EXTRA_TRIGGER_HUB, true) + underTest.doKeyguardTimeout(timeoutOptions) + testableLooper.processAllMessages() + + // Sleep is requested. + verify(powerManager) + .goToSleep(anyOrNull(), eq(PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON), eq(0)) + + // Hub scene is not changed. + assertThat(communalSceneRepository.currentScene.value).isEqualTo(CommunalScenes.Blank) + } + + @Test + fun doKeyguardTimeout_hubConditionNotActive_sleeps() = + kosmos.runTest { + // Communal enabled, but hub condition set to never. + setCommunalV2Enabled(true) + disableHubShowingAutomatically() + + // doKeyguardTimeout message received. + val timeoutOptions = Bundle() + timeoutOptions.putBoolean(KeyguardViewMediator.EXTRA_TRIGGER_HUB, true) + underTest.doKeyguardTimeout(timeoutOptions) + testableLooper.processAllMessages() + + // Sleep is requested. + verify(powerManager) + .goToSleep(anyOrNull(), eq(PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON), eq(0)) + + // Hub scene is not changed. + assertThat(communalSceneRepository.currentScene.value).isEqualTo(CommunalScenes.Blank) + } + + private fun Kosmos.enableHubOnCharging() { + communalSettingsInteractor.setSuppressionReasons(emptyList()) + batteryRepository.fake.setDevicePluggedIn(true) + } + + private fun Kosmos.disableHubShowingAutomatically() { + communalSettingsInteractor.setSuppressionReasons( + listOf(SuppressionReason.ReasonUnknown(FEATURE_AUTO_OPEN)) + ) + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt index df24bff43c08..78a4fbecabe8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/sections/DefaultDeviceEntrySectionTest.kt @@ -31,11 +31,13 @@ import com.android.systemui.flags.Flags import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryBackgroundViewModel import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryForegroundViewModel import com.android.systemui.keyguard.ui.viewmodel.DeviceEntryIconViewModel +import com.android.systemui.kosmos.testDispatcher import com.android.systemui.log.logcatLogBuffer import com.android.systemui.plugins.FalsingManager import com.android.systemui.res.R import com.android.systemui.shade.NotificationPanelView import com.android.systemui.statusbar.VibratorHelper +import com.android.systemui.testKosmos import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.flow.MutableStateFlow @@ -68,6 +70,7 @@ class DefaultDeviceEntrySectionTest : SysuiTestCase() { underTest = DefaultDeviceEntrySection( TestScope().backgroundScope, + testKosmos().testDispatcher, authController, windowManager, context, diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt index 8a4f1ad13b78..5596cc7ee7da 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt @@ -38,8 +38,6 @@ import com.android.systemui.notetask.NoteTaskEntryPoint.TAIL_BUTTON import com.android.systemui.settings.FakeUserTracker import com.android.systemui.statusbar.CommandQueue import com.android.systemui.util.concurrency.FakeExecutor -import com.android.systemui.util.mockito.any -import com.android.systemui.util.mockito.eq import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever import com.android.systemui.util.mockito.withArgCaptor @@ -52,11 +50,14 @@ import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock +import org.mockito.Mockito.anyList import org.mockito.Mockito.never import org.mockito.Mockito.times import org.mockito.Mockito.verify import org.mockito.Mockito.verifyNoMoreInteractions import org.mockito.MockitoAnnotations.initMocks +import org.mockito.kotlin.any +import org.mockito.kotlin.eq /** atest SystemUITests:NoteTaskInitializerTest */ @OptIn(InternalNoteTaskApi::class) @@ -180,6 +181,18 @@ internal class NoteTaskInitializerTest : SysuiTestCase() { @Test @EnableFlags(com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER) + fun initialize_keyGestureTypeOpenNotes_isRegistered() { + val underTest = createUnderTest(isEnabled = true, bubbles = bubbles) + underTest.initialize() + verify(inputManager) + .registerKeyGestureEventHandler( + eq(listOf(KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_NOTES)), + any(), + ) + } + + @Test + @EnableFlags(com.android.hardware.input.Flags.FLAG_USE_KEY_GESTURE_EVENT_HANDLER) fun handlesShortcut_keyGestureTypeOpenNotes() { val gestureEvent = KeyGestureEvent.Builder() @@ -189,12 +202,12 @@ internal class NoteTaskInitializerTest : SysuiTestCase() { val underTest = createUnderTest(isEnabled = true, bubbles = bubbles) underTest.initialize() val callback = withArgCaptor { - verify(inputManager).registerKeyGestureEventHandler(capture()) + verify(inputManager).registerKeyGestureEventHandler(anyList(), capture()) } - assertThat(callback.handleKeyGestureEvent(gestureEvent, null)).isTrue() - + callback.handleKeyGestureEvent(gestureEvent, null) executor.runAllReady() + verify(controller).showNoteTask(eq(KEYBOARD_SHORTCUT)) } @@ -203,19 +216,19 @@ internal class NoteTaskInitializerTest : SysuiTestCase() { fun handlesShortcut_stylusTailButton() { val gestureEvent = KeyGestureEvent.Builder() - .setKeycodes(intArrayOf(KeyEvent.KEYCODE_STYLUS_BUTTON_TAIL)) + .setKeycodes(intArrayOf(KEYCODE_STYLUS_BUTTON_TAIL)) .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_NOTES) .setAction(KeyGestureEvent.ACTION_GESTURE_COMPLETE) .build() val underTest = createUnderTest(isEnabled = true, bubbles = bubbles) underTest.initialize() val callback = withArgCaptor { - verify(inputManager).registerKeyGestureEventHandler(capture()) + verify(inputManager).registerKeyGestureEventHandler(anyList(), capture()) } - assertThat(callback.handleKeyGestureEvent(gestureEvent, null)).isTrue() - + callback.handleKeyGestureEvent(gestureEvent, null) executor.runAllReady() + verify(controller).showNoteTask(eq(TAIL_BUTTON)) } @@ -224,19 +237,19 @@ internal class NoteTaskInitializerTest : SysuiTestCase() { fun ignoresUnrelatedShortcuts() { val gestureEvent = KeyGestureEvent.Builder() - .setKeycodes(intArrayOf(KeyEvent.KEYCODE_STYLUS_BUTTON_TAIL)) + .setKeycodes(intArrayOf(KEYCODE_STYLUS_BUTTON_TAIL)) .setKeyGestureType(KeyGestureEvent.KEY_GESTURE_TYPE_HOME) .setAction(KeyGestureEvent.ACTION_GESTURE_COMPLETE) .build() val underTest = createUnderTest(isEnabled = true, bubbles = bubbles) underTest.initialize() val callback = withArgCaptor { - verify(inputManager).registerKeyGestureEventHandler(capture()) + verify(inputManager).registerKeyGestureEventHandler(anyList(), capture()) } - assertThat(callback.handleKeyGestureEvent(gestureEvent, null)).isFalse() - + callback.handleKeyGestureEvent(gestureEvent, null) executor.runAllReady() + verify(controller, never()).showNoteTask(any()) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/DragAndDropTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/DragAndDropTest.kt index 7e42ec7e83b1..1551375f6879 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/DragAndDropTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/DragAndDropTest.kt @@ -37,6 +37,7 @@ import com.android.systemui.common.shared.model.Icon import com.android.systemui.qs.panels.shared.model.SizedTile import com.android.systemui.qs.panels.shared.model.SizedTileImpl import com.android.systemui.qs.panels.ui.compose.infinitegrid.DefaultEditTileGrid +import com.android.systemui.qs.panels.ui.viewmodel.AvailableEditActions import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModel import com.android.systemui.qs.pipeline.shared.TileSpec import com.android.systemui.qs.shared.model.TileCategory @@ -63,7 +64,7 @@ class DragAndDropTest : SysuiTestCase() { columns = 4, largeTilesSpan = 4, modifier = Modifier.fillMaxSize(), - onAddTile = {}, + onAddTile = { _, _ -> }, onRemoveTile = {}, onSetTiles = onSetTiles, onResize = { _, _ -> }, @@ -84,7 +85,7 @@ class DragAndDropTest : SysuiTestCase() { } composeRule.waitForIdle() - listState.onStarted(TestEditTiles[0], DragType.Add) + listState.onStarted(TestEditTiles[0], DragType.Move) // Tile is being dragged, it should be replaced with a placeholder composeRule.onNodeWithContentDescription("tileA").assertDoesNotExist() @@ -103,6 +104,45 @@ class DragAndDropTest : SysuiTestCase() { } @Test + fun nonRemovableDraggedTile_removeHeaderShouldNotExist() { + val nonRemovableTile = createEditTile("tileA", isRemovable = false) + val listState = EditTileListState(listOf(nonRemovableTile), columns = 4, largeTilesSpan = 2) + composeRule.setContent { EditTileGridUnderTest(listState) {} } + composeRule.waitForIdle() + + listState.onStarted(nonRemovableTile, DragType.Move) + + // Tile is being dragged, it should be replaced with a placeholder + composeRule.onNodeWithContentDescription("tileA").assertDoesNotExist() + + // Remove drop zone should not appear + composeRule.onNodeWithText("Remove").assertDoesNotExist() + } + + @Test + fun droppedNonRemovableDraggedTile_shouldStayInGrid() { + val nonRemovableTile = createEditTile("tileA", isRemovable = false) + val listState = EditTileListState(listOf(nonRemovableTile), columns = 4, largeTilesSpan = 2) + composeRule.setContent { EditTileGridUnderTest(listState) {} } + composeRule.waitForIdle() + + listState.onStarted(nonRemovableTile, DragType.Move) + + // Tile is being dragged, it should be replaced with a placeholder + composeRule.onNodeWithContentDescription("tileA").assertDoesNotExist() + + // Remove drop zone should not appear + composeRule.onNodeWithText("Remove").assertDoesNotExist() + + // Drop tile outside of the grid + listState.movedOutOfBounds() + listState.onDrop() + + // Tile A is still in the grid + composeRule.assertGridContainsExactly(CURRENT_TILES_GRID_TEST_TAG, listOf("tileA")) + } + + @Test fun draggedTile_shouldChangePosition() { var tiles by mutableStateOf(TestEditTiles) val listState = EditTileListState(tiles, columns = 4, largeTilesSpan = 2) @@ -113,7 +153,11 @@ class DragAndDropTest : SysuiTestCase() { } composeRule.waitForIdle() - listState.onStarted(TestEditTiles[0], DragType.Add) + listState.onStarted(TestEditTiles[0], DragType.Move) + + // Remove drop zone should appear + composeRule.onNodeWithText("Remove").assertExists() + listState.onTargeting(1, false) listState.onDrop() @@ -141,7 +185,11 @@ class DragAndDropTest : SysuiTestCase() { } composeRule.waitForIdle() - listState.onStarted(TestEditTiles[0], DragType.Add) + listState.onStarted(TestEditTiles[0], DragType.Move) + + // Remove drop zone should appear + composeRule.onNodeWithText("Remove").assertExists() + listState.movedOutOfBounds() listState.onDrop() @@ -169,7 +217,11 @@ class DragAndDropTest : SysuiTestCase() { } composeRule.waitForIdle() - listState.onStarted(createEditTile("tile_new"), DragType.Add) + listState.onStarted(createEditTile("tile_new", isRemovable = false), DragType.Add) + + // Remove drop zone should appear + composeRule.onNodeWithText("Remove").assertExists() + // Insert after tileD, which is at index 4 // [ a ] [ b ] [ c ] [ empty ] // [ tile d ] [ e ] @@ -193,7 +245,10 @@ class DragAndDropTest : SysuiTestCase() { private const val CURRENT_TILES_GRID_TEST_TAG = "CurrentTilesGrid" private const val AVAILABLE_TILES_GRID_TEST_TAG = "AvailableTilesGrid" - private fun createEditTile(tileSpec: String): SizedTile<EditTileViewModel> { + private fun createEditTile( + tileSpec: String, + isRemovable: Boolean = true, + ): SizedTile<EditTileViewModel> { return SizedTileImpl( EditTileViewModel( tileSpec = TileSpec.create(tileSpec), @@ -205,7 +260,8 @@ class DragAndDropTest : SysuiTestCase() { label = AnnotatedString(tileSpec), appName = null, isCurrent = true, - availableEditActions = emptySet(), + availableEditActions = + if (isRemovable) setOf(AvailableEditActions.REMOVE) else emptySet(), category = TileCategory.UNKNOWN, ), getWidth(tileSpec), diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/EditModeTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/EditModeTest.kt index 9d4a425c678b..acb441c3765d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/EditModeTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/EditModeTest.kt @@ -23,6 +23,7 @@ import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier +import androidx.compose.ui.test.doubleClick import androidx.compose.ui.test.junit4.ComposeContentTestRule import androidx.compose.ui.test.junit4.createComposeRule import androidx.compose.ui.test.onAllNodesWithText @@ -30,6 +31,7 @@ import androidx.compose.ui.test.onFirst import androidx.compose.ui.test.onNodeWithContentDescription import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick +import androidx.compose.ui.test.performTouchInput import androidx.compose.ui.text.AnnotatedString import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest @@ -40,6 +42,7 @@ import com.android.systemui.common.shared.model.Icon import com.android.systemui.qs.panels.shared.model.SizedTile import com.android.systemui.qs.panels.shared.model.SizedTileImpl import com.android.systemui.qs.panels.ui.compose.infinitegrid.DefaultEditTileGrid +import com.android.systemui.qs.panels.ui.viewmodel.AvailableEditActions import com.android.systemui.qs.panels.ui.viewmodel.EditTileViewModel import com.android.systemui.qs.pipeline.shared.TileSpec import com.android.systemui.qs.shared.model.TileCategory @@ -53,8 +56,8 @@ class EditModeTest : SysuiTestCase() { @get:Rule val composeRule = createComposeRule() @Composable - private fun EditTileGridUnderTest() { - var tiles by remember { mutableStateOf(TestEditTiles) } + private fun EditTileGridUnderTest(sizedTiles: List<SizedTile<EditTileViewModel>>) { + var tiles by remember { mutableStateOf(sizedTiles) } val (currentTiles, otherTiles) = tiles.partition { it.tile.isCurrent } val listState = EditTileListState(currentTiles, columns = 4, largeTilesSpan = 2) @@ -65,7 +68,7 @@ class EditModeTest : SysuiTestCase() { columns = 4, largeTilesSpan = 4, modifier = Modifier.fillMaxSize(), - onAddTile = { tiles = tiles.add(it) }, + onAddTile = { spec, _ -> tiles = tiles.add(spec) }, onRemoveTile = { tiles = tiles.remove(it) }, onSetTiles = {}, onResize = { _, _ -> }, @@ -77,7 +80,7 @@ class EditModeTest : SysuiTestCase() { @Test fun clickAvailableTile_shouldAdd() { - composeRule.setContent { EditTileGridUnderTest() } + composeRule.setContent { EditTileGridUnderTest(TestEditTiles) } composeRule.waitForIdle() composeRule.onNodeWithContentDescription("tileF").performClick() // Tap to add @@ -93,7 +96,7 @@ class EditModeTest : SysuiTestCase() { @Test fun clickRemoveTarget_shouldRemoveSelection() { - composeRule.setContent { EditTileGridUnderTest() } + composeRule.setContent { EditTileGridUnderTest(TestEditTiles) } composeRule.waitForIdle() // Selects first "tileA", i.e. the one in the current grid @@ -110,6 +113,36 @@ class EditModeTest : SysuiTestCase() { ) } + @Test + fun selectNonRemovableTile_removeTargetShouldHide() { + val nonRemovableTile = createEditTile("tileA", isRemovable = false) + composeRule.setContent { EditTileGridUnderTest(listOf(nonRemovableTile)) } + composeRule.waitForIdle() + + // Selects first "tileA", i.e. the one in the current grid + composeRule.onAllNodesWithText("tileA").onFirst().performClick() + + // Assert the remove target isn't shown + composeRule.onNodeWithText("Remove").assertDoesNotExist() + } + + @Test + fun placementMode_shouldRepositionTile() { + composeRule.setContent { EditTileGridUnderTest(TestEditTiles) } + composeRule.waitForIdle() + + // Double tap first "tileA", i.e. the one in the current grid + composeRule.onAllNodesWithText("tileA").onFirst().performTouchInput { doubleClick() } + + // Tap on tileE to position tileA in its spot + composeRule.onAllNodesWithText("tileE").onFirst().performClick() + + // Assert tileA moved to tileE's position + composeRule.assertCurrentTilesGridContainsExactly( + listOf("tileB", "tileC", "tileD_large", "tileE", "tileA") + ) + } + private fun ComposeContentTestRule.assertCurrentTilesGridContainsExactly(specs: List<String>) = assertGridContainsExactly(CURRENT_TILES_GRID_TEST_TAG, specs) @@ -148,6 +181,7 @@ class EditModeTest : SysuiTestCase() { private fun createEditTile( tileSpec: String, isCurrent: Boolean = true, + isRemovable: Boolean = true, ): SizedTile<EditTileViewModel> { return SizedTileImpl( EditTileViewModel( @@ -160,7 +194,8 @@ class EditModeTest : SysuiTestCase() { label = AnnotatedString(tileSpec), appName = null, isCurrent = isCurrent, - availableEditActions = emptySet(), + availableEditActions = + if (isRemovable) setOf(AvailableEditActions.REMOVE) else emptySet(), category = TileCategory.UNKNOWN, ), getWidth(tileSpec), diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/ResizingTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/ResizingTest.kt index 5e76000cc7f0..274c44cef949 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/ResizingTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/panels/ui/compose/ResizingTest.kt @@ -69,7 +69,7 @@ class ResizingTest : SysuiTestCase() { columns = 4, largeTilesSpan = 4, modifier = Modifier.fillMaxSize(), - onAddTile = {}, + onAddTile = { _, _ -> }, onRemoveTile = {}, onSetTiles = {}, onResize = onResize, diff --git a/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayCoreStartableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayCoreStartableTest.kt index 997cf417fe10..f4d0c26f12ee 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayCoreStartableTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayCoreStartableTest.kt @@ -18,9 +18,11 @@ package com.android.systemui.reardisplay import android.hardware.devicestate.feature.flags.Flags.FLAG_DEVICE_STATE_RDM_V2 import android.hardware.display.rearDisplay +import android.os.fakeExecutorHandler import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags import android.view.Display +import android.view.accessibility.accessibilityManager import androidx.test.filters.SmallTest import com.android.keyguard.keyguardUpdateMonitor import com.android.systemui.SysuiTestCase @@ -62,6 +64,8 @@ class RearDisplayCoreStartableTest : SysuiTestCase() { kosmos.rearDisplayInnerDialogDelegateFactory, kosmos.testScope, kosmos.keyguardUpdateMonitor, + kosmos.accessibilityManager, + kosmos.fakeExecutorHandler, ) @Before @@ -69,7 +73,7 @@ class RearDisplayCoreStartableTest : SysuiTestCase() { whenever(kosmos.rearDisplay.flags).thenReturn(Display.FLAG_REAR) whenever(kosmos.rearDisplay.displayAdjustments) .thenReturn(mContext.display.displayAdjustments) - whenever(kosmos.rearDisplayInnerDialogDelegateFactory.create(any(), any())) + whenever(kosmos.rearDisplayInnerDialogDelegateFactory.create(any(), any(), any())) .thenReturn(mockDelegate) whenever(mockDelegate.createDialog()).thenReturn(mockDialog) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayInnerDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayInnerDialogDelegateTest.kt index fc7661666825..477e5babdcc3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayInnerDialogDelegateTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/reardisplay/RearDisplayInnerDialogDelegateTest.kt @@ -17,7 +17,10 @@ package com.android.systemui.reardisplay import android.testing.TestableLooper +import android.view.View +import android.widget.Button import android.widget.SeekBar +import android.widget.TextView import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.haptics.msdl.msdlPlayer @@ -28,6 +31,7 @@ import com.android.systemui.res.R import com.android.systemui.statusbar.phone.systemUIDialogDotFactory import com.android.systemui.testKosmos import com.android.systemui.util.time.systemClock +import com.google.common.truth.Truth.assertThat import junit.framework.Assert.assertFalse import junit.framework.Assert.assertTrue import org.junit.Test @@ -49,6 +53,7 @@ class RearDisplayInnerDialogDelegateTest : SysuiTestCase() { RearDisplayInnerDialogDelegate( kosmos.systemUIDialogDotFactory, mContext, + false /* touchExplorationEnabled */, kosmos.vibratorHelper, kosmos.msdlPlayer, kosmos.systemClock, @@ -68,6 +73,7 @@ class RearDisplayInnerDialogDelegateTest : SysuiTestCase() { RearDisplayInnerDialogDelegate( kosmos.systemUIDialogDotFactory, mContext, + false /* touchExplorationEnabled */, kosmos.vibratorHelper, kosmos.msdlPlayer, kosmos.systemClock, @@ -78,6 +84,9 @@ class RearDisplayInnerDialogDelegateTest : SysuiTestCase() { .apply { show() val seekbar = findViewById<SeekBar>(R.id.seekbar) + assertThat(seekbar.visibility).isEqualTo(View.VISIBLE) + assertThat(findViewById<TextView>(R.id.seekbar_instructions).visibility) + .isEqualTo(View.VISIBLE) seekbar.progress = 50 seekbar.progress = 100 verify(mockCallback).run() @@ -90,6 +99,7 @@ class RearDisplayInnerDialogDelegateTest : SysuiTestCase() { RearDisplayInnerDialogDelegate( kosmos.systemUIDialogDotFactory, mContext, + false /* touchExplorationEnabled */, kosmos.vibratorHelper, kosmos.msdlPlayer, kosmos.systemClock, @@ -118,4 +128,33 @@ class RearDisplayInnerDialogDelegateTest : SysuiTestCase() { // Progress is reset verify(mockSeekbar).setProgress(eq(0)) } + + @Test + fun testTouchExplorationEnabled() { + val mockCallback = mock<Runnable>() + + RearDisplayInnerDialogDelegate( + kosmos.systemUIDialogDotFactory, + mContext, + true /* touchExplorationEnabled */, + kosmos.vibratorHelper, + kosmos.msdlPlayer, + kosmos.systemClock, + ) { + mockCallback.run() + } + .createDialog() + .apply { + show() + assertThat(findViewById<SeekBar>(R.id.seekbar).visibility).isEqualTo(View.GONE) + assertThat(findViewById<TextView>(R.id.seekbar_instructions).visibility) + .isEqualTo(View.GONE) + + val cancelButton = findViewById<Button>(R.id.cancel_button) + assertThat(cancelButton.visibility).isEqualTo(View.VISIBLE) + + cancelButton.performClick() + verify(mockCallback).run() + } + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java index 2ea4e7f67b3c..bc7ab9d4fe3c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java @@ -582,7 +582,7 @@ public class ExpandableNotificationRowTest extends SysuiTestCase { public void testIconScrollXAfterTranslationAndReset() throws Exception { ExpandableNotificationRow group = mNotificationTestHelper.createGroup(); - group.setDismissUsingRowTranslationX(false); + group.setDismissUsingRowTranslationX(false, false); group.setTranslation(50); assertEquals(50, -group.getEntry().getIcons().getShelfIcon().getScrollX()); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java index 0d99c0e8cab8..320a87e7db17 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java @@ -176,6 +176,7 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { private FakeKeyguardStateController mKeyguardStateController = spy(new FakeKeyguardStateController()); private final FakeExecutor mExecutor = new FakeExecutor(new FakeSystemClock()); + private final static String TEST_REASON = "reason"; @Mock private ViewRootImpl mViewRootImpl; @@ -272,14 +273,15 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { mStatusBarKeyguardViewManager.dismissWithAction( action, cancelAction, false /* afterKeyguardGone */); verify(mPrimaryBouncerInteractor).setDismissAction(eq(action), eq(cancelAction)); - verify(mPrimaryBouncerInteractor).show(eq(true)); + verify(mPrimaryBouncerInteractor).show(eq(true), + eq("StatusBarKeyguardViewManager#dismissWithAction")); } @Test public void showPrimaryBouncer_onlyWhenShowing() { mStatusBarKeyguardViewManager.hide(0 /* startTime */, 0 /* fadeoutDuration */); - mStatusBarKeyguardViewManager.showPrimaryBouncer(true /* scrimmed */); - verify(mPrimaryBouncerInteractor, never()).show(anyBoolean()); + mStatusBarKeyguardViewManager.showPrimaryBouncer(true /* scrimmed */, TEST_REASON); + verify(mPrimaryBouncerInteractor, never()).show(anyBoolean(), eq(TEST_REASON)); verify(mDeviceEntryInteractor, never()).attemptDeviceEntry(); verify(mSceneInteractor, never()).changeScene(any(), any()); } @@ -289,8 +291,8 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { mStatusBarKeyguardViewManager.hide(0 /* startTime */, 0 /* fadeoutDuration */); when(mKeyguardSecurityModel.getSecurityMode(anyInt())).thenReturn( KeyguardSecurityModel.SecurityMode.Password); - mStatusBarKeyguardViewManager.showPrimaryBouncer(true /* scrimmed */); - verify(mPrimaryBouncerInteractor, never()).show(anyBoolean()); + mStatusBarKeyguardViewManager.showPrimaryBouncer(true /* scrimmed */, TEST_REASON); + verify(mPrimaryBouncerInteractor, never()).show(anyBoolean(), eq(TEST_REASON)); verify(mDeviceEntryInteractor, never()).attemptDeviceEntry(); verify(mSceneInteractor, never()).changeScene(any(), any()); } @@ -298,8 +300,8 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { @Test @DisableSceneContainer public void showBouncer_showsTheBouncer() { - mStatusBarKeyguardViewManager.showPrimaryBouncer(true /* scrimmed */); - verify(mPrimaryBouncerInteractor).show(eq(true)); + mStatusBarKeyguardViewManager.showPrimaryBouncer(true /* scrimmed */, TEST_REASON); + verify(mPrimaryBouncerInteractor).show(eq(true), eq(TEST_REASON)); } @Test @@ -344,19 +346,20 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { public void onPanelExpansionChanged_showsBouncerWhenSwiping() { mKeyguardStateController.setCanDismissLockScreen(false); mStatusBarKeyguardViewManager.onPanelExpansionChanged(EXPANSION_EVENT); - verify(mPrimaryBouncerInteractor).show(eq(false)); + verify(mPrimaryBouncerInteractor).show(eq(false), + eq("StatusBarKeyguardViewManager#onPanelExpansionChanged")); // But not when it's already visible reset(mPrimaryBouncerInteractor); when(mPrimaryBouncerInteractor.isFullyShowing()).thenReturn(true); mStatusBarKeyguardViewManager.onPanelExpansionChanged(EXPANSION_EVENT); - verify(mPrimaryBouncerInteractor, never()).show(eq(false)); + verify(mPrimaryBouncerInteractor, never()).show(eq(false), eq(TEST_REASON)); // Or animating away reset(mPrimaryBouncerInteractor); when(mPrimaryBouncerInteractor.isAnimatingAway()).thenReturn(true); mStatusBarKeyguardViewManager.onPanelExpansionChanged(EXPANSION_EVENT); - verify(mPrimaryBouncerInteractor, never()).show(eq(false)); + verify(mPrimaryBouncerInteractor, never()).show(eq(false), eq(TEST_REASON)); } @Test @@ -546,7 +549,7 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(true); // WHEN showBouncer is called - mStatusBarKeyguardViewManager.showPrimaryBouncer(true); + mStatusBarKeyguardViewManager.showPrimaryBouncer(true, TEST_REASON); // THEN alt bouncer should be hidden verify(mAlternateBouncerInteractor).hide(); @@ -571,10 +574,10 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { // WHEN showGenericBouncer is called final boolean scrimmed = true; - mStatusBarKeyguardViewManager.showBouncer(scrimmed); + mStatusBarKeyguardViewManager.showBouncer(scrimmed, TEST_REASON); // THEN regular bouncer is shown - verify(mPrimaryBouncerInteractor).show(eq(scrimmed)); + verify(mPrimaryBouncerInteractor).show(eq(scrimmed), eq(TEST_REASON)); } @Test @@ -835,7 +838,7 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(false); // WHEN request to show primary bouncer - mStatusBarKeyguardViewManager.showPrimaryBouncer(true); + mStatusBarKeyguardViewManager.showPrimaryBouncer(true, TEST_REASON); // THEN the scrim isn't updated from StatusBarKeyguardViewManager verify(mCentralSurfaces, never()).updateScrimController(); @@ -847,9 +850,9 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { public void testShowBouncerOrKeyguard_needsFullScreen() { when(mKeyguardSecurityModel.getSecurityMode(anyInt())).thenReturn( KeyguardSecurityModel.SecurityMode.SimPin); - mStatusBarKeyguardViewManager.showBouncerOrKeyguard(false, false); + mStatusBarKeyguardViewManager.showBouncerOrKeyguard(false, false, TEST_REASON); verify(mCentralSurfaces).hideKeyguard(); - verify(mPrimaryBouncerInteractor).show(true); + verify(mPrimaryBouncerInteractor).show(true, TEST_REASON); } @Test @@ -859,7 +862,7 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { when(mKeyguardSecurityModel.getSecurityMode(anyInt())).thenReturn( KeyguardSecurityModel.SecurityMode.SimPin); // Returning false means unable to show the bouncer - when(mPrimaryBouncerInteractor.show(true)).thenReturn(false); + when(mPrimaryBouncerInteractor.show(true, TEST_REASON)).thenReturn(false); when(mKeyguardTransitionInteractor.getTransitionState().getValue().getTo()) .thenReturn(KeyguardState.LOCKSCREEN); mStatusBarKeyguardViewManager.onStartedWakingUp(); @@ -868,8 +871,8 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { // Advance past reattempts mStatusBarKeyguardViewManager.setAttemptsToShowBouncer(10); - mStatusBarKeyguardViewManager.showBouncerOrKeyguard(false, false); - verify(mPrimaryBouncerInteractor).show(true); + mStatusBarKeyguardViewManager.showBouncerOrKeyguard(false, false, TEST_REASON); + verify(mPrimaryBouncerInteractor).show(true, TEST_REASON); verify(mCentralSurfaces).showKeyguard(); } @@ -884,7 +887,7 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { reset(mCentralSurfaces); reset(mPrimaryBouncerInteractor); mStatusBarKeyguardViewManager.showBouncerOrKeyguard( - /* hideBouncerWhenShowing= */true, false); + /* hideBouncerWhenShowing= */true, false, TEST_REASON); verify(mCentralSurfaces).showKeyguard(); verify(mPrimaryBouncerInteractor).hide(); } @@ -897,9 +900,9 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { when(mKeyguardSecurityModel.getSecurityMode(anyInt())).thenReturn( KeyguardSecurityModel.SecurityMode.SimPin); when(mPrimaryBouncerInteractor.isFullyShowing()).thenReturn(true); - mStatusBarKeyguardViewManager.showBouncerOrKeyguard(false, isFalsingReset); + mStatusBarKeyguardViewManager.showBouncerOrKeyguard(false, isFalsingReset, TEST_REASON); verify(mCentralSurfaces, never()).hideKeyguard(); - verify(mPrimaryBouncerInteractor).show(true); + verify(mPrimaryBouncerInteractor).show(true, TEST_REASON); } @Test @@ -909,24 +912,24 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { when(mKeyguardSecurityModel.getSecurityMode(anyInt())).thenReturn( KeyguardSecurityModel.SecurityMode.SimPin); when(mPrimaryBouncerInteractor.isFullyShowing()).thenReturn(true); - mStatusBarKeyguardViewManager.showBouncerOrKeyguard(false, isFalsingReset); + mStatusBarKeyguardViewManager.showBouncerOrKeyguard(false, isFalsingReset, TEST_REASON); verify(mCentralSurfaces, never()).hideKeyguard(); // Do not refresh the full screen bouncer if the call is from falsing - verify(mPrimaryBouncerInteractor, never()).show(true); + verify(mPrimaryBouncerInteractor, never()).show(true, TEST_REASON); } @Test @EnableSceneContainer public void showBouncer_attemptDeviceEntry() { - mStatusBarKeyguardViewManager.showBouncer(false); + mStatusBarKeyguardViewManager.showBouncer(false, TEST_REASON); verify(mDeviceEntryInteractor).attemptDeviceEntry(); } @Test @EnableSceneContainer public void showPrimaryBouncer() { - mStatusBarKeyguardViewManager.showPrimaryBouncer(false); + mStatusBarKeyguardViewManager.showPrimaryBouncer(false, TEST_REASON); verify(mSceneInteractor).showOverlay(eq(Overlays.Bouncer), anyString()); } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java index 846db6389d0c..2facc1c01ae1 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java @@ -283,6 +283,9 @@ public abstract class SysuiTestCase { } public FakeBroadcastDispatcher getFakeBroadcastDispatcher() { + if (mSysuiDependency == null) { + return null; + } return mSysuiDependency.getFakeBroadcastDispatcher(); } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCaseExt.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCaseExt.kt index d3dccb021ff8..c86ba6ccf47f 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCaseExt.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCaseExt.kt @@ -18,9 +18,22 @@ package com.android.systemui import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.testCase +import com.android.systemui.kosmos.useStandardTestDispatcher fun SysuiTestCase.testKosmos(): Kosmos = Kosmos().apply { testCase = this@testKosmos } +/** + * This should not be called directly. Instead, you can use: + * - testKosmos() to use the default dispatcher (which will soon be unconfined, see go/thetiger) + * - testKosmos().useStandardTestDispatcher() to explicitly choose the standard dispatcher + * - testKosmos().useUnconfinedTestDispatcher() to explicitly choose the unconfined dispatcher + * + * For details, see go/thetiger + */ +@Deprecated("Do not call this directly. Use testKosmos() with dispatcher functions if needed.") +fun SysuiTestCase.testKosmosLegacy(): Kosmos = + Kosmos().useStandardTestDispatcher().apply { testCase = this@testKosmosLegacy } + /** Run [f] on the main thread and return its result once completed. */ fun <T : Any> SysuiTestCase.runOnMainThreadAndWaitForIdleSync(f: () -> T): T { lateinit var result: T diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/DisplayScopeRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/DisplayScopeRepositoryKosmos.kt index ff4ba61b6965..208eabc44073 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/DisplayScopeRepositoryKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/DisplayScopeRepositoryKosmos.kt @@ -16,11 +16,13 @@ package com.android.systemui.display.data.repository +import com.android.app.displaylib.PerDisplayRepository import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.testDispatcher +import kotlinx.coroutines.CoroutineScope val Kosmos.fakeDisplayScopeRepository by Kosmos.Fixture { FakeDisplayScopeRepository(testDispatcher) } -var Kosmos.displayScopeRepository: DisplayScopeRepository by +var Kosmos.displayScopeRepository: PerDisplayRepository<CoroutineScope> by Kosmos.Fixture { fakeDisplayScopeRepository } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayScopeRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayScopeRepository.kt index 3c2592471694..84c9abfba803 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayScopeRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/FakeDisplayScopeRepository.kt @@ -16,15 +16,18 @@ package com.android.systemui.display.data.repository +import com.android.app.displaylib.PerDisplayRepository import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope -class FakeDisplayScopeRepository(private val dispatcher: CoroutineDispatcher) : - DisplayScopeRepository { +class FakeDisplayScopeRepository( + private val dispatcher: CoroutineDispatcher, + override val debugName: String = "FakeDisplayScopeRepository", +) : PerDisplayRepository<CoroutineScope> { private val perDisplayScopes = mutableMapOf<Int, CoroutineScope>() - override fun scopeForDisplay(displayId: Int): CoroutineScope { + override fun get(displayId: Int): CoroutineScope { return perDisplayScopes.computeIfAbsent(displayId) { CoroutineScope(dispatcher) } } } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/PerDisplayStoreKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/PerDisplayStoreKosmos.kt index 4b516e9c74bc..161e06295852 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/PerDisplayStoreKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/display/data/repository/PerDisplayStoreKosmos.kt @@ -16,6 +16,8 @@ package com.android.systemui.display.data.repository +import com.android.app.displaylib.DisplayInstanceLifecycleManager +import com.android.app.displaylib.FakeDisplayInstanceLifecycleManager import com.android.app.displaylib.PerDisplayInstanceProviderWithTeardown import com.android.app.displaylib.PerDisplayInstanceRepositoryImpl import com.android.systemui.dump.dumpManager @@ -69,13 +71,25 @@ val Kosmos.fakePerDisplayInstanceProviderWithTeardown by Kosmos.Fixture { FakePerDisplayInstanceProviderWithTeardown() } val Kosmos.perDisplayDumpHelper by Kosmos.Fixture { PerDisplayRepoDumpHelper(dumpManager) } +val Kosmos.fakeDisplayInstanceLifecycleManager by + Kosmos.Fixture { FakeDisplayInstanceLifecycleManager() } + val Kosmos.fakePerDisplayInstanceRepository by Kosmos.Fixture { - PerDisplayInstanceRepositoryImpl( - debugName = "fakePerDisplayInstanceRepository", - instanceProvider = fakePerDisplayInstanceProviderWithTeardown, - testScope.backgroundScope, - displayRepository, - perDisplayDumpHelper, - ) + { lifecycleManager: DisplayInstanceLifecycleManager? -> + PerDisplayInstanceRepositoryImpl( + debugName = "fakePerDisplayInstanceRepository", + instanceProvider = fakePerDisplayInstanceProviderWithTeardown, + lifecycleManager, + testScope.backgroundScope, + displayRepository, + perDisplayDumpHelper, + ) + } } + +fun Kosmos.createPerDisplayInstanceRepository( + overrideLifecycleManager: DisplayInstanceLifecycleManager? = null +): PerDisplayInstanceRepositoryImpl<TestPerDisplayInstance> { + return fakePerDisplayInstanceRepository(overrideLifecycleManager) +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorKosmos.kt index 511bede7349b..41dddce77a30 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractorKosmos.kt @@ -33,6 +33,7 @@ var Kosmos.fromLockscreenTransitionInteractor by transitionInteractor = keyguardTransitionInteractor, internalTransitionInteractor = internalKeyguardTransitionInteractor, scope = applicationCoroutineScope, + applicationScope = applicationCoroutineScope, bgDispatcher = testDispatcher, mainDispatcher = testDispatcher, keyguardInteractor = keyguardInteractor, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/startable/SceneContainerStartableKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/startable/SceneContainerStartableKosmos.kt index 7a9b052481cb..349e670a9af3 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/startable/SceneContainerStartableKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/startable/SceneContainerStartableKosmos.kt @@ -46,7 +46,6 @@ import com.android.systemui.scene.domain.interactor.sceneContainerOcclusionInter import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.scene.session.shared.shadeSessionStorage import com.android.systemui.scene.shared.logger.sceneLogger -import com.android.systemui.settings.displayTracker import com.android.systemui.shade.domain.interactor.shadeInteractor import com.android.systemui.shade.domain.interactor.shadeModeInteractor import com.android.systemui.statusbar.notification.stack.domain.interactor.headsUpNotificationInteractor @@ -65,7 +64,6 @@ val Kosmos.sceneContainerStartable by Fixture { bouncerInteractor = bouncerInteractor, keyguardInteractor = keyguardInteractor, sysUiState = sysUiState, - displayId = displayTracker.defaultDisplayId, sceneLogger = sceneLogger, falsingCollector = falsingCollector, falsingManager = falsingManager, 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 34e5bfde43c9..9a9f3354a7ba 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 @@ -14,10 +14,14 @@ * limitations under the License. */ +@file:OptIn(ExperimentalKairosApi::class) + package com.android.systemui.shade.ui.viewmodel import android.content.applicationContext import com.android.systemui.battery.batteryMeterViewControllerFactory +import com.android.systemui.kairos.ExperimentalKairosApi +import com.android.systemui.kairos.kairos import com.android.systemui.kosmos.Kosmos import com.android.systemui.plugins.activityStarter import com.android.systemui.scene.domain.interactor.sceneInteractor @@ -46,6 +50,8 @@ val Kosmos.shadeHeaderViewModel: ShadeHeaderViewModel by tintedIconManagerFactory = tintedIconManagerFactory, batteryMeterViewControllerFactory = batteryMeterViewControllerFactory, statusBarIconController = mock<StatusBarIconController>(), + kairosNetwork = kairos, + mobileIconsViewModelKairos = mock(), ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/model/ActiveNotificationModelBuilder.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/model/ActiveNotificationModelBuilder.kt index 63085e178e7d..4af4e804ff10 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/model/ActiveNotificationModelBuilder.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/data/model/ActiveNotificationModelBuilder.kt @@ -19,7 +19,7 @@ package com.android.systemui.statusbar.notification.data.model import android.app.PendingIntent import android.graphics.drawable.Icon import com.android.systemui.statusbar.StatusBarIconView -import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel +import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModels import com.android.systemui.statusbar.notification.shared.ActiveNotificationModel import com.android.systemui.statusbar.notification.shared.CallType import com.android.systemui.statusbar.notification.stack.BUCKET_UNKNOWN @@ -29,6 +29,8 @@ fun activeNotificationModel( key: String, groupKey: String? = null, whenTime: Long = 0L, + isForegroundService: Boolean = false, + isOngoingEvent: Boolean = false, isAmbient: Boolean = false, isRowDismissed: Boolean = false, isSilent: Boolean = false, @@ -47,12 +49,14 @@ fun activeNotificationModel( contentIntent: PendingIntent? = null, bucket: Int = BUCKET_UNKNOWN, callType: CallType = CallType.None, - promotedContent: PromotedNotificationContentModel? = null, + promotedContent: PromotedNotificationContentModels? = null, ) = ActiveNotificationModel( key = key, groupKey = groupKey, whenTime = whenTime, + isForegroundService = isForegroundService, + isOngoingEvent = isOngoingEvent, isAmbient = isAmbient, isRowDismissed = isRowDismissed, isSilent = isSilent, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/FakePromotedNotificationContentExtractor.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/FakePromotedNotificationContentExtractor.kt index 8fdf5dbf2aeb..aaa86aaaedc6 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/FakePromotedNotificationContentExtractor.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/FakePromotedNotificationContentExtractor.kt @@ -17,21 +17,23 @@ package com.android.systemui.statusbar.notification.promoted import android.app.Notification +import com.android.systemui.statusbar.NotificationLockscreenUserManager.RedactionType import com.android.systemui.statusbar.notification.collection.NotificationEntry -import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel +import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModels import com.android.systemui.statusbar.notification.row.shared.ImageModelProvider import org.junit.Assert class FakePromotedNotificationContentExtractor : PromotedNotificationContentExtractor { @JvmField - val contentForEntry = mutableMapOf<NotificationEntry, PromotedNotificationContentModel?>() + val contentForEntry = mutableMapOf<NotificationEntry, PromotedNotificationContentModels?>() @JvmField val extractCalls = mutableListOf<Pair<NotificationEntry, Notification.Builder>>() override fun extractContent( entry: NotificationEntry, recoveredBuilder: Notification.Builder, + @RedactionType redactionType: Int, imageModelProvider: ImageModelProvider, - ): PromotedNotificationContentModel? { + ): PromotedNotificationContentModels? { extractCalls.add(entry to recoveredBuilder) if (contentForEntry.isEmpty()) { @@ -44,7 +46,7 @@ class FakePromotedNotificationContentExtractor : PromotedNotificationContentExtr } } - fun resetForEntry(entry: NotificationEntry, content: PromotedNotificationContentModel?) { + fun resetForEntry(entry: NotificationEntry, content: PromotedNotificationContentModels?) { contentForEntry.clear() contentForEntry[entry] = content extractCalls.clear() diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorKosmos.kt index 2b3158da38f9..c4542c4e709b 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/PromotedNotificationContentExtractorKosmos.kt @@ -19,6 +19,7 @@ package com.android.systemui.statusbar.notification.promoted import android.app.Notification import android.content.applicationContext import com.android.systemui.kosmos.Kosmos +import com.android.systemui.statusbar.NotificationLockscreenUserManager.REDACTION_TYPE_NONE import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.row.RowImageInflater import com.android.systemui.statusbar.notification.row.shared.skeletonImageTransform @@ -39,9 +40,10 @@ fun Kosmos.setPromotedContent(entry: NotificationEntry) { promotedNotificationContentExtractor.extractContent( entry, Notification.Builder.recoverBuilder(applicationContext, entry.sbn.notification), + REDACTION_TYPE_NONE, RowImageInflater.newInstance(previousIndex = null, reinflating = false) .useForContentModel(), ) - entry.promotedNotificationContentModel = + entry.promotedNotificationContentModels = requireNotNull(extractedContent) { "extractContent returned null" } } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/PromotedNotificationsInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/PromotedNotificationsInteractorKosmos.kt index 093ec10e2642..8b2c68aa04cd 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/PromotedNotificationsInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/domain/interactor/PromotedNotificationsInteractorKosmos.kt @@ -19,13 +19,17 @@ package com.android.systemui.statusbar.notification.promoted.domain.interactor import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.testDispatcher import com.android.systemui.statusbar.chips.call.domain.interactor.callChipInteractor +import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.mediaProjectionChipInteractor import com.android.systemui.statusbar.chips.notification.domain.interactor.statusBarNotificationChipsInteractor +import com.android.systemui.statusbar.chips.screenrecord.domain.interactor.screenRecordChipInteractor import com.android.systemui.statusbar.notification.domain.interactor.activeNotificationsInteractor val Kosmos.promotedNotificationsInteractor by Kosmos.Fixture { PromotedNotificationsInteractor( activeNotificationsInteractor = activeNotificationsInteractor, + screenRecordChipInteractor = screenRecordChipInteractor, + mediaProjectionChipInteractor = mediaProjectionChipInteractor, callChipInteractor = callChipInteractor, notifChipsInteractor = statusBarNotificationChipsInteractor, backgroundDispatcher = testDispatcher, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/shared/model/PromotedNotificationContentBuilder.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/shared/model/PromotedNotificationContentBuilder.kt new file mode 100644 index 000000000000..6916d560a7ad --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/promoted/shared/model/PromotedNotificationContentBuilder.kt @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.notification.promoted.shared.model + +class PromotedNotificationContentBuilder(val key: String) { + private val sharedBuilder = PromotedNotificationContentModel.Builder(key) + + fun applyToShared( + block: PromotedNotificationContentModel.Builder.() -> Unit + ): PromotedNotificationContentBuilder { + sharedBuilder.apply(block) + return this + } + + fun build(): PromotedNotificationContentModels { + val sharedModel = sharedBuilder.build() + return PromotedNotificationContentModels(sharedModel, sharedModel) + } +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/icon/AppIconProviderKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/icon/AppIconProviderKosmos.kt index 0fd0f1469818..277fc62242b1 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/icon/AppIconProviderKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/icon/AppIconProviderKosmos.kt @@ -19,6 +19,9 @@ package com.android.systemui.statusbar.notification.row.icon import android.content.applicationContext import com.android.systemui.dump.dumpManager import com.android.systemui.kosmos.Kosmos +import org.mockito.kotlin.mock + +val Kosmos.mockAppIconProvider by Kosmos.Fixture { mock<AppIconProvider>() } val Kosmos.appIconProvider by Kosmos.Fixture { AppIconProviderImpl(applicationContext, dumpManager) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/icon/NotificationIconStyleProviderKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/icon/NotificationIconStyleProviderKosmos.kt index b4fb7dd9d760..86ff722f99be 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/icon/NotificationIconStyleProviderKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/row/icon/NotificationIconStyleProviderKosmos.kt @@ -19,6 +19,10 @@ package com.android.systemui.statusbar.notification.row.icon import android.os.userManager import com.android.systemui.dump.dumpManager import com.android.systemui.kosmos.Kosmos +import org.mockito.kotlin.mock + +val Kosmos.mockNotificationIconStyleProvider by + Kosmos.Fixture { mock<NotificationIconStyleProvider>() } val Kosmos.notificationIconStyleProvider by Kosmos.Fixture { NotificationIconStyleProviderImpl(userManager, dumpManager) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallTestHelper.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallTestHelper.kt index 3e96fd7c729f..e5e1a830231e 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallTestHelper.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ongoingcall/shared/model/OngoingCallTestHelper.kt @@ -26,7 +26,7 @@ import com.android.systemui.statusbar.notification.data.model.activeNotification import com.android.systemui.statusbar.notification.data.repository.activeNotificationListRepository import com.android.systemui.statusbar.notification.data.repository.addNotif import com.android.systemui.statusbar.notification.data.repository.removeNotif -import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModel +import com.android.systemui.statusbar.notification.promoted.shared.model.PromotedNotificationContentModels import com.android.systemui.statusbar.notification.shared.CallType import com.android.systemui.statusbar.phone.ongoingcall.StatusBarChipsModernization import com.android.systemui.statusbar.phone.ongoingcall.data.repository.ongoingCallRepository @@ -39,7 +39,7 @@ fun inCallModel( intent: PendingIntent? = null, notificationKey: String = "test", appName: String = "", - promotedContent: PromotedNotificationContentModel? = null, + promotedContent: PromotedNotificationContentModels? = null, isAppVisible: Boolean = false, ) = OngoingCallModel.InCall( @@ -77,7 +77,7 @@ object OngoingCallTestHelper { key: String = "notif", startTimeMs: Long = 1000L, statusBarChipIconView: StatusBarIconView? = createStatusBarIconViewOrNull(), - promotedContent: PromotedNotificationContentModel? = null, + promotedContent: PromotedNotificationContentModels? = null, contentIntent: PendingIntent? = null, uid: Int = DEFAULT_UID, appName: String = "Fake name", diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ui/TintedIconManagerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ui/TintedIconManagerKosmos.kt index 8e13b624f5fa..fd63ce28a29d 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ui/TintedIconManagerKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/ui/TintedIconManagerKosmos.kt @@ -16,16 +16,24 @@ package com.android.systemui.statusbar.phone.ui +import com.android.systemui.kairos.ExperimentalKairosApi +import com.android.systemui.kairos.kairos import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.statusbar.connectivity.ui.mobileContextProvider import com.android.systemui.statusbar.pipeline.mobile.ui.mobileUiAdapter +import com.android.systemui.statusbar.pipeline.mobile.ui.mobileUiAdapterKairos import com.android.systemui.statusbar.pipeline.wifi.ui.wifiUiAdapter +@OptIn(ExperimentalKairosApi::class) val Kosmos.tintedIconManagerFactory by -Kosmos.Fixture { - TintedIconManager.Factory( - wifiUiAdapter, - mobileUiAdapter, - mobileContextProvider, - ) -}
\ No newline at end of file + Kosmos.Fixture { + TintedIconManager.Factory( + wifiUiAdapter, + mobileUiAdapter, + mobileContextProvider, + { mobileUiAdapterKairos }, + kairos, + applicationCoroutineScope, + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileUiAdapterKairosKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileUiAdapterKairosKosmos.kt new file mode 100644 index 000000000000..3a3f18ad2ede --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileUiAdapterKairosKosmos.kt @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.pipeline.mobile.ui + +import com.android.systemui.dump.dumpManager +import com.android.systemui.kairos.ActivatedKairosFixture +import com.android.systemui.kairos.ExperimentalKairosApi +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.statusbar.phone.ui.statusBarIconController +import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.mobileIconsViewModelKairos + +@ExperimentalKairosApi +val Kosmos.mobileUiAdapterKairos by ActivatedKairosFixture { + MobileUiAdapterKairos( + statusBarIconController, + mobileIconsViewModelKairos, + mobileViewLogger, + dumpManager, + ) +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelKairosKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelKairosKosmos.kt new file mode 100644 index 000000000000..83b8283b1892 --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelKairosKosmos.kt @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel + +import com.android.systemui.flags.featureFlagsClassic +import com.android.systemui.kairos.ActivatedKairosFixture +import com.android.systemui.kairos.ExperimentalKairosApi +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.airplaneModeInteractor +import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.mobileIconsInteractorKairos +import com.android.systemui.statusbar.pipeline.mobile.ui.mobileViewLogger +import com.android.systemui.util.mockito.mock + +@ExperimentalKairosApi +val Kosmos.mobileIconsViewModelKairos by ActivatedKairosFixture { + MobileIconsViewModelKairos( + logger = mobileViewLogger, + verboseLogger = mock(), + interactor = mobileIconsInteractorKairos, + airplaneModeInteractor = airplaneModeInteractor, + constants = mock(), + flags = featureFlagsClassic, + ) +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModelKairosKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModelKairosKosmos.kt index 3ee33802e9d5..ad42a89dc237 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModelKairosKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModelKairosKosmos.kt @@ -16,7 +16,11 @@ package com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel +import com.android.systemui.kairos.ActivatedKairosFixture +import com.android.systemui.kairos.ExperimentalKairosApi import com.android.systemui.kosmos.Kosmos -val Kosmos.stackedMobileIconViewModelKairos by - Kosmos.Fixture { StackedMobileIconViewModelKairos(mobileIconsViewModel) } +@ExperimentalKairosApi +val Kosmos.stackedMobileIconViewModelKairos by ActivatedKairosFixture { + StackedMobileIconViewModelKairos(mobileIconsViewModelKairos) +} diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModelKosmos.kt index 880ba5eee5d2..0a8e0a7d48b6 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/StackedMobileIconViewModelKosmos.kt @@ -18,5 +18,8 @@ package com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel import com.android.systemui.kosmos.Kosmos -val Kosmos.stackedMobileIconViewModel: StackedMobileIconViewModel by - Kosmos.Fixture { StackedMobileIconViewModel(mobileIconsViewModel) } +var Kosmos.stackedMobileIconViewModel: StackedMobileIconViewModel by + Kosmos.Fixture { stackedMobileIconViewModelImpl } + +val Kosmos.stackedMobileIconViewModelImpl by + Kosmos.Fixture { StackedMobileIconViewModelImpl(mobileIconsViewModel) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorKosmos.kt index 1504df4ef6d0..6767300a22bc 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/user/domain/interactor/UserSwitcherInteractorKosmos.kt @@ -55,5 +55,6 @@ val Kosmos.userSwitcherInteractor by uiEventLogger = uiEventLogger, userRestrictionChecker = userRestrictionChecker, processWrapper = processWrapper, + userLogoutInteractor = userLogoutInteractor, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/window/data/repository/WindowRootViewBlurRepositoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/window/data/repository/WindowRootViewBlurRepositoryKosmos.kt index b619e2d70724..2b518182922d 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/window/data/repository/WindowRootViewBlurRepositoryKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/window/data/repository/WindowRootViewBlurRepositoryKosmos.kt @@ -26,7 +26,7 @@ val Kosmos.windowRootViewBlurRepository: WindowRootViewBlurRepository by Kosmos.Fixture { fakeWindowRootViewBlurRepository } class FakeWindowRootViewBlurRepository : WindowRootViewBlurRepository { - override val blurRadius: MutableStateFlow<Int> = MutableStateFlow(0) + override val blurRequestedByShade: MutableStateFlow<Int> = MutableStateFlow(0) override val isBlurOpaque: MutableStateFlow<Boolean> = MutableStateFlow(false) override val isBlurSupported: MutableStateFlow<Boolean> = MutableStateFlow(false) override var blurAppliedListener: BlurAppliedListener? = null diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/window/ui/viewmodel/WindowRootViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/window/ui/viewmodel/WindowRootViewModelKosmos.kt index 5a02bfbacd35..e56f32f3534f 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/window/ui/viewmodel/WindowRootViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/window/ui/viewmodel/WindowRootViewModelKosmos.kt @@ -16,9 +16,11 @@ package com.android.systemui.window.ui.viewmodel +import com.android.systemui.keyguard.domain.interactor.keyguardInteractor import com.android.systemui.keyguard.ui.transitions.FakeBouncerTransition import com.android.systemui.keyguard.ui.transitions.FakeGlanceableHubTransition import com.android.systemui.kosmos.Kosmos +import com.android.systemui.shade.domain.interactor.shadeInteractor import com.android.systemui.window.domain.interactor.windowRootViewBlurInteractor import org.mockito.internal.util.collections.Sets @@ -38,5 +40,7 @@ val Kosmos.windowRootViewModel by fakeBouncerTransitions, fakeGlanceableHubTransitions, windowRootViewBlurInteractor, + keyguardInteractor, + shadeInteractor, ) } diff --git a/packages/WallpaperBackup/Android.bp b/packages/WallpaperBackup/Android.bp index b8e0d427f3d8..b0c0c3a5d455 100644 --- a/packages/WallpaperBackup/Android.bp +++ b/packages/WallpaperBackup/Android.bp @@ -48,7 +48,9 @@ android_test { static_libs: [ "androidx.test.core", "androidx.test.rules", + "flag-junit", "mockito-target-minus-junit4", + "platform-test-annotations", "truth", ], resource_dirs: ["test/res"], diff --git a/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java b/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java index 44ea9a2848ac..a80a64d6daba 100644 --- a/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java +++ b/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java @@ -16,6 +16,7 @@ package com.android.wallpaperbackup; +import static android.app.Flags.liveWallpaperContentHandling; import static android.app.WallpaperManager.FLAG_LOCK; import static android.app.WallpaperManager.FLAG_SYSTEM; import static android.app.WallpaperManager.ORIENTATION_UNKNOWN; @@ -27,6 +28,7 @@ import static com.android.wallpaperbackup.WallpaperEventLogger.ERROR_NO_WALLPAPE import static com.android.wallpaperbackup.WallpaperEventLogger.ERROR_QUOTA_EXCEEDED; import static com.android.window.flags.Flags.multiCrop; +import android.annotation.Nullable; import android.app.AppGlobals; import android.app.WallpaperManager; import android.app.backup.BackupAgent; @@ -35,6 +37,7 @@ import android.app.backup.BackupDataOutput; import android.app.backup.BackupManager; import android.app.backup.BackupRestoreEventLogger.BackupRestoreError; import android.app.backup.FullBackupDataOutput; +import android.app.wallpaper.WallpaperDescription; import android.content.ComponentName; import android.content.Context; import android.content.SharedPreferences; @@ -493,11 +496,13 @@ public class WallpaperBackupAgent extends BackupAgent { // First parse the live component name so that we know for logging if we care about // logging errors with the image restore. - ComponentName wpService = parseWallpaperComponent(infoStage, "wp"); - mSystemHasLiveComponent = wpService != null; + Pair<ComponentName, WallpaperDescription> wpService = parseWallpaperComponent(infoStage, + "wp"); + mSystemHasLiveComponent = wpService.first != null; - ComponentName kwpService = parseWallpaperComponent(infoStage, "kwp"); - mLockHasLiveComponent = kwpService != null; + Pair<ComponentName, WallpaperDescription> kwpService = parseWallpaperComponent( + infoStage, "kwp"); + mLockHasLiveComponent = kwpService.first != null; boolean separateLockWallpaper = mLockHasLiveComponent || lockImageStage.exists(); // if there's no separate lock wallpaper, apply the system wallpaper to both screens. @@ -586,25 +591,39 @@ public class WallpaperBackupAgent extends BackupAgent { } @VisibleForTesting - void updateWallpaperComponent(ComponentName wpService, int which) + void updateWallpaperComponent(Pair<ComponentName, WallpaperDescription> wpService, int which) throws IOException { - if (servicePackageExists(wpService)) { - Slog.i(TAG, "Using wallpaper service " + wpService); - mWallpaperManager.setWallpaperComponentWithFlags(wpService, which); - if ((which & FLAG_LOCK) != 0) { - mEventLogger.onLockLiveWallpaperRestored(wpService); - } - if ((which & FLAG_SYSTEM) != 0) { - mEventLogger.onSystemLiveWallpaperRestored(wpService); + WallpaperDescription description = wpService.second; + boolean hasDescription = (liveWallpaperContentHandling() && description != null); + ComponentName component = hasDescription ? description.getComponent() : wpService.first; + if (servicePackageExists(component)) { + if (hasDescription) { + Slog.i(TAG, "Using wallpaper description " + description); + mWallpaperManager.setWallpaperComponentWithDescription(description, which); + if ((which & FLAG_LOCK) != 0) { + mEventLogger.onLockLiveWallpaperRestoredWithDescription(description); + } + if ((which & FLAG_SYSTEM) != 0) { + mEventLogger.onSystemLiveWallpaperRestoredWithDescription(description); + } + } else { + Slog.i(TAG, "Using wallpaper service " + component); + mWallpaperManager.setWallpaperComponentWithFlags(component, which); + if ((which & FLAG_LOCK) != 0) { + mEventLogger.onLockLiveWallpaperRestored(component); + } + if ((which & FLAG_SYSTEM) != 0) { + mEventLogger.onSystemLiveWallpaperRestored(component); + } } } else { // If we've restored a live wallpaper, but the component doesn't exist, // we should log it as an error so we can easily identify the problem // in reports from users - if (wpService != null) { + if (component != null) { // TODO(b/268471749): Handle delayed case - applyComponentAtInstall(wpService, which); - Slog.w(TAG, "Wallpaper service " + wpService + " isn't available. " + applyComponentAtInstall(component, description, which); + Slog.w(TAG, "Wallpaper service " + component + " isn't available. " + " Will try to apply later"); } } @@ -697,7 +716,6 @@ public class WallpaperBackupAgent extends BackupAgent { * (thereby preserving the center point). Then finally, adding any leftover image real-estate * (i.e. space left over on the horizontal axis) to add parallax effect. Parallax is only added * if was present in the old device's settings. - * */ private Rect findNewCropfromOldCrop(Rect oldCrop, Point oldDisplaySize, boolean oldRtl, Point newDisplaySize, Point bitmapSize, boolean newRtl) { @@ -976,10 +994,12 @@ public class WallpaperBackupAgent extends BackupAgent { return cropHints; } - private ComponentName parseWallpaperComponent(File wallpaperInfo, String sectionTag) { + private Pair<ComponentName, WallpaperDescription> parseWallpaperComponent(File wallpaperInfo, + String sectionTag) { ComponentName name = null; + WallpaperDescription description = null; try (FileInputStream stream = new FileInputStream(wallpaperInfo)) { - final XmlPullParser parser = Xml.resolvePullParser(stream); + final TypedXmlPullParser parser = Xml.resolvePullParser(stream); int type; do { @@ -991,6 +1011,7 @@ public class WallpaperBackupAgent extends BackupAgent { name = (parsedName != null) ? ComponentName.unflattenFromString(parsedName) : null; + description = parseWallpaperDescription(parser, name); break; } } @@ -998,9 +1019,30 @@ public class WallpaperBackupAgent extends BackupAgent { } catch (Exception e) { // Whoops; can't process the info file at all. Report failure. Slog.w(TAG, "Failed to parse restored component: " + e.getMessage()); - return null; + return new Pair<>(null, null); } - return name; + return new Pair<>(name, description); + } + + // Copied from com.android.server.wallpaper.WallpaperDataParser + private WallpaperDescription parseWallpaperDescription(TypedXmlPullParser parser, + ComponentName component) throws XmlPullParserException, IOException { + + WallpaperDescription description = null; + int type = parser.next(); + if (type == XmlPullParser.START_TAG && "description".equals(parser.getName())) { + // Always read the description if it's there - there may be one from a previous save + // with content handling enabled even if it's enabled now + description = WallpaperDescription.restoreFromXml(parser); + if (liveWallpaperContentHandling()) { + // null component means that wallpaper was last saved without content handling, so + // populate description from saved component + if (description.getComponent() == null) { + description = description.toBuilder().setComponent(component).build(); + } + } + } + return description; } private int getAttributeInt(XmlPullParser parser, String name, int defValue) { @@ -1037,14 +1079,16 @@ public class WallpaperBackupAgent extends BackupAgent { // Intentionally blank } - private void applyComponentAtInstall(ComponentName componentName, int which) { - PackageMonitor packageMonitor = getWallpaperPackageMonitor( - componentName, which); + private void applyComponentAtInstall(ComponentName componentName, + @Nullable WallpaperDescription description, int which) { + PackageMonitor packageMonitor = getWallpaperPackageMonitor(componentName, description, + which); packageMonitor.register(getBaseContext(), null, UserHandle.ALL, true); } @VisibleForTesting - PackageMonitor getWallpaperPackageMonitor(ComponentName componentName, int which) { + PackageMonitor getWallpaperPackageMonitor(ComponentName componentName, + @Nullable WallpaperDescription description, int which) { return new PackageMonitor() { @Override public void onPackageAdded(String packageName, int uid) { @@ -1068,7 +1112,33 @@ public class WallpaperBackupAgent extends BackupAgent { return; } - if (componentName.getPackageName().equals(packageName)) { + boolean useDescription = (liveWallpaperContentHandling() && description != null + && description.getComponent() != null); + if (useDescription && description.getComponent().getPackageName().equals( + packageName)) { + Slog.d(TAG, "Applying description " + description); + boolean success = mWallpaperManager.setWallpaperComponentWithDescription( + description, which); + WallpaperEventLogger logger = new WallpaperEventLogger( + mBackupManager.getDelayedRestoreLogger()); + if (success) { + if ((which & FLAG_SYSTEM) != 0) { + logger.onSystemLiveWallpaperRestoredWithDescription(description); + } + if ((which & FLAG_LOCK) != 0) { + logger.onLockLiveWallpaperRestoredWithDescription(description); + } + } else { + if ((which & FLAG_SYSTEM) != 0) { + logger.onSystemLiveWallpaperRestoreFailed( + WallpaperEventLogger.ERROR_SET_DESCRIPTION_EXCEPTION); + } + if ((which & FLAG_LOCK) != 0) { + logger.onLockLiveWallpaperRestoreFailed( + WallpaperEventLogger.ERROR_SET_DESCRIPTION_EXCEPTION); + } + } + } else if (componentName.getPackageName().equals(packageName)) { Slog.d(TAG, "Applying component " + componentName); boolean success = mWallpaperManager.setWallpaperComponentWithFlags( componentName, which); @@ -1191,4 +1261,4 @@ public class WallpaperBackupAgent extends BackupAgent { void setBackupManagerForTesting(BackupManager backupManager) { mBackupManager = backupManager; } -}
\ No newline at end of file +} diff --git a/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperEventLogger.java b/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperEventLogger.java index b25f95ab1936..69469e427266 100644 --- a/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperEventLogger.java +++ b/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperEventLogger.java @@ -16,12 +16,14 @@ package com.android.wallpaperbackup; +import android.annotation.NonNull; import android.annotation.Nullable; import android.app.WallpaperInfo; import android.app.backup.BackupManager; import android.app.backup.BackupRestoreEventLogger; import android.app.backup.BackupRestoreEventLogger.BackupRestoreDataType; import android.app.backup.BackupRestoreEventLogger.BackupRestoreError; +import android.app.wallpaper.WallpaperDescription; import android.content.ComponentName; import com.android.internal.annotations.VisibleForTesting; @@ -53,6 +55,16 @@ public class WallpaperEventLogger { @VisibleForTesting static final String WALLPAPER_LIVE_LOCK = "wlp_live_lock"; + /* Live component used as system (or home) screen wallpaper */ + @BackupRestoreDataType + @VisibleForTesting + static final String WALLPAPER_DESCRIPTION_SYSTEM = "wlp_description_system"; + + /* Live component used as lock screen wallpaper */ + @BackupRestoreDataType + @VisibleForTesting + static final String WALLPAPER_DESCRIPTION_LOCK = "wlp_description_lock"; + @BackupRestoreError static final String ERROR_INELIGIBLE = "ineligible"; @BackupRestoreError @@ -64,6 +76,8 @@ public class WallpaperEventLogger { @BackupRestoreError static final String ERROR_SET_COMPONENT_EXCEPTION = "exception_in_set_component"; @BackupRestoreError + static final String ERROR_SET_DESCRIPTION_EXCEPTION = "exception_in_set_description"; + @BackupRestoreError static final String ERROR_LIVE_PACKAGE_NOT_INSTALLED = "live_pkg_not_installed_in_restore"; private final BackupRestoreEventLogger mLogger; @@ -115,11 +129,11 @@ public class WallpaperEventLogger { } void onSystemImageWallpaperRestored() { - logRestoreSuccessInternal(WALLPAPER_IMG_SYSTEM, /* liveComponentWallpaperInfo */ null); + logRestoreSuccessInternal(WALLPAPER_IMG_SYSTEM, (ComponentName) null); } void onLockImageWallpaperRestored() { - logRestoreSuccessInternal(WALLPAPER_IMG_LOCK, /* liveComponentWallpaperInfo */ null); + logRestoreSuccessInternal(WALLPAPER_IMG_LOCK, (ComponentName) null); } void onSystemLiveWallpaperRestored(ComponentName wpService) { @@ -146,6 +160,13 @@ public class WallpaperEventLogger { logRestoreFailureInternal(WALLPAPER_LIVE_LOCK, error); } + void onSystemLiveWallpaperRestoredWithDescription(@NonNull WallpaperDescription description) { + logRestoreSuccessInternal(WALLPAPER_DESCRIPTION_SYSTEM, description); + } + + void onLockLiveWallpaperRestoredWithDescription(@NonNull WallpaperDescription description) { + logRestoreSuccessInternal(WALLPAPER_DESCRIPTION_LOCK, description); + } /** @@ -168,15 +189,17 @@ public class WallpaperEventLogger { */ void onRestoreException(Exception exception) { String error = exception.getClass().getName(); - if (!mProcessedDataTypes.contains(WALLPAPER_IMG_SYSTEM) && !mProcessedDataTypes.contains( - WALLPAPER_LIVE_SYSTEM)) { + if (!(mProcessedDataTypes.contains(WALLPAPER_IMG_SYSTEM) || mProcessedDataTypes.contains( + WALLPAPER_LIVE_SYSTEM) || mProcessedDataTypes.contains( + WALLPAPER_DESCRIPTION_SYSTEM))) { mLogger.logItemsRestoreFailed(WALLPAPER_IMG_SYSTEM, /* count */ 1, error); } - if (!mProcessedDataTypes.contains(WALLPAPER_IMG_LOCK) && !mProcessedDataTypes.contains( - WALLPAPER_LIVE_LOCK)) { + if (!(mProcessedDataTypes.contains(WALLPAPER_IMG_LOCK) || mProcessedDataTypes.contains( + WALLPAPER_LIVE_LOCK) || mProcessedDataTypes.contains(WALLPAPER_DESCRIPTION_LOCK))) { mLogger.logItemsRestoreFailed(WALLPAPER_IMG_LOCK, /* count */ 1, error); } } + private void logBackupSuccessInternal(@BackupRestoreDataType String which, @Nullable WallpaperInfo liveComponentWallpaperInfo) { mLogger.logItemsBackedUp(which, /* count */ 1); @@ -197,6 +220,13 @@ public class WallpaperEventLogger { mProcessedDataTypes.add(which); } + private void logRestoreSuccessInternal(@BackupRestoreDataType String which, + @NonNull WallpaperDescription description) { + mLogger.logItemsRestored(which, /* count */ 1); + logRestoredLiveWallpaperDescription(which, description); + mProcessedDataTypes.add(which); + } + private void logRestoreFailureInternal(@BackupRestoreDataType String which, @BackupRestoreError String error) { mLogger.logItemsRestoreFailed(which, /* count */ 1, error); @@ -216,4 +246,11 @@ public class WallpaperEventLogger { mLogger.logRestoreMetadata(wallpaperType, wpService.getClassName()); } } + + private void logRestoredLiveWallpaperDescription(@BackupRestoreDataType String wallpaperType, + WallpaperDescription description) { + if (description != null) { + mLogger.logRestoreMetadata(wallpaperType, description.toString()); + } + } } diff --git a/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java b/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java index f5fb644502ab..c9f1b5857d10 100644 --- a/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java +++ b/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java @@ -16,6 +16,7 @@ package com.android.wallpaperbackup; +import static android.app.Flags.FLAG_LIVE_WALLPAPER_CONTENT_HANDLING; import static android.app.WallpaperManager.FLAG_LOCK; import static android.app.WallpaperManager.FLAG_SYSTEM; import static android.os.ParcelFileDescriptor.MODE_READ_ONLY; @@ -27,6 +28,8 @@ import static com.android.wallpaperbackup.WallpaperEventLogger.ERROR_INELIGIBLE; import static com.android.wallpaperbackup.WallpaperEventLogger.ERROR_NO_METADATA; import static com.android.wallpaperbackup.WallpaperEventLogger.ERROR_NO_WALLPAPER; import static com.android.wallpaperbackup.WallpaperEventLogger.ERROR_QUOTA_EXCEEDED; +import static com.android.wallpaperbackup.WallpaperEventLogger.WALLPAPER_DESCRIPTION_LOCK; +import static com.android.wallpaperbackup.WallpaperEventLogger.WALLPAPER_DESCRIPTION_SYSTEM; import static com.android.wallpaperbackup.WallpaperEventLogger.WALLPAPER_IMG_LOCK; import static com.android.wallpaperbackup.WallpaperEventLogger.WALLPAPER_IMG_SYSTEM; import static com.android.wallpaperbackup.WallpaperEventLogger.WALLPAPER_LIVE_LOCK; @@ -54,6 +57,7 @@ import android.app.backup.BackupManager; import android.app.backup.BackupRestoreEventLogger; import android.app.backup.BackupRestoreEventLogger.DataTypeResult; import android.app.backup.FullBackupDataOutput; +import android.app.wallpaper.WallpaperDescription; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -63,7 +67,10 @@ import android.graphics.Rect; import android.os.FileUtils; import android.os.ParcelFileDescriptor; import android.os.UserHandle; +import android.platform.test.annotations.EnableFlags; +import android.platform.test.flag.junit.SetFlagsRule; import android.service.wallpaper.WallpaperService; +import android.util.Pair; import android.util.SparseArray; import android.util.Xml; @@ -79,6 +86,7 @@ import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; +import org.junit.rules.RuleChain; import org.junit.rules.TemporaryFolder; import org.junit.runner.RunWith; import org.mockito.ArgumentMatcher; @@ -113,12 +121,15 @@ public class WallpaperBackupAgentTest { @Mock private BackupManager mBackupManager; - @Rule - public TemporaryFolder mTemporaryFolder = new TemporaryFolder(); - private ContextWithServiceOverrides mContext; private IsolatedWallpaperBackupAgent mWallpaperBackupAgent; private ComponentName mWallpaperComponent; + private WallpaperDescription mWallpaperDescription; + + private final TemporaryFolder mTemporaryFolder = new TemporaryFolder(); + + @Rule + public RuleChain mRuleChain = RuleChain.outerRule(new SetFlagsRule()).around(mTemporaryFolder); @Before public void setUp() { @@ -135,6 +146,8 @@ public class WallpaperBackupAgentTest { BackupAnnotations.OperationType.BACKUP); mWallpaperComponent = new ComponentName(TEST_WALLPAPER_PACKAGE, ""); + mWallpaperDescription = new WallpaperDescription.Builder().setComponent( + mWallpaperComponent).setId("id").build(); } @After @@ -366,11 +379,128 @@ public class WallpaperBackupAgentTest { } @Test - public void testUpdateWallpaperComponent_systemAndLock() throws IOException { - mWallpaperBackupAgent.mIsDeviceInRestore = true; - mWallpaperBackupAgent.updateWallpaperComponent(mWallpaperComponent, + public void testUpdateWallpaperComponent_immediate_systemAndLock() throws IOException { + mWallpaperBackupAgent.mPackageExists = true; + + mWallpaperBackupAgent.updateWallpaperComponent(new Pair<>(mWallpaperComponent, null), /* which */ FLAG_LOCK | FLAG_SYSTEM); + assertThat(mWallpaperBackupAgent.mGetPackageMonitorCallCount).isEqualTo(0); + verify(mWallpaperManager, times(1)) + .setWallpaperComponentWithFlags(mWallpaperComponent, FLAG_LOCK | FLAG_SYSTEM); + verify(mWallpaperManager, never()) + .setWallpaperComponentWithFlags(mWallpaperComponent, FLAG_SYSTEM); + verify(mWallpaperManager, never()) + .setWallpaperComponentWithFlags(mWallpaperComponent, FLAG_LOCK); + verify(mWallpaperManager, never()).clear(anyInt()); + } + + @Test + public void testUpdateWallpaperComponent_immediate_systemOnly() + throws IOException { + mWallpaperBackupAgent.mPackageExists = true; + + mWallpaperBackupAgent.updateWallpaperComponent(new Pair<>(mWallpaperComponent, null), + /* which */ FLAG_SYSTEM); + + assertThat(mWallpaperBackupAgent.mGetPackageMonitorCallCount).isEqualTo(0); + verify(mWallpaperManager, times(1)) + .setWallpaperComponentWithFlags(mWallpaperComponent, FLAG_SYSTEM); + verify(mWallpaperManager, never()) + .setWallpaperComponentWithFlags(mWallpaperComponent, FLAG_LOCK); + verify(mWallpaperManager, never()) + .setWallpaperComponentWithFlags(mWallpaperComponent, FLAG_LOCK | FLAG_SYSTEM); + verify(mWallpaperManager, never()).clear(anyInt()); + } + + @Test + @EnableFlags(FLAG_LIVE_WALLPAPER_CONTENT_HANDLING) + public void testUpdateWallpaperDescription_immediate_systemAndLock() + throws IOException { + mWallpaperBackupAgent.mPackageExists = true; + + mWallpaperBackupAgent.updateWallpaperComponent( + new Pair<>(mWallpaperComponent, mWallpaperDescription), /* which */ + FLAG_LOCK | FLAG_SYSTEM); + + verify(mWallpaperManager, times(1)) + .setWallpaperComponentWithDescription(mWallpaperDescription, + FLAG_LOCK | FLAG_SYSTEM); + verify(mWallpaperManager, never()) + .setWallpaperComponentWithDescription(mWallpaperDescription, FLAG_SYSTEM); + verify(mWallpaperManager, never()) + .setWallpaperComponentWithDescription(mWallpaperDescription, FLAG_LOCK); + verify(mWallpaperManager, never()).clear(anyInt()); + } + + @Test + @EnableFlags(FLAG_LIVE_WALLPAPER_CONTENT_HANDLING) + public void testUpdateWallpaperDescription_immediate_systemOnly() throws IOException { + mWallpaperBackupAgent.mPackageExists = true; + + mWallpaperBackupAgent.updateWallpaperComponent( + new Pair<>(mWallpaperComponent, mWallpaperDescription), /* which */ FLAG_SYSTEM); + + verify(mWallpaperManager, times(1)) + .setWallpaperComponentWithDescription(mWallpaperDescription, FLAG_SYSTEM); + verify(mWallpaperManager, never()) + .setWallpaperComponentWithDescription(mWallpaperDescription, FLAG_LOCK); + verify(mWallpaperManager, never()) + .setWallpaperComponentWithDescription(mWallpaperDescription, + FLAG_LOCK | FLAG_SYSTEM); + verify(mWallpaperManager, never()).clear(anyInt()); + } + + @Test + @EnableFlags(FLAG_LIVE_WALLPAPER_CONTENT_HANDLING) + public void testUpdateWallpaperDescription_delayed_systemAndLock() + throws IOException { + mWallpaperBackupAgent.mIsDeviceInRestore = true; + mWallpaperBackupAgent.updateWallpaperComponent( + new Pair<>(mWallpaperComponent, mWallpaperDescription), /* which */ + FLAG_LOCK | FLAG_SYSTEM); + + // Imitate wallpaper component installation. + mWallpaperBackupAgent.mWallpaperPackageMonitor.onPackageAdded(TEST_WALLPAPER_PACKAGE, + /* uid */0); + verify(mWallpaperManager, times(1)) + .setWallpaperComponentWithDescription(mWallpaperDescription, + FLAG_LOCK | FLAG_SYSTEM); + verify(mWallpaperManager, never()) + .setWallpaperComponentWithDescription(mWallpaperDescription, FLAG_SYSTEM); + verify(mWallpaperManager, never()) + .setWallpaperComponentWithDescription(mWallpaperDescription, FLAG_LOCK); + verify(mWallpaperManager, never()).clear(anyInt()); + } + + @Test + @EnableFlags(FLAG_LIVE_WALLPAPER_CONTENT_HANDLING) + public void testUpdateWallpaperDescription_delayed_systemOnly() throws IOException { + mWallpaperBackupAgent.mIsDeviceInRestore = true; + + mWallpaperBackupAgent.updateWallpaperComponent( + new Pair<>(mWallpaperComponent, mWallpaperDescription), /* which */ FLAG_SYSTEM); + + // Imitate wallpaper component installation. + mWallpaperBackupAgent.mWallpaperPackageMonitor.onPackageAdded(TEST_WALLPAPER_PACKAGE, + /* uid */0); + + verify(mWallpaperManager, times(1)) + .setWallpaperComponentWithDescription(mWallpaperDescription, FLAG_SYSTEM); + verify(mWallpaperManager, never()) + .setWallpaperComponentWithDescription(mWallpaperDescription, FLAG_LOCK); + verify(mWallpaperManager, never()) + .setWallpaperComponentWithDescription(mWallpaperDescription, + FLAG_LOCK | FLAG_SYSTEM); + verify(mWallpaperManager, never()).clear(anyInt()); + } + + @Test + public void testUpdateWallpaperComponent_delayed_systemAndLock() throws IOException { + mWallpaperBackupAgent.mIsDeviceInRestore = true; + + mWallpaperBackupAgent.updateWallpaperComponent(new Pair<>(mWallpaperComponent, null), + /* which */ FLAG_LOCK | FLAG_SYSTEM); // Imitate wallpaper component installation. mWallpaperBackupAgent.mWallpaperPackageMonitor.onPackageAdded(TEST_WALLPAPER_PACKAGE, /* uid */0); @@ -384,13 +514,12 @@ public class WallpaperBackupAgentTest { } @Test - public void testUpdateWallpaperComponent_systemOnly() + public void testUpdateWallpaperComponent_delayed_systemOnly() throws IOException { mWallpaperBackupAgent.mIsDeviceInRestore = true; - mWallpaperBackupAgent.updateWallpaperComponent(mWallpaperComponent, + mWallpaperBackupAgent.updateWallpaperComponent(new Pair<>(mWallpaperComponent, null), /* which */ FLAG_SYSTEM); - // Imitate wallpaper component installation. mWallpaperBackupAgent.mWallpaperPackageMonitor.onPackageAdded(TEST_WALLPAPER_PACKAGE, /* uid */0); @@ -405,11 +534,11 @@ public class WallpaperBackupAgentTest { } @Test - public void testUpdateWallpaperComponent_deviceNotInRestore_doesNotApply() + public void testUpdateWallpaperComponent_delayed_deviceNotInRestore_doesNotApply() throws IOException { mWallpaperBackupAgent.mIsDeviceInRestore = false; - mWallpaperBackupAgent.updateWallpaperComponent(mWallpaperComponent, + mWallpaperBackupAgent.updateWallpaperComponent(new Pair<>(mWallpaperComponent, null), /* which */ FLAG_LOCK | FLAG_SYSTEM); // Imitate wallpaper component installation. @@ -421,11 +550,11 @@ public class WallpaperBackupAgentTest { } @Test - public void testUpdateWallpaperComponent_differentPackageInstalled_doesNotApply() + public void testUpdateWallpaperComponent_delayed_differentPackageInstalled_doesNotApply() throws IOException { mWallpaperBackupAgent.mIsDeviceInRestore = false; - mWallpaperBackupAgent.updateWallpaperComponent(mWallpaperComponent, + mWallpaperBackupAgent.updateWallpaperComponent(new Pair<>(mWallpaperComponent, null), /* which */ FLAG_LOCK | FLAG_SYSTEM); // Imitate "wrong" wallpaper component installation. @@ -745,9 +874,8 @@ public class WallpaperBackupAgentTest { } @Test - public void testUpdateWallpaperComponent_delayedRestore_logsSuccess() throws Exception { + public void testUpdateWallpaperComponent_delayed_succeeds_logsSuccess() throws Exception { mWallpaperBackupAgent.mIsDeviceInRestore = true; - when(mWallpaperManager.setWallpaperComponent(any())).thenReturn(true); when(mWallpaperManager.setWallpaperComponentWithFlags(any(), eq(FLAG_LOCK | FLAG_SYSTEM))) .thenReturn(true); BackupRestoreEventLogger logger = new BackupRestoreEventLogger( @@ -755,7 +883,7 @@ public class WallpaperBackupAgentTest { when(mBackupManager.getDelayedRestoreLogger()).thenReturn(logger); mWallpaperBackupAgent.setBackupManagerForTesting(mBackupManager); - mWallpaperBackupAgent.updateWallpaperComponent(mWallpaperComponent, + mWallpaperBackupAgent.updateWallpaperComponent(new Pair<>(mWallpaperComponent, null), /* which */ FLAG_LOCK | FLAG_SYSTEM); // Imitate wallpaper component installation. mWallpaperBackupAgent.mWallpaperPackageMonitor.onPackageAdded(TEST_WALLPAPER_PACKAGE, @@ -771,15 +899,41 @@ public class WallpaperBackupAgentTest { @Test - public void testUpdateWallpaperComponent_delayedRestoreFails_logsFailure() throws Exception { + @EnableFlags(FLAG_LIVE_WALLPAPER_CONTENT_HANDLING) + public void testUpdateWallpaperDescription_delayed_succeeds_logsSuccess() throws Exception { + mWallpaperBackupAgent.mIsDeviceInRestore = true; + when(mWallpaperManager.setWallpaperComponentWithDescription(any(), + eq(FLAG_LOCK | FLAG_SYSTEM))).thenReturn(true); + BackupRestoreEventLogger logger = new BackupRestoreEventLogger( + BackupAnnotations.OperationType.RESTORE); + when(mBackupManager.getDelayedRestoreLogger()).thenReturn(logger); + mWallpaperBackupAgent.setBackupManagerForTesting(mBackupManager); + + mWallpaperBackupAgent.updateWallpaperComponent(new Pair<>(null, mWallpaperDescription), + /* which */ FLAG_LOCK | FLAG_SYSTEM); + // Imitate wallpaper component installation. + mWallpaperBackupAgent.mWallpaperPackageMonitor.onPackageAdded(TEST_WALLPAPER_PACKAGE, + /* uid */0); + + DataTypeResult system = getLoggingResult(WALLPAPER_DESCRIPTION_SYSTEM, + logger.getLoggingResults()); + DataTypeResult lock = getLoggingResult(WALLPAPER_DESCRIPTION_LOCK, + logger.getLoggingResults()); + assertThat(system).isNotNull(); + assertThat(system.getSuccessCount()).isEqualTo(1); + assertThat(lock).isNotNull(); + assertThat(lock.getSuccessCount()).isEqualTo(1); + } + + @Test + public void testUpdateWallpaperComponent_delayed_fails_logsFailure() throws Exception { mWallpaperBackupAgent.mIsDeviceInRestore = true; - when(mWallpaperManager.setWallpaperComponent(any())).thenReturn(false); BackupRestoreEventLogger logger = new BackupRestoreEventLogger( BackupAnnotations.OperationType.RESTORE); when(mBackupManager.getDelayedRestoreLogger()).thenReturn(logger); mWallpaperBackupAgent.setBackupManagerForTesting(mBackupManager); - mWallpaperBackupAgent.updateWallpaperComponent(mWallpaperComponent, + mWallpaperBackupAgent.updateWallpaperComponent(new Pair<>(mWallpaperComponent, null), /* which */ FLAG_LOCK | FLAG_SYSTEM); // Imitate wallpaper component installation. mWallpaperBackupAgent.mWallpaperPackageMonitor.onPackageAdded(TEST_WALLPAPER_PACKAGE, @@ -793,7 +947,29 @@ public class WallpaperBackupAgentTest { } @Test - public void testUpdateWallpaperComponent_delayedRestore_packageNotInstalled_logsFailure() + @EnableFlags(FLAG_LIVE_WALLPAPER_CONTENT_HANDLING) + public void testUpdateWallpaperDescription_delayed_fails_logsFailure() throws Exception { + mWallpaperBackupAgent.mIsDeviceInRestore = true; + BackupRestoreEventLogger logger = new BackupRestoreEventLogger( + BackupAnnotations.OperationType.RESTORE); + when(mBackupManager.getDelayedRestoreLogger()).thenReturn(logger); + mWallpaperBackupAgent.setBackupManagerForTesting(mBackupManager); + + mWallpaperBackupAgent.updateWallpaperComponent(new Pair<>(null, mWallpaperDescription), + /* which */ FLAG_LOCK | FLAG_SYSTEM); + // Imitate wallpaper component installation. + mWallpaperBackupAgent.mWallpaperPackageMonitor.onPackageAdded(TEST_WALLPAPER_PACKAGE, + /* uid */0); + + DataTypeResult system = getLoggingResult(WALLPAPER_LIVE_SYSTEM, logger.getLoggingResults()); + assertThat(system).isNotNull(); + assertThat(system.getFailCount()).isEqualTo(1); + assertThat(system.getErrors()).containsKey( + WallpaperEventLogger.ERROR_SET_DESCRIPTION_EXCEPTION); + } + + @Test + public void testUpdateWallpaperComponent_delayed_packageNotInstalled_logsFailure() throws Exception { mWallpaperBackupAgent.mIsDeviceInRestore = false; BackupRestoreEventLogger logger = new BackupRestoreEventLogger( @@ -801,7 +977,7 @@ public class WallpaperBackupAgentTest { when(mBackupManager.getDelayedRestoreLogger()).thenReturn(logger); mWallpaperBackupAgent.setBackupManagerForTesting(mBackupManager); - mWallpaperBackupAgent.updateWallpaperComponent(mWallpaperComponent, + mWallpaperBackupAgent.updateWallpaperComponent(new Pair<>(mWallpaperComponent, null), /* which */ FLAG_LOCK | FLAG_SYSTEM); // Imitate wallpaper component installation. @@ -990,6 +1166,8 @@ public class WallpaperBackupAgentTest { List<File> mBackedUpFiles = new ArrayList<>(); PackageMonitor mWallpaperPackageMonitor; boolean mIsDeviceInRestore = false; + boolean mPackageExists = false; + int mGetPackageMonitorCallCount = 0; @Override protected void backupFile(File file, FullBackupDataOutput data) { @@ -998,7 +1176,7 @@ public class WallpaperBackupAgentTest { @Override boolean servicePackageExists(ComponentName comp) { - return false; + return mPackageExists; } @Override @@ -1007,8 +1185,11 @@ public class WallpaperBackupAgentTest { } @Override - PackageMonitor getWallpaperPackageMonitor(ComponentName componentName, int which) { - mWallpaperPackageMonitor = super.getWallpaperPackageMonitor(componentName, which); + PackageMonitor getWallpaperPackageMonitor(ComponentName componentName, + WallpaperDescription description, int which) { + mGetPackageMonitorCallCount++; + mWallpaperPackageMonitor = super.getWallpaperPackageMonitor(componentName, description, + which); return mWallpaperPackageMonitor; } diff --git a/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperEventLoggerTest.java b/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperEventLoggerTest.java index 383bf2f68217..09aa23e713a5 100644 --- a/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperEventLoggerTest.java +++ b/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperEventLoggerTest.java @@ -16,6 +16,10 @@ package com.android.wallpaperbackup; +import static android.app.Flags.FLAG_LIVE_WALLPAPER_CONTENT_HANDLING; + +import static com.android.wallpaperbackup.WallpaperEventLogger.WALLPAPER_DESCRIPTION_LOCK; +import static com.android.wallpaperbackup.WallpaperEventLogger.WALLPAPER_DESCRIPTION_SYSTEM; import static com.android.wallpaperbackup.WallpaperEventLogger.WALLPAPER_IMG_LOCK; import static com.android.wallpaperbackup.WallpaperEventLogger.WALLPAPER_IMG_SYSTEM; import static com.android.wallpaperbackup.WallpaperEventLogger.WALLPAPER_LIVE_LOCK; @@ -31,16 +35,20 @@ import android.app.WallpaperInfo; import android.app.backup.BackupAnnotations; import android.app.backup.BackupManager; import android.app.backup.BackupRestoreEventLogger; +import android.app.wallpaper.WallpaperDescription; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; +import android.platform.test.annotations.EnableFlags; +import android.platform.test.flag.junit.SetFlagsRule; import android.service.wallpaper.WallpaperService; import androidx.test.InstrumentationRegistry; import androidx.test.runner.AndroidJUnit4; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; @@ -63,6 +71,10 @@ public class WallpaperEventLoggerTest { private WallpaperEventLogger mWallpaperEventLogger; private WallpaperInfo mWallpaperInfo; + private WallpaperDescription mWallpaperDescription; + + @Rule + public SetFlagsRule mSetFlagsRule = new SetFlagsRule(); @Before public void setUp() throws Exception { @@ -73,6 +85,8 @@ public class WallpaperEventLoggerTest { mWallpaperInfo = getWallpaperInfo(); mWallpaperEventLogger = new WallpaperEventLogger(mMockBackupManager, mMockBackupAgent); + mWallpaperDescription = new WallpaperDescription.Builder().setComponent( + mWallpaperInfo.getComponent()).build(); } @Test @@ -263,6 +277,19 @@ public class WallpaperEventLoggerTest { } @Test + @EnableFlags(FLAG_LIVE_WALLPAPER_CONTENT_HANDLING) + public void onSystemLiveWallpaperRestoredWithDescription_logsSuccess() { + setUpLoggerForRestore(); + + mWallpaperEventLogger.onSystemLiveWallpaperRestoredWithDescription(mWallpaperDescription); + BackupRestoreEventLogger.DataTypeResult result = getLogsForType( + WALLPAPER_DESCRIPTION_SYSTEM); + + assertThat(result).isNotNull(); + assertThat(result.getSuccessCount()).isEqualTo(1); + } + + @Test public void onLockLiveWallpaperRestored_logsSuccess() { setUpLoggerForRestore(); @@ -274,6 +301,17 @@ public class WallpaperEventLoggerTest { } @Test + public void onLockLiveWallpaperRestoredWithDescription_logsSuccess() { + setUpLoggerForRestore(); + + mWallpaperEventLogger.onLockLiveWallpaperRestoredWithDescription(mWallpaperDescription); + BackupRestoreEventLogger.DataTypeResult result = getLogsForType(WALLPAPER_DESCRIPTION_LOCK); + + assertThat(result).isNotNull(); + assertThat(result.getSuccessCount()).isEqualTo(1); + } + + @Test public void onImgWallpaperRestored_nullInfo_doesNotLogMetadata() { setUpLoggerForRestore(); @@ -286,7 +324,7 @@ public class WallpaperEventLoggerTest { @Test - public void onLiveWallpaperRestored_logsMetadata() { + public void onSystemLiveWallpaperRestored_logsMetadata() { setUpLoggerForRestore(); mWallpaperEventLogger.onSystemLiveWallpaperRestored(mWallpaperInfo.getComponent()); @@ -296,6 +334,19 @@ public class WallpaperEventLoggerTest { assertThat(result.getMetadataHash()).isNotNull(); } + @Test + @EnableFlags(FLAG_LIVE_WALLPAPER_CONTENT_HANDLING) + public void onSystemLiveWallpaperRestoredDescription_logsMetadata() { + setUpLoggerForRestore(); + + mWallpaperEventLogger.onSystemLiveWallpaperRestoredWithDescription(mWallpaperDescription); + BackupRestoreEventLogger.DataTypeResult result = getLogsForType( + WALLPAPER_DESCRIPTION_SYSTEM); + + assertThat(result).isNotNull(); + assertThat(result.getMetadataHash()).isNotNull(); + } + @Test public void onSystemImgWallpaperRestoreFailed_logsFail() { @@ -373,7 +424,7 @@ public class WallpaperEventLoggerTest { } @Test - public void onWallpaperRestoreException_liveTypeProcessed_doesNotLogForSameImgType() { + public void onSystemWallpaperRestoreException_liveTypeProcessed_doesNotLogForSameImgType() { setUpLoggerForRestore(); mWallpaperEventLogger.onSystemLiveWallpaperRestored(mWallpaperInfo.getComponent()); @@ -383,6 +434,41 @@ public class WallpaperEventLoggerTest { assertThat(result).isNull(); } + @Test + @EnableFlags(FLAG_LIVE_WALLPAPER_CONTENT_HANDLING) + public void onSystemWallpaperRestoreException_descriptionProcessed_doesNotLogForSameImgType() { + setUpLoggerForRestore(); + mWallpaperEventLogger.onSystemLiveWallpaperRestoredWithDescription(mWallpaperDescription); + + mWallpaperEventLogger.onRestoreException(new Exception()); + BackupRestoreEventLogger.DataTypeResult result = getLogsForType(WALLPAPER_IMG_SYSTEM); + + assertThat(result).isNull(); + } + + @Test + public void onLockWallpaperRestoreException_liveTypeProcessed_doesNotLogForSameImgType() { + setUpLoggerForRestore(); + mWallpaperEventLogger.onLockLiveWallpaperRestored(mWallpaperInfo.getComponent()); + + mWallpaperEventLogger.onRestoreException(new Exception()); + BackupRestoreEventLogger.DataTypeResult result = getLogsForType(WALLPAPER_IMG_LOCK); + + assertThat(result).isNull(); + } + + @Test + @EnableFlags(FLAG_LIVE_WALLPAPER_CONTENT_HANDLING) + public void onLockWallpaperRestoreException_descriptionProcessed_doesNotLogForSameImgType() { + setUpLoggerForRestore(); + mWallpaperEventLogger.onLockLiveWallpaperRestoredWithDescription(mWallpaperDescription); + + mWallpaperEventLogger.onRestoreException(new Exception()); + BackupRestoreEventLogger.DataTypeResult result = getLogsForType(WALLPAPER_IMG_LOCK); + + assertThat(result).isNull(); + } + private BackupRestoreEventLogger.DataTypeResult getLogsForType(String dataType) { for (BackupRestoreEventLogger.DataTypeResult result : mEventLogger.getLoggingResults()) { if ((result.getDataType()).equals(dataType)) { diff --git a/ravenwood/scripts/ravenwood-stats-collector.sh b/ravenwood/scripts/ravenwood-stats-collector.sh index b83216af95fe..c2bf8d82e272 100755 --- a/ravenwood/scripts/ravenwood-stats-collector.sh +++ b/ravenwood/scripts/ravenwood-stats-collector.sh @@ -29,21 +29,46 @@ mkdir -p $out_dir mkdir -p $keep_all_dir mkdir -p $dump_dir -# Where the input files are. -path=$ANDROID_BUILD_TOP/out/host/linux-x86/testcases/ravenwood-stats-checker/x86_64/ -timestamp="$(date --iso-8601=seconds)" +stats_checker_module="ravenwood-stats-checker" +minfo=$OUT/module-info.json -m() { - ${ANDROID_BUILD_TOP}/build/soong/soong_ui.bash --make-mode "$@" -} +timestamp="$(date --iso-8601=seconds)" -# Building this will generate the files we need. -m ravenwood-stats-checker +# First, use jq to get the output files from the checker module. This will be something like this: +# +# --- +# out/host/linux-x86/nativetest64/ravenwood-stats-checker/framework-configinfrastructure_apis.csv +# out/host/linux-x86/nativetest64/ravenwood-stats-checker/framework-configinfrastructure_dump.txt +# : +# out/host/linux-x86/nativetest64/ravenwood-stats-checker/hoststubgen_services.core_stats.csv +# out/host/linux-x86/nativetest64/ravenwood-stats-checker/ravenwood-stats-checker +# --- +# Then, use grep to find the script's path (the last line in the above examle) +script_path="$( + jq -r ".\"$stats_checker_module\".installed | .[]" $minfo | + grep '/ravenwood-stats-checker$' +)" + +if [[ "$script_path" == "" ]] ; then + echo "Error: $stats_checker_module script not found from $minfo" + exit 1 +fi + +# This is the directory where our input files are. +script_dir="$ANDROID_BUILD_TOP/$(dirname "$script_path")" + +# Clear it before (re-)buildign the script, to make sure we won't have stale files. +rm -fr "$script_dir" + +# Then build it, which will also collect the input files in the same dir. +echo "Collecting the input files..." +m "$stats_checker_module" # Start... -cd $path +echo "Files directory is: $script_dir" +cd "$script_dir" dump() { local jar=$1 @@ -55,6 +80,7 @@ dump() { collect_stats() { local out="$1" + local desc="$2" { # Copy the header, with the first column appended. echo -n "Jar,Generated Date," @@ -66,11 +92,12 @@ collect_stats() { dump "framework-statsd" framework-statsd_stats.csv } > "$out" - echo "Stats CVS created at $out" + echo "Stats CVS created at $out$desc" } collect_apis() { local out="$1" + local desc="$2" { # Copy the header, with the first column appended. echo -n "Jar,Generated Date," @@ -82,12 +109,12 @@ collect_apis() { dump "framework-statsd" framework-statsd_apis.csv } > "$out" - echo "API CVS created at $out" + echo "API CVS created at $out$desc" } -collect_stats $stats -collect_apis $apis +collect_stats $stats " (import it as 'ravenwood_stats')" +collect_apis $apis " (import it as 'ravenwood_supported_apis')" cp *keep_all.txt $keep_all_dir echo "Keep all files created at:" diff --git a/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/HostStubGenStats.kt b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/HostStubGenStats.kt index 9045db210495..ea8c25b6833c 100644 --- a/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/HostStubGenStats.kt +++ b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/HostStubGenStats.kt @@ -15,13 +15,24 @@ */ package com.android.hoststubgen +import com.android.hoststubgen.asm.ClassNodes import com.android.hoststubgen.asm.getOuterClassNameFromFullClassName import com.android.hoststubgen.asm.getPackageNameFromFullClassName import com.android.hoststubgen.filters.FilterPolicyWithReason +import com.android.hoststubgen.filters.StatsLabel import org.objectweb.asm.Opcodes import java.io.PrintWriter -open class HostStubGenStats { +/** + * TODO This is for the legacy API coverage stats CSV that shows how many APIs are "supported" + * in each class with some heuristics. We created [ApiDumper] later, which dumpps all methods + * with the "supported" status. We should update the coverage dashboard to use the [ApiDumper] + * output and remove this class, once we port all the heuristics to [ApiDumper] as well. + * (For example, this class ignores non-public and/or abstract methods, but [ApiDumper] shows + * all of them in the same way. We should probably mark them as "Boring" or maybe "Ignore" + * for [ApiDumper]) + */ +open class HostStubGenStats(val classes: ClassNodes) { data class Stats( var supported: Int = 0, var total: Int = 0, @@ -30,14 +41,6 @@ open class HostStubGenStats { private val stats = mutableMapOf<String, Stats>() - data class Api( - val fullClassName: String, - val methodName: String, - val methodDesc: String, - ) - - private val apis = mutableListOf<Api>() - fun onVisitPolicyForMethod( fullClassName: String, methodName: String, @@ -45,16 +48,16 @@ open class HostStubGenStats { policy: FilterPolicyWithReason, access: Int ) { - if (policy.policy.isSupported) { - apis.add(Api(fullClassName, methodName, descriptor)) - } - // Ignore methods that aren't public if ((access and Opcodes.ACC_PUBLIC) == 0) return // Ignore methods that are abstract if ((access and Opcodes.ACC_ABSTRACT) != 0) return + // Ignore methods where policy isn't relevant - if (policy.isIgnoredForStats) return + val statsLabel = policy.statsLabel + if (statsLabel == StatsLabel.Ignored) return + + val cn = classes.findClass(fullClassName) ?: return val packageName = getPackageNameFromFullClassName(fullClassName) val className = getOuterClassNameFromFullClassName(fullClassName) @@ -70,7 +73,7 @@ open class HostStubGenStats { val packageStats = stats.getOrPut(packageName) { Stats() } val classStats = packageStats.children.getOrPut(className) { Stats() } - if (policy.policy.isSupported) { + if (statsLabel == StatsLabel.Supported) { packageStats.supported += 1 classStats.supported += 1 } diff --git a/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/dumper/ApiDumper.kt b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/dumper/ApiDumper.kt index 5e4e70f0cbaa..bb8cdccafaa6 100644 --- a/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/dumper/ApiDumper.kt +++ b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/dumper/ApiDumper.kt @@ -25,6 +25,7 @@ import com.android.hoststubgen.csvEscape import com.android.hoststubgen.filters.FilterPolicy import com.android.hoststubgen.filters.FilterPolicyWithReason import com.android.hoststubgen.filters.OutputFilter +import com.android.hoststubgen.filters.StatsLabel import com.android.hoststubgen.log import org.objectweb.asm.Type import org.objectweb.asm.tree.ClassNode @@ -44,7 +45,10 @@ class ApiDumper( val descriptor: String, ) - private val javaStandardApiPolicy = FilterPolicy.Keep.withReason("Java standard API") + private val javaStandardApiPolicy = FilterPolicy.Keep.withReason( + "Java standard API", + StatsLabel.Supported, + ) private val shownMethods = mutableSetOf<MethodKey>() @@ -53,7 +57,7 @@ class ApiDumper( */ fun dump() { pw.printf("PackageName,ClassName,FromSubclass,DeclareClass,MethodName,MethodDesc" + - ",Supported,Policy,Reason\n") + ",Supported,Policy,Reason,SupportedLabel\n") classes.forEach { classNode -> shownMethods.clear() @@ -68,23 +72,36 @@ class ApiDumper( methodClassName: String, methodName: String, methodDesc: String, - policy: FilterPolicyWithReason, + classPolicy: FilterPolicyWithReason, + methodPolicy: FilterPolicyWithReason, ) { + if (methodPolicy.statsLabel == StatsLabel.Ignored) { + return + } + // Label hack -- if the method is supported, but the class is boring, then the + // method is boring too. + var methodLabel = methodPolicy.statsLabel + if (methodLabel == StatsLabel.SupportedButBoring + && classPolicy.statsLabel == StatsLabel.SupportedButBoring) { + methodLabel = classPolicy.statsLabel + } + pw.printf( - "%s,%s,%d,%s,%s,%s,%d,%s,%s\n", + "%s,%s,%d,%s,%s,%s,%d,%s,%s,%s\n", csvEscape(classPackage), csvEscape(className), if (isSuperClass) { 1 } else { 0 }, csvEscape(methodClassName), csvEscape(methodName), csvEscape(methodDesc), - if (policy.policy.isSupported) { 1 } else { 0 }, - policy.policy, - csvEscape(policy.reason), + methodLabel.statValue, + methodPolicy.policy, + csvEscape(methodPolicy.reason), + methodLabel, ) } - private fun isDuplicate(methodName: String, methodDesc: String): Boolean { + private fun shownAlready(methodName: String, methodDesc: String): Boolean { val methodKey = MethodKey(methodName, methodDesc) if (shownMethods.contains(methodKey)) { @@ -98,6 +115,12 @@ class ApiDumper( dumpClass: ClassNode, methodClass: ClassNode, ) { + val classPolicy = filter.getPolicyForClass(dumpClass.name) + if (classPolicy.statsLabel == StatsLabel.Ignored) { + return + } + log.d("Class ${dumpClass.name} -- policy $classPolicy") + val pkg = getPackageNameFromFullClassName(dumpClass.name).toHumanReadableClassName() val cls = getClassNameFromFullClassName(dumpClass.name).toHumanReadableClassName() @@ -112,23 +135,23 @@ class ApiDumper( } } // If we already printed the method from a subclass, don't print it. - if (isDuplicate(method.name, method.desc)) { + if (shownAlready(method.name, method.desc)) { return@forEach } - val policy = filter.getPolicyForMethod(methodClass.name, method.name, method.desc) + val methodPolicy = filter.getPolicyForMethod(methodClass.name, method.name, method.desc) // Let's skip "Remove" APIs. Ideally we want to print it, just to make the CSV // complete, we still need to hide methods substituted (== @RavenwoodReplace) methods // and for now we don't have an easy way to detect it. - if (policy.policy == FilterPolicy.Remove) { + if (methodPolicy.policy == FilterPolicy.Remove) { return@forEach } val renameTo = filter.getRenameTo(methodClass.name, method.name, method.desc) dumpMethod(pkg, cls, isSuperClass, methodClass.name.toHumanReadableClassName(), - renameTo ?: method.name, method.desc, policy) + renameTo ?: method.name, method.desc, classPolicy, methodPolicy) } // Dump super class methods. @@ -155,10 +178,13 @@ class ApiDumper( dump(dumpClass, methodClass) return } - if (methodClassName.startsWith("java/") || - methodClassName.startsWith("javax/") - ) { - dumpStandardClass(dumpClass, methodClassName) + + // Dump overriding methods from Java standard classes, except for the Object methods, + // which are obvious. + if (methodClassName.startsWith("java/") || methodClassName.startsWith("javax/")) { + if (methodClassName != "java/lang/Object") { + dumpStandardClass(dumpClass, methodClassName) + } return } log.w("Super class or interface $methodClassName (used by ${dumpClass.name}) not found.") @@ -188,12 +214,12 @@ class ApiDumper( val methodDesc = Type.getMethodDescriptor(method) // If we already printed the method from a subclass, don't print it. - if (isDuplicate(methodName, methodDesc)) { + if (shownAlready(methodName, methodDesc)) { return@forEach } dumpMethod(pkg, cls, true, methodClassName, - methodName, methodDesc, javaStandardApiPolicy) + methodName, methodDesc, javaStandardApiPolicy, javaStandardApiPolicy) } } catch (e: ClassNotFoundException) { log.w("JVM type $methodClassName (used by ${dumpClass.name}) not found.") diff --git a/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/FilterPolicy.kt b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/FilterPolicy.kt index 81c26ffdf1f4..c3c870f59347 100644 --- a/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/FilterPolicy.kt +++ b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/FilterPolicy.kt @@ -155,7 +155,10 @@ enum class FilterPolicy(val policyStringOrPrefix: String) { /** * Create a [FilterPolicyWithReason] with a given reason. */ - fun withReason(reason: String): FilterPolicyWithReason { - return FilterPolicyWithReason(this, reason) + fun withReason( + reason: String, + statsLabelOverride: StatsLabel? = null, + ): FilterPolicyWithReason { + return FilterPolicyWithReason(this, reason, statsLabelOverride = statsLabelOverride) } } diff --git a/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/FilterPolicyWithReason.kt b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/FilterPolicyWithReason.kt index b10165b835f2..7358a0bfb3e6 100644 --- a/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/FilterPolicyWithReason.kt +++ b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/FilterPolicyWithReason.kt @@ -16,32 +16,54 @@ package com.android.hoststubgen.filters /** + * How each entry should be handled on the dashboard. + */ +enum class StatsLabel(val statValue: Int, val label: String) { + /** Entry shouldn't show up in the dashboard. */ + Ignored(-1, ""), + + /** Entry should be shown as "not supported" */ + NotSupported(0, "NotSupported"), + + /** + * Entry should be shown as "supported", but are too "boring" to show on the dashboard, + * e.g. annotation classes. + */ + SupportedButBoring(1, "Boring"), + + /** Entry should be shown as "supported" */ + Supported(2, "Supported"), +} + +/** * Captures a [FilterPolicy] with a human-readable reason. */ data class FilterPolicyWithReason ( - val policy: FilterPolicy, - val reason: String = "", + val policy: FilterPolicy, + val reason: String = "", + private val statsLabelOverride: StatsLabel? = null ) { /** * Return a new [FilterPolicy] with an updated reason, while keeping the original reason * as an "inner-reason". */ - fun wrapReason(reason: String): FilterPolicyWithReason { - return FilterPolicyWithReason(policy, "$reason [inner-reason: ${this.reason}]") + fun wrapReason(reason: String, statsLabelOverride: StatsLabel? = null): FilterPolicyWithReason { + return FilterPolicyWithReason( + policy, + "$reason [inner-reason: ${this.reason}]", + statsLabelOverride = statsLabelOverride, + ) } override fun toString(): String { - return "[$policy - reason: $reason]" + return "[$policy/$statsLabel - reason: $reason]" } - /** Returns whether this policy should be ignored for stats. */ - val isIgnoredForStats: Boolean - get() { - return reason.contains("anonymous-inner-class") - || reason.contains("is-annotation") - || reason.contains("is-enum") - || reason.contains("is-synthetic-method") - || reason.contains("special-class") - || reason.contains("substitute-to") + val statsLabel: StatsLabel get() { + statsLabelOverride?.let { return it } + if (policy.isSupported) { + return StatsLabel.Supported } + return StatsLabel.NotSupported + } } diff --git a/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/ImplicitOutputFilter.kt b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/ImplicitOutputFilter.kt index d44d016f7c5b..1145da635606 100644 --- a/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/ImplicitOutputFilter.kt +++ b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/ImplicitOutputFilter.kt @@ -48,7 +48,11 @@ class ImplicitOutputFilter( // If the outer class needs to be in impl, it should be in impl too. val outerPolicy = outermostFilter.getPolicyForClass(cn.outerClass) if (outerPolicy.policy.needsInOutput) { - return FilterPolicy.KeepClass.withReason("anonymous-inner-class") + // We keep this class, but don't need to show it in the dashboard. + return FilterPolicy.KeepClass.withReason( + "anonymous-inner-class", + StatsLabel.Ignored, + ) } } return null @@ -62,6 +66,15 @@ class ImplicitOutputFilter( // Use the implicit policy, if any. getClassImplicitPolicy(cn)?.let { return it } + // If it's an annotation class and we need to keep it, then + // change the reason to hide it from the stats. + if (cn.isAnnotation() && fallback.policy.needsInOutput) { + return FilterPolicy.KeepClass.withReason( + "is-annotation", + StatsLabel.Ignored, + ) + } + return fallback } @@ -102,14 +115,20 @@ class ImplicitOutputFilter( if (cn.isEnum()) { mn?.let { mn -> if (isAutoGeneratedEnumMember(mn)) { - return memberPolicy.withReason(classPolicy.reason).wrapReason("is-enum") + return memberPolicy.withReason(classPolicy.reason).wrapReason( + "is-enum", + StatsLabel.Ignored, + ) } } } // Keep (or stub) all members of annotations. if (cn.isAnnotation()) { - return memberPolicy.withReason(classPolicy.reason).wrapReason("is-annotation") + return memberPolicy.withReason(classPolicy.reason).wrapReason( + "is-annotation", + StatsLabel.Ignored, + ) } mn?.let { @@ -117,7 +136,8 @@ class ImplicitOutputFilter( // For synthetic methods (such as lambdas), let's just inherit the class's // policy. return memberPolicy.withReason(classPolicy.reason).wrapReason( - "is-synthetic-method" + "is-synthetic-method", + StatsLabel.Ignored, ) } } diff --git a/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/KeepNativeFilter.kt b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/KeepNativeFilter.kt index 00e7d77fa6e7..57309b49a2cd 100644 --- a/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/KeepNativeFilter.kt +++ b/ravenwood/tools/hoststubgen/lib/com/android/hoststubgen/filters/KeepNativeFilter.kt @@ -22,6 +22,9 @@ import com.android.hoststubgen.asm.isNative * For native methods that weren't handled by outer filters, we keep it so that * native method registration will not crash at runtime. Ideally we shouldn't need * this, but in practice unsupported native method registrations do occur. + * + * Native methods kept by this filter will all have a "Keep" policy, but they won't show + * up as "supported" in the stats dashboard beucase we set reallySupported to false. */ class KeepNativeFilter( private val classes: ClassNodes, @@ -34,7 +37,7 @@ class KeepNativeFilter( ): FilterPolicyWithReason { return classes.findMethod(className, methodName, descriptor)?.let { mn -> if (mn.isNative()) { - FilterPolicy.Keep.withReason("native-preserve") + FilterPolicy.Keep.withReason("native-preserve", StatsLabel.NotSupported) } else { null } diff --git a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt index 2edcb2a6c199..3291ff6b8bc6 100644 --- a/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt +++ b/ravenwood/tools/hoststubgen/src/com/android/hoststubgen/HostStubGen.kt @@ -31,12 +31,13 @@ import org.apache.commons.compress.archivers.zip.ZipFile class HostStubGen(val options: HostStubGenOptions) { fun run() { val errors = HostStubGenErrors() - val stats = HostStubGenStats() val inJar = ZipFile(options.inJar.get) // Load all classes. val allClasses = ClassNodes.loadClassStructures(inJar, options.inJar.get) + val stats = HostStubGenStats(allClasses) + // Dump the classes, if specified. options.inputJarDumpFile.ifSet { log.iTime("Dump file created at $it") { diff --git a/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework-dump-test.py b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework-dump-test.py index 761748265726..0c2269ab5e0d 100755 --- a/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework-dump-test.py +++ b/ravenwood/tools/hoststubgen/test-tiny-framework/tiny-framework-dump-test.py @@ -33,7 +33,7 @@ def run_diff(file1, file2): '--ignore-space-change', # Ignore the class file version. - '--ignore-matching-lines=^ *\(major\|minor\) version:$', + '--ignore-matching-lines=^ *\\(major\\|minor\\) version:$', # We shouldn't need `--ignore-matching-lines`, but somehow # the golden files were generated without these lines for b/388562869, diff --git a/services/accessibility/accessibility.aconfig b/services/accessibility/accessibility.aconfig index 35db3c6f0a6d..a133131a1d3f 100644 --- a/services/accessibility/accessibility.aconfig +++ b/services/accessibility/accessibility.aconfig @@ -223,6 +223,16 @@ flag { } flag { + name: "manager_lifecycle_user_change" + namespace: "accessibility" + description: "Use A11yManagerService's Lifecycle to change users, instead of listening for user changed events." + bug: "393626471" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { name: "motion_event_injector_cancel_fix" namespace: "accessibility" description: "Fix the ACTION_CANCEL logic used in MotionEventInjector to avoid InputDispatcher inconsistency" diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java index 703e37fad5ad..39c1fa73b7ce 100644 --- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -500,6 +500,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub mService = new AccessibilityManagerService(context); } + @VisibleForTesting + public Lifecycle(Context context, AccessibilityManagerService service) { + super(context); + mService = service; + } + @Override public void onStart() { LocalServices.addService(AccessibilityManagerInternal.class, @@ -511,17 +517,19 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub public void onBootPhase(int phase) { mService.onBootPhase(phase); } + + @Override + public void onUserSwitching(@androidx.annotation.Nullable TargetUser from, + @androidx.annotation.NonNull TargetUser to) { + super.onUserSwitching(from, to); + if (Flags.managerLifecycleUserChange()) { + mService.switchUser(to.getUserIdentifier()); + } + } } private InputManager.KeyGestureEventHandler mKeyGestureEventHandler = - new InputManager.KeyGestureEventHandler() { - @Override - public boolean handleKeyGestureEvent( - @NonNull KeyGestureEvent event, - @Nullable IBinder focusedToken) { - return AccessibilityManagerService.this.handleKeyGestureEvent(event); - } - }; + (event, focusedToken) -> AccessibilityManagerService.this.handleKeyGestureEvent(event); @VisibleForTesting AccessibilityManagerService( @@ -637,7 +645,11 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub new AccessibilityContentObserver(mMainHandler).register( mContext.getContentResolver()); if (enableTalkbackAndMagnifierKeyGestures()) { - mInputManager.registerKeyGestureEventHandler(mKeyGestureEventHandler); + List<Integer> supportedGestures = List.of( + KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MAGNIFICATION, + KeyGestureEvent.KEY_GESTURE_TYPE_ACTIVATE_SELECT_TO_SPEAK); + mInputManager.registerKeyGestureEventHandler(supportedGestures, + mKeyGestureEventHandler); } if (com.android.settingslib.flags.Flags.hearingDevicesInputRoutingControl()) { if (mHearingDeviceNotificationController != null) { @@ -686,13 +698,13 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } @VisibleForTesting - boolean handleKeyGestureEvent(KeyGestureEvent event) { + void handleKeyGestureEvent(KeyGestureEvent event) { final boolean complete = event.getAction() == KeyGestureEvent.ACTION_GESTURE_COMPLETE && !event.isCancelled(); final int gestureType = event.getKeyGestureType(); if (!complete) { - return false; + return; } String targetName; @@ -703,7 +715,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub case KeyGestureEvent.KEY_GESTURE_TYPE_ACTIVATE_SELECT_TO_SPEAK: targetName = mContext.getString(R.string.config_defaultSelectToSpeakService); if (targetName.isEmpty()) { - return false; + return; } final ComponentName targetServiceComponent = TextUtils.isEmpty(targetName) @@ -715,7 +727,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub userState.getInstalledServiceInfoLocked(targetServiceComponent); } if (accessibilityServiceInfo == null) { - return false; + return; } // Skip enabling if a warning dialog is required for the feature. @@ -725,11 +737,13 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub Slog.w(LOG_TAG, "Accessibility warning is required before this service can be " + "activated automatically via KEY_GESTURE shortcut."); - return false; + return; } break; default: - return false; + Slog.w(LOG_TAG, "Received a key gesture " + event + + " that was not registered by this handler"); + return; } List<String> shortcutTargets = getAccessibilityShortcutTargets( @@ -748,14 +762,12 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub // this will be a separate dialog that appears that requires the user to confirm // which will resolve this race condition. For now, just require two presses the // first time it is activated. - return true; + return; } final int displayId = event.getDisplayId() != INVALID_DISPLAY ? event.getDisplayId() : getLastNonProxyTopFocusedDisplayId(); performAccessibilityShortcutInternal(displayId, KEY_GESTURE, targetName); - - return true; } @Override @@ -1055,7 +1067,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub String action = intent.getAction(); if (Intent.ACTION_USER_SWITCHED.equals(action)) { - switchUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0)); + if (!Flags.managerLifecycleUserChange()) { + switchUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0)); + } } else if (Intent.ACTION_USER_UNLOCKED.equals(action)) { unlockUser(intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 0)); } else if (Intent.ACTION_USER_REMOVED.equals(action)) { diff --git a/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickController.java b/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickController.java index 0b9c45de6e40..60343e9e81e5 100644 --- a/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickController.java +++ b/services/accessibility/java/com/android/server/accessibility/autoclick/AutoclickController.java @@ -152,9 +152,20 @@ public class AutoclickController extends BaseEventStreamTransformation { if (direction == AutoclickScrollPanel.DIRECTION_EXIT) { return; } - // For direction buttons, perform scroll action immediately. - if (hovered && direction != AutoclickScrollPanel.DIRECTION_NONE) { - handleScroll(direction); + + // Handle all non-exit buttons when hovered. + if (hovered) { + // Clear the indicator. + if (mAutoclickIndicatorScheduler != null) { + mAutoclickIndicatorScheduler.cancel(); + if (mAutoclickIndicatorView != null) { + mAutoclickIndicatorView.clearIndicator(); + } + } + // Perform scroll action. + if (direction != DIRECTION_NONE) { + handleScroll(direction); + } } } }; diff --git a/services/core/java/com/android/server/am/AppStartInfoTracker.java b/services/core/java/com/android/server/am/AppStartInfoTracker.java index 517279bd7527..8b3eb48fc783 100644 --- a/services/core/java/com/android/server/am/AppStartInfoTracker.java +++ b/services/core/java/com/android/server/am/AppStartInfoTracker.java @@ -98,6 +98,9 @@ public final class AppStartInfoTracker { @VisibleForTesting static final int APP_START_INFO_HISTORY_LIST_SIZE = 16; + @VisibleForTesting + static final long APP_START_INFO_HISTORY_LENGTH_MS = TimeUnit.DAYS.toMillis(14); + /** * The max number of records that can be present in {@link mInProgressRecords}. * @@ -120,9 +123,13 @@ public final class AppStartInfoTracker { * Monotonic clock which does not reset on reboot. * * Time for offset is persisted along with records, see {@link #persistProcessStartInfo}. - * This does not follow the recommendation of {@link MonotonicClock} to persist on shutdown as - * it's ok in this case to lose any time change past the last persist as records added since - * then will be lost as well and the purpose of this clock is to keep records in order. + * This does not currently follow the recommendation of {@link MonotonicClock} to persist on + * shutdown as it's ok in this case to lose any time change past the last persist as records + * added since then will be lost as well. Since this time is used for cleanup as well, the + * potential old offset may result in the cleanup window being extended slightly beyond the + * targeted 14 days. + * + * TODO: b/402794215 - Persist on shutdown once persist performance is sufficiently improved. */ @VisibleForTesting MonotonicClock mMonotonicClock = null; @@ -296,7 +303,7 @@ public final class AppStartInfoTracker { if (!mEnabled) { return; } - ApplicationStartInfo start = new ApplicationStartInfo(getMonotonicTime()); + ApplicationStartInfo start = new ApplicationStartInfo(getMonotonicTimeMs()); start.setStartupState(ApplicationStartInfo.STARTUP_STATE_STARTED); start.setIntent(intent); start.setStartType(ApplicationStartInfo.START_TYPE_UNSET); @@ -454,7 +461,7 @@ public final class AppStartInfoTracker { if (!mEnabled) { return; } - ApplicationStartInfo start = new ApplicationStartInfo(getMonotonicTime()); + ApplicationStartInfo start = new ApplicationStartInfo(getMonotonicTimeMs()); addBaseFieldsFromProcessRecord(start, app); start.setStartupState(ApplicationStartInfo.STARTUP_STATE_STARTED); start.addStartupTimestamp( @@ -484,7 +491,7 @@ public final class AppStartInfoTracker { if (!mEnabled) { return; } - ApplicationStartInfo start = new ApplicationStartInfo(getMonotonicTime()); + ApplicationStartInfo start = new ApplicationStartInfo(getMonotonicTimeMs()); addBaseFieldsFromProcessRecord(start, app); start.setStartupState(ApplicationStartInfo.STARTUP_STATE_STARTED); start.addStartupTimestamp( @@ -511,7 +518,7 @@ public final class AppStartInfoTracker { if (!mEnabled) { return; } - ApplicationStartInfo start = new ApplicationStartInfo(getMonotonicTime()); + ApplicationStartInfo start = new ApplicationStartInfo(getMonotonicTimeMs()); addBaseFieldsFromProcessRecord(start, app); start.setStartupState(ApplicationStartInfo.STARTUP_STATE_STARTED); start.addStartupTimestamp( @@ -533,7 +540,7 @@ public final class AppStartInfoTracker { if (!mEnabled) { return; } - ApplicationStartInfo start = new ApplicationStartInfo(getMonotonicTime()); + ApplicationStartInfo start = new ApplicationStartInfo(getMonotonicTimeMs()); addBaseFieldsFromProcessRecord(start, app); start.setStartupState(ApplicationStartInfo.STARTUP_STATE_STARTED); start.addStartupTimestamp( @@ -721,8 +728,8 @@ public final class AppStartInfoTracker { Collections.sort( list, (a, b) -> - Long.compare(b.getMonoticCreationTimeMs(), - a.getMonoticCreationTimeMs())); + Long.compare(b.getMonotonicCreationTimeMs(), + a.getMonotonicCreationTimeMs())); int size = list.size(); if (maxNum > 0) { size = Math.min(size, maxNum); @@ -1098,7 +1105,7 @@ public final class AppStartInfoTracker { mLastAppStartInfoPersistTimestamp = now; } } - proto.write(AppsStartInfoProto.MONOTONIC_TIME, getMonotonicTime()); + proto.write(AppsStartInfoProto.MONOTONIC_TIME, getMonotonicTimeMs()); if (succeeded) { proto.flush(); af.finishWrite(out); @@ -1219,7 +1226,11 @@ public final class AppStartInfoTracker { } } - private long getMonotonicTime() { + /** + * Monotonic time that doesn't change with reboot or device time change for ordering records. + */ + @VisibleForTesting + public long getMonotonicTimeMs() { if (mMonotonicClock == null) { // This should never happen. Return 0 to not interfere with past or future records. return 0; @@ -1229,7 +1240,7 @@ public final class AppStartInfoTracker { /** A container class of (@link android.app.ApplicationStartInfo) */ final class AppStartInfoContainer { - private ArrayList<ApplicationStartInfo> mInfos; // Always kept sorted by first timestamp. + private ArrayList<ApplicationStartInfo> mInfos; // Always kept sorted by monotonic time. private int mMaxCapacity; private int mUid; private boolean mMonitoringModeEnabled = false; @@ -1260,9 +1271,12 @@ public final class AppStartInfoTracker { return; } - // Sort records so we can remove the least recent ones. - Collections.sort(mInfos, (a, b) -> - Long.compare(b.getMonoticCreationTimeMs(), a.getMonoticCreationTimeMs())); + if (!android.app.Flags.appStartInfoKeepRecordsSorted()) { + // Sort records so we can remove the least recent ones. + Collections.sort(mInfos, (a, b) -> + Long.compare(b.getMonotonicCreationTimeMs(), + a.getMonotonicCreationTimeMs())); + } // Remove records and trim list object back to size. mInfos.subList(0, mInfos.size() - getMaxCapacity()).clear(); @@ -1277,25 +1291,34 @@ public final class AppStartInfoTracker { @GuardedBy("mLock") void addStartInfoLocked(ApplicationStartInfo info) { - int size = mInfos.size(); - if (size >= getMaxCapacity()) { - // Remove oldest record if size is over max capacity. - int oldestIndex = -1; - long oldestTimeStamp = Long.MAX_VALUE; - for (int i = 0; i < size; i++) { - ApplicationStartInfo startInfo = mInfos.get(i); - if (startInfo.getMonoticCreationTimeMs() < oldestTimeStamp) { - oldestTimeStamp = startInfo.getMonoticCreationTimeMs(); - oldestIndex = i; - } + if (android.app.Flags.appStartInfoKeepRecordsSorted()) { + while (mInfos.size() >= getMaxCapacity()) { + // Expected to execute at most once. + mInfos.removeLast(); } - if (oldestIndex >= 0) { - mInfos.remove(oldestIndex); + mInfos.addFirst(info); + } else { + int size = mInfos.size(); + if (size >= getMaxCapacity()) { + // Remove oldest record if size is over max capacity. + int oldestIndex = -1; + long oldestTimeStamp = Long.MAX_VALUE; + for (int i = 0; i < size; i++) { + ApplicationStartInfo startInfo = mInfos.get(i); + if (startInfo.getMonotonicCreationTimeMs() < oldestTimeStamp) { + oldestTimeStamp = startInfo.getMonotonicCreationTimeMs(); + oldestIndex = i; + } + } + if (oldestIndex >= 0) { + mInfos.remove(oldestIndex); + } } + mInfos.add(info); + Collections.sort(mInfos, (a, b) -> + Long.compare(b.getMonotonicCreationTimeMs(), + a.getMonotonicCreationTimeMs())); } - mInfos.add(info); - Collections.sort(mInfos, (a, b) -> - Long.compare(b.getMonoticCreationTimeMs(), a.getMonoticCreationTimeMs())); } /** @@ -1439,9 +1462,25 @@ public final class AppStartInfoTracker { long token = proto.start(fieldId); proto.write(AppsStartInfoProto.Package.User.UID, mUid); int size = mInfos.size(); - for (int i = 0; i < size; i++) { - mInfos.get(i).writeToProto(proto, AppsStartInfoProto.Package.User.APP_START_INFO, - byteArrayOutputStream, objectOutputStream, typedXmlSerializer); + if (android.app.Flags.appStartInfoCleanupOldRecords()) { + long removeOlderThan = getMonotonicTimeMs() - APP_START_INFO_HISTORY_LENGTH_MS; + // Iterate backwards so we can remove old records as we go. + for (int i = size - 1; i >= 0; i--) { + if (mInfos.get(i).getMonotonicCreationTimeMs() < removeOlderThan) { + // Remove the record. + mInfos.remove(i); + } else { + mInfos.get(i).writeToProto( + proto, AppsStartInfoProto.Package.User.APP_START_INFO, + byteArrayOutputStream, objectOutputStream, typedXmlSerializer); + } + } + } else { + for (int i = 0; i < size; i++) { + mInfos.get(i).writeToProto( + proto, AppsStartInfoProto.Package.User.APP_START_INFO, + byteArrayOutputStream, objectOutputStream, typedXmlSerializer); + } } proto.write(AppsStartInfoProto.Package.User.MONITORING_ENABLED, mMonitoringModeEnabled); proto.end(token); @@ -1466,7 +1505,13 @@ public final class AppStartInfoTracker { info.readFromProto(proto, AppsStartInfoProto.Package.User.APP_START_INFO, byteArrayInputStream, objectInputStream, typedXmlPullParser); info.setPackageName(packageName); - mInfos.add(info); + if (android.app.Flags.appStartInfoKeepRecordsSorted()) { + // Since the writes are done from oldest to newest, each additional + // record will be newer than the previous so use addFirst. + mInfos.addFirst(info); + } else { + mInfos.add(info); + } break; case (int) AppsStartInfoProto.Package.User.MONITORING_ENABLED: mMonitoringModeEnabled = proto.readBoolean( diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index a61368c4bc36..ad47e67b9332 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -793,6 +793,7 @@ public final class ProcessList { final ProcessMap<ProcessRecord> mDyingProcesses = new ProcessMap<>(); // Self locked with the inner lock within the RemoteCallbackList + @GuardedBy("mProcessObservers") private final RemoteCallbackList<IProcessObserver> mProcessObservers = new RemoteCallbackList<>(); @@ -4980,11 +4981,15 @@ public final class ProcessList { } void registerProcessObserver(IProcessObserver observer) { - mProcessObservers.register(observer); + synchronized (mProcessObservers) { + mProcessObservers.register(observer); + } } void unregisterProcessObserver(IProcessObserver observer) { - mProcessObservers.unregister(observer); + synchronized (mProcessObservers) { + mProcessObservers.unregister(observer); + } } void dispatchProcessesChanged() { @@ -5002,38 +5007,41 @@ public final class ProcessList { } } - int i = mProcessObservers.beginBroadcast(); - while (i > 0) { - i--; - final IProcessObserver observer = mProcessObservers.getBroadcastItem(i); - if (observer != null) { - try { - for (int j = 0; j < numOfChanges; j++) { - ProcessChangeItem item = mActiveProcessChanges[j]; - if ((item.changes & ProcessChangeItem.CHANGE_ACTIVITIES) != 0) { - if (DEBUG_PROCESS_OBSERVERS) { - Slog.i(TAG_PROCESS_OBSERVERS, - "ACTIVITIES CHANGED pid=" + item.pid + " uid=" - + item.uid + ": " + item.foregroundActivities); + synchronized (mProcessObservers) { + int i = mProcessObservers.beginBroadcast(); + while (i > 0) { + i--; + final IProcessObserver observer = mProcessObservers.getBroadcastItem(i); + if (observer != null) { + try { + for (int j = 0; j < numOfChanges; j++) { + ProcessChangeItem item = mActiveProcessChanges[j]; + if ((item.changes & ProcessChangeItem.CHANGE_ACTIVITIES) != 0) { + if (DEBUG_PROCESS_OBSERVERS) { + Slog.i(TAG_PROCESS_OBSERVERS, + "ACTIVITIES CHANGED pid=" + item.pid + " uid=" + + item.uid + ": " + item.foregroundActivities); + } + observer.onForegroundActivitiesChanged(item.pid, item.uid, + item.foregroundActivities); } - observer.onForegroundActivitiesChanged(item.pid, item.uid, - item.foregroundActivities); - } - if ((item.changes & ProcessChangeItem.CHANGE_FOREGROUND_SERVICES) != 0) { - if (DEBUG_PROCESS_OBSERVERS) { - Slog.i(TAG_PROCESS_OBSERVERS, - "FOREGROUND SERVICES CHANGED pid=" + item.pid + " uid=" - + item.uid + ": " + item.foregroundServiceTypes); + if ((item.changes & ProcessChangeItem.CHANGE_FOREGROUND_SERVICES) + != 0) { + if (DEBUG_PROCESS_OBSERVERS) { + Slog.i(TAG_PROCESS_OBSERVERS, + "FOREGROUND SERVICES CHANGED pid=" + item.pid + " uid=" + + item.uid + ": " + item.foregroundServiceTypes); + } + observer.onForegroundServicesChanged(item.pid, item.uid, + item.foregroundServiceTypes); } - observer.onForegroundServicesChanged(item.pid, item.uid, - item.foregroundServiceTypes); } + } catch (RemoteException e) { } - } catch (RemoteException e) { } } + mProcessObservers.finishBroadcast(); } - mProcessObservers.finishBroadcast(); synchronized (mProcessChangeLock) { for (int j = 0; j < numOfChanges; j++) { @@ -5122,22 +5130,42 @@ public final class ProcessList { } void dispatchProcessStarted(ProcessRecord app, int pid) { - // TODO(b/323959187) Add the implementation. + if (!android.app.Flags.enableProcessObserverBroadcastOnProcessStarted()) { + Slog.i(TAG, "ProcessObserver broadcast disabled"); + return; + } + synchronized (mProcessObservers) { + int i = mProcessObservers.beginBroadcast(); + while (i > 0) { + i--; + final IProcessObserver observer = mProcessObservers.getBroadcastItem(i); + if (observer != null) { + try { + observer.onProcessStarted(pid, app.uid, app.info.uid, + app.info.packageName, app.processName); + } catch (RemoteException e) { + } + } + } + mProcessObservers.finishBroadcast(); + } } void dispatchProcessDied(int pid, int uid) { - int i = mProcessObservers.beginBroadcast(); - while (i > 0) { - i--; - final IProcessObserver observer = mProcessObservers.getBroadcastItem(i); - if (observer != null) { - try { - observer.onProcessDied(pid, uid); - } catch (RemoteException e) { + synchronized (mProcessObservers) { + int i = mProcessObservers.beginBroadcast(); + while (i > 0) { + i--; + final IProcessObserver observer = mProcessObservers.getBroadcastItem(i); + if (observer != null) { + try { + observer.onProcessDied(pid, uid); + } catch (RemoteException e) { + } } } + mProcessObservers.finishBroadcast(); } - mProcessObservers.finishBroadcast(); } @GuardedBy(anyOf = {"mService", "mProcLock"}) diff --git a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java index cb4342f27bc8..c2ed4d557e69 100644 --- a/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java +++ b/services/core/java/com/android/server/am/SettingsToPropertiesMapper.java @@ -256,6 +256,7 @@ public class SettingsToPropertiesMapper { "pixel_vpn", "pixel_watch", "pixel_watch_debug_trace", + "pixel_watch_watchfaces", "pixel_wifi", "platform_compat", "platform_security", diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index a28069bbf050..95e58e1a7300 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -684,8 +684,9 @@ public final class DisplayManagerService extends SystemService { final var backupManager = new BackupManager(mContext); Consumer<Pair<DisplayTopology, DisplayTopologyGraph>> topologyChangedCallback = update -> { - if (mInputManagerInternal != null) { - mInputManagerInternal.setDisplayTopology(update.second); + DisplayTopologyGraph graph = update.second; + if (mInputManagerInternal != null && graph != null) { + mInputManagerInternal.setDisplayTopology(graph); } deliverTopologyUpdate(update.first); }; @@ -3647,7 +3648,7 @@ public final class DisplayManagerService extends SystemService { private void deliverTopologyUpdate(DisplayTopology topology) { if (DEBUG) { - Slog.d(TAG, "Delivering topology update"); + Slog.d(TAG, "Delivering topology update: " + topology); } if (Trace.isTagEnabled(Trace.TRACE_TAG_POWER)) { Trace.instant(Trace.TRACE_TAG_POWER, "deliverTopologyUpdate"); @@ -4209,13 +4210,18 @@ public final class DisplayManagerService extends SystemService { public boolean mWifiDisplayScanRequested; - // A single pending event. + // A single pending display event. private record Event(int displayId, @DisplayEvent int event) { }; - // The list of pending events. This is null until there is a pending event to be saved. - // This is only used if {@link deferDisplayEventsWhenFrozen()} is true. + // The list of pending display events. This is null until there is a pending event to be + // saved. This is only used if {@link deferDisplayEventsWhenFrozen()} is true. + @GuardedBy("mCallback") + @Nullable + private ArrayList<Event> mPendingDisplayEvents; + @GuardedBy("mCallback") - private ArrayList<Event> mPendingEvents; + @Nullable + private DisplayTopology mPendingTopology; // Process states: a process is ready to receive events if it is neither cached nor // frozen. @@ -4285,7 +4291,10 @@ public final class DisplayManagerService extends SystemService { */ @GuardedBy("mCallback") private boolean hasPendingAndIsReadyLocked() { - return isReadyLocked() && mPendingEvents != null && !mPendingEvents.isEmpty() && mAlive; + boolean pendingDisplayEvents = mPendingDisplayEvents != null + && !mPendingDisplayEvents.isEmpty(); + boolean pendingTopology = mPendingTopology != null; + return isReadyLocked() && (pendingDisplayEvents || pendingTopology) && mAlive; } /** @@ -4366,7 +4375,8 @@ public final class DisplayManagerService extends SystemService { // occurs as the client is transitioning to ready but pending events have not // been dispatched. The new event must be added to the pending list to // preserve event ordering. - if (!isReadyLocked() || (mPendingEvents != null && !mPendingEvents.isEmpty())) { + if (!isReadyLocked() || (mPendingDisplayEvents != null + && !mPendingDisplayEvents.isEmpty())) { // The client is interested in the event but is not ready to receive it. // Put the event on the pending list. addDisplayEvent(displayId, event); @@ -4453,13 +4463,13 @@ public final class DisplayManagerService extends SystemService { // This is only used if {@link deferDisplayEventsWhenFrozen()} is true. @GuardedBy("mCallback") private void addDisplayEvent(int displayId, int event) { - if (mPendingEvents == null) { - mPendingEvents = new ArrayList<>(); + if (mPendingDisplayEvents == null) { + mPendingDisplayEvents = new ArrayList<>(); } - if (!mPendingEvents.isEmpty()) { + if (!mPendingDisplayEvents.isEmpty()) { // Ignore redundant events. Further optimization is possible by merging adjacent // events. - Event last = mPendingEvents.get(mPendingEvents.size() - 1); + Event last = mPendingDisplayEvents.get(mPendingDisplayEvents.size() - 1); if (last.displayId == displayId && last.event == event) { if (DEBUG) { Slog.d(TAG, "Ignore redundant display event " + displayId + "/" + event @@ -4468,12 +4478,13 @@ public final class DisplayManagerService extends SystemService { return; } } - mPendingEvents.add(new Event(displayId, event)); + mPendingDisplayEvents.add(new Event(displayId, event)); } /** * @return {@code false} if RemoteException happens; otherwise {@code true} for - * success. + * success. This returns true even if the update was deferred because the remote client is + * cached or frozen. */ boolean notifyTopologyUpdateAsync(DisplayTopology topology) { if ((mInternalEventFlagsMask.get() @@ -4490,6 +4501,18 @@ public final class DisplayManagerService extends SystemService { // The client is not interested in this event, so do nothing. return true; } + + if (deferDisplayEventsWhenFrozen()) { + synchronized (mCallback) { + // Save the new update if the client frozen or cached (not ready). + if (!isReadyLocked()) { + // The client is interested in the update but is not ready to receive it. + mPendingTopology = topology; + return true; + } + } + } + return transmitTopologyUpdate(topology); } @@ -4514,37 +4537,54 @@ public final class DisplayManagerService extends SystemService { // would be unusual to do so. The method returns true on success. // This is only used if {@link deferDisplayEventsWhenFrozen()} is true. public boolean dispatchPending() { - Event[] pending; + Event[] pendingDisplayEvents = null; + DisplayTopology pendingTopology; synchronized (mCallback) { - if (mPendingEvents == null || mPendingEvents.isEmpty() || !mAlive) { + if (!mAlive) { return true; } if (!isReadyLocked()) { return false; } - pending = new Event[mPendingEvents.size()]; - pending = mPendingEvents.toArray(pending); - mPendingEvents.clear(); + + if (mPendingDisplayEvents != null && !mPendingDisplayEvents.isEmpty()) { + pendingDisplayEvents = new Event[mPendingDisplayEvents.size()]; + pendingDisplayEvents = mPendingDisplayEvents.toArray(pendingDisplayEvents); + mPendingDisplayEvents.clear(); + } + + pendingTopology = mPendingTopology; + mPendingTopology = null; } try { - for (int i = 0; i < pending.length; i++) { - Event displayEvent = pending[i]; - if (DEBUG) { - Slog.d(TAG, "Send pending display event #" + i + " " - + displayEvent.displayId + "/" - + displayEvent.event + " to " + mUid + "/" + mPid); - } + if (pendingDisplayEvents != null) { + for (int i = 0; i < pendingDisplayEvents.length; i++) { + Event displayEvent = pendingDisplayEvents[i]; + if (DEBUG) { + Slog.d(TAG, "Send pending display event #" + i + " " + + displayEvent.displayId + "/" + + displayEvent.event + " to " + mUid + "/" + mPid); + } + + if (!shouldReceiveRefreshRateWithChangeUpdate(displayEvent.event)) { + continue; + } - if (!shouldReceiveRefreshRateWithChangeUpdate(displayEvent.event)) { - continue; + transmitDisplayEvent(displayEvent.displayId, displayEvent.event); } + } - transmitDisplayEvent(displayEvent.displayId, displayEvent.event); + if (pendingTopology != null) { + if (DEBUG) { + Slog.d(TAG, "Send pending topology: " + pendingTopology + + " to " + mUid + "/" + mPid); + } + mCallback.onTopologyChanged(pendingTopology); } + return true; } catch (RemoteException ex) { - Slog.w(TAG, "Failed to notify process " - + mPid + " that display topology changed, assuming it died.", ex); + Slog.w(TAG, "Failed to notify process " + mPid + ", assuming it died.", ex); binderDied(); return false; @@ -4556,11 +4596,12 @@ public final class DisplayManagerService extends SystemService { if (deferDisplayEventsWhenFrozen()) { final String fmt = "mPid=%d mUid=%d mWifiDisplayScanRequested=%s" - + " cached=%s frozen=%s pending=%d"; + + " cached=%s frozen=%s pendingDisplayEvents=%d pendingTopology=%b"; synchronized (mCallback) { return formatSimple(fmt, mPid, mUid, mWifiDisplayScanRequested, mCached, mFrozen, - (mPendingEvents == null) ? 0 : mPendingEvents.size()); + (mPendingDisplayEvents == null) ? 0 : mPendingDisplayEvents.size(), + mPendingTopology != null); } } else { final String fmt = diff --git a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java index c37733b05fba..2c90e1919123 100644 --- a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java +++ b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java @@ -156,6 +156,8 @@ public class DisplayModeDirector { private SparseArray<Display.Mode> mDefaultModeByDisplay; // a map from display id to display device config private SparseArray<DisplayDeviceConfig> mDisplayDeviceConfigByDisplay = new SparseArray<>(); + // set containing connected external display ids + private final Set<Integer> mExternalDisplaysConnected = new HashSet<>(); private SparseBooleanArray mHasArrSupport; @@ -425,7 +427,7 @@ public class DisplayModeDirector { // Some external displays physical refresh rate modes are slightly above 60hz. // SurfaceFlinger will not enable these display modes unless it is configured to allow // render rate at least at this frame rate. - if (mDisplayObserver.isExternalDisplayLocked(displayId)) { + if (isExternalDisplayLocked(displayId)) { primarySummary.maxRenderFrameRate = Math.max(baseMode.getRefreshRate(), primarySummary.maxRenderFrameRate); appRequestSummary.maxRenderFrameRate = Math.max(baseMode.getRefreshRate(), @@ -653,6 +655,10 @@ public class DisplayModeDirector { } } + boolean isExternalDisplayLocked(int displayId) { + return mExternalDisplaysConnected.contains(displayId); + } + private static String switchingTypeToString(@DisplayManager.SwitchingType int type) { switch (type) { case DisplayManager.SWITCHING_TYPE_NONE: @@ -694,6 +700,11 @@ public class DisplayModeDirector { } @VisibleForTesting + void addExternalDisplayId(int externalDisplayId) { + mExternalDisplaysConnected.add(externalDisplayId); + } + + @VisibleForTesting void injectBrightnessObserver(BrightnessObserver brightnessObserver) { mBrightnessObserver = brightnessObserver; } @@ -1210,7 +1221,7 @@ public class DisplayModeDirector { @GuardedBy("mLock") private void updateRefreshRateSettingLocked(float minRefreshRate, float peakRefreshRate, float defaultRefreshRate, int displayId) { - if (mDisplayObserver.isExternalDisplayLocked(displayId)) { + if (isExternalDisplayLocked(displayId)) { if (mLoggingEnabled) { Slog.d(TAG, "skip updateRefreshRateSettingLocked for external display " + displayId); @@ -1309,20 +1320,25 @@ public class DisplayModeDirector { public void setAppRequest(int displayId, int modeId, float requestedRefreshRate, float requestedMinRefreshRateRange, float requestedMaxRefreshRateRange) { Display.Mode requestedMode; + boolean isExternalDisplay; synchronized (mLock) { requestedMode = findModeLocked(displayId, modeId, requestedRefreshRate); + isExternalDisplay = isExternalDisplayLocked(displayId); } Vote frameRateVote = getFrameRateVote( requestedMinRefreshRateRange, requestedMaxRefreshRateRange); Vote baseModeRefreshRateVote = getBaseModeVote(requestedMode, requestedRefreshRate); - Vote sizeVote = getSizeVote(requestedMode); mVotesStorage.updateVote(displayId, Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE, frameRateVote); mVotesStorage.updateVote(displayId, Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE, baseModeRefreshRateVote); - mVotesStorage.updateVote(displayId, Vote.PRIORITY_APP_REQUEST_SIZE, sizeVote); + + if (!isExternalDisplay) { + Vote sizeVote = getSizeVote(requestedMode); + mVotesStorage.updateVote(displayId, Vote.PRIORITY_APP_REQUEST_SIZE, sizeVote); + } } private Display.Mode findModeLocked(int displayId, int modeId, float requestedRefreshRate) { @@ -1420,7 +1436,6 @@ public class DisplayModeDirector { private int mExternalDisplayPeakHeight; private int mExternalDisplayPeakRefreshRate; private final boolean mRefreshRateSynchronizationEnabled; - private final Set<Integer> mExternalDisplaysConnected = new HashSet<>(); DisplayObserver(Context context, Handler handler, VotesStorage votesStorage, Injector injector) { @@ -1541,10 +1556,6 @@ public class DisplayModeDirector { } } - boolean isExternalDisplayLocked(int displayId) { - return mExternalDisplaysConnected.contains(displayId); - } - @Nullable private DisplayInfo getDisplayInfo(int displayId) { DisplayInfo info = new DisplayInfo(); diff --git a/services/core/java/com/android/server/display/mode/ModeChangeObserver.java b/services/core/java/com/android/server/display/mode/ModeChangeObserver.java index 50782a2f22c8..debfb067b710 100644 --- a/services/core/java/com/android/server/display/mode/ModeChangeObserver.java +++ b/services/core/java/com/android/server/display/mode/ModeChangeObserver.java @@ -25,6 +25,8 @@ import android.view.Display; import android.view.DisplayAddress; import android.view.DisplayEventReceiver; +import androidx.annotation.VisibleForTesting; + import java.util.HashSet; import java.util.Set; @@ -35,8 +37,10 @@ final class ModeChangeObserver { private final DisplayModeDirector.Injector mInjector; @SuppressWarnings("unused") - private DisplayEventReceiver mModeChangeListener; - private DisplayManager.DisplayListener mDisplayListener; + @VisibleForTesting + DisplayEventReceiver mModeChangeListener; + @VisibleForTesting + DisplayManager.DisplayListener mDisplayListener; private final LongSparseArray<Set<Integer>> mRejectedModesMap = new LongSparseArray<>(); private final LongSparseArray<Integer> mPhysicalIdToLogicalIdMap = new LongSparseArray<>(); diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java index 41b0b4dc716a..a2d065400045 100644 --- a/services/core/java/com/android/server/hdmi/HdmiControlService.java +++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java @@ -252,17 +252,22 @@ public class HdmiControlService extends SystemService { static final AudioDeviceAttributes AUDIO_OUTPUT_DEVICE_HDMI_EARC = new AudioDeviceAttributes(AudioDeviceAttributes.ROLE_OUTPUT, AudioDeviceInfo.TYPE_HDMI_EARC, ""); + static final AudioDeviceAttributes AUDIO_OUTPUT_DEVICE_LINE_DIGITAL = + new AudioDeviceAttributes(AudioDeviceAttributes.ROLE_OUTPUT, + AudioDeviceInfo.TYPE_LINE_DIGITAL, ""); // Audio output devices used for absolute volume behavior private static final List<AudioDeviceAttributes> AVB_AUDIO_OUTPUT_DEVICES = List.of(AUDIO_OUTPUT_DEVICE_HDMI, AUDIO_OUTPUT_DEVICE_HDMI_ARC, - AUDIO_OUTPUT_DEVICE_HDMI_EARC); + AUDIO_OUTPUT_DEVICE_HDMI_EARC, + AUDIO_OUTPUT_DEVICE_LINE_DIGITAL); // Audio output devices used for absolute volume behavior on TV panels private static final List<AudioDeviceAttributes> TV_AVB_AUDIO_OUTPUT_DEVICES = List.of(AUDIO_OUTPUT_DEVICE_HDMI_ARC, - AUDIO_OUTPUT_DEVICE_HDMI_EARC); + AUDIO_OUTPUT_DEVICE_HDMI_EARC, + AUDIO_OUTPUT_DEVICE_LINE_DIGITAL); // Audio output devices used for absolute volume behavior on Playback devices private static final List<AudioDeviceAttributes> PLAYBACK_AVB_AUDIO_OUTPUT_DEVICES = diff --git a/services/core/java/com/android/server/input/AppLaunchShortcutManager.java b/services/core/java/com/android/server/input/AppLaunchShortcutManager.java index 8c028bc92841..eb102294ac32 100644 --- a/services/core/java/com/android/server/input/AppLaunchShortcutManager.java +++ b/services/core/java/com/android/server/input/AppLaunchShortcutManager.java @@ -111,7 +111,7 @@ final class AppLaunchShortcutManager { mContext = context; } - public void systemRunning() { + public void init() { loadShortcuts(); } diff --git a/services/core/java/com/android/server/input/InputGestureManager.java b/services/core/java/com/android/server/input/InputGestureManager.java index 67e1ccc6a850..e6d71900f106 100644 --- a/services/core/java/com/android/server/input/InputGestureManager.java +++ b/services/core/java/com/android/server/input/InputGestureManager.java @@ -94,9 +94,9 @@ final class InputGestureManager { mContext = context; } - public void systemRunning() { + public void init(List<InputGestureData> bookmarks) { initSystemShortcuts(); - blockListBookmarkedTriggers(); + blockListBookmarkedTriggers(bookmarks); } private void initSystemShortcuts() { @@ -263,10 +263,9 @@ final class InputGestureManager { } } - private void blockListBookmarkedTriggers() { + private void blockListBookmarkedTriggers(List<InputGestureData> bookmarks) { synchronized (mGestureLock) { - InputManager im = Objects.requireNonNull(mContext.getSystemService(InputManager.class)); - for (InputGestureData bookmark : im.getAppLaunchBookmarks()) { + for (InputGestureData bookmark : bookmarks) { mBlockListedTriggers.add(bookmark.getTrigger()); } } diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index 6e6d00d62819..29e04e744759 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -2751,18 +2751,23 @@ public class InputManagerService extends IInputManager.Stub @SuppressLint("MissingPermission") private void initKeyGestures() { InputManager im = Objects.requireNonNull(mContext.getSystemService(InputManager.class)); - im.registerKeyGestureEventHandler(new InputManager.KeyGestureEventHandler() { - @Override - public boolean handleKeyGestureEvent(@NonNull KeyGestureEvent event, - @Nullable IBinder focussedToken) { - return InputManagerService.this.handleKeyGestureEvent(event); - } - }); + List<Integer> supportedGestures = List.of( + KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_UP, + KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_DOWN, + KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_TOGGLE, + KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK, + KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_BOUNCE_KEYS, + KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MOUSE_KEYS, + KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_STICKY_KEYS, + KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_SLOW_KEYS + ); + im.registerKeyGestureEventHandler(supportedGestures, + (event, focusedToken) -> InputManagerService.this.handleKeyGestureEvent(event)); } @SuppressLint("MissingPermission") @VisibleForTesting - boolean handleKeyGestureEvent(@NonNull KeyGestureEvent event) { + void handleKeyGestureEvent(@NonNull KeyGestureEvent event) { int deviceId = event.getDeviceId(); boolean complete = event.getAction() == KeyGestureEvent.ACTION_GESTURE_COMPLETE && !event.isCancelled(); @@ -2771,20 +2776,20 @@ public class InputManagerService extends IInputManager.Stub if (complete) { mKeyboardBacklightController.incrementKeyboardBacklight(deviceId); } - return true; + break; case KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_DOWN: if (complete) { mKeyboardBacklightController.decrementKeyboardBacklight(deviceId); } - return true; + break; case KeyGestureEvent.KEY_GESTURE_TYPE_KEYBOARD_BACKLIGHT_TOGGLE: // TODO(b/367748270): Add functionality to turn keyboard backlight on/off. - return true; + break; case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK: if (complete) { mNative.toggleCapsLock(deviceId); } - return true; + break; case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_BOUNCE_KEYS: if (complete) { final boolean bounceKeysEnabled = @@ -2792,7 +2797,6 @@ public class InputManagerService extends IInputManager.Stub InputSettings.setAccessibilityBounceKeysThreshold(mContext, bounceKeysEnabled ? 0 : InputSettings.DEFAULT_BOUNCE_KEYS_THRESHOLD_MILLIS); - return true; } break; case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_MOUSE_KEYS: @@ -2800,7 +2804,6 @@ public class InputManagerService extends IInputManager.Stub final boolean mouseKeysEnabled = InputSettings.isAccessibilityMouseKeysEnabled( mContext); InputSettings.setAccessibilityMouseKeysEnabled(mContext, !mouseKeysEnabled); - return true; } break; case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_STICKY_KEYS: @@ -2808,7 +2811,6 @@ public class InputManagerService extends IInputManager.Stub final boolean stickyKeysEnabled = InputSettings.isAccessibilityStickyKeysEnabled(mContext); InputSettings.setAccessibilityStickyKeysEnabled(mContext, !stickyKeysEnabled); - return true; } break; case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_SLOW_KEYS: @@ -2817,14 +2819,13 @@ public class InputManagerService extends IInputManager.Stub InputSettings.isAccessibilitySlowKeysEnabled(mContext); InputSettings.setAccessibilitySlowKeysThreshold(mContext, slowKeysEnabled ? 0 : InputSettings.DEFAULT_SLOW_KEYS_THRESHOLD_MILLIS); - return true; } break; default: - return false; - + Log.w(TAG, "Received a key gesture " + event + + " that was not registered by this handler"); + break; } - return false; } // Native callback. @@ -3147,11 +3148,14 @@ public class InputManagerService extends IInputManager.Stub @Override @PermissionManuallyEnforced - public void registerKeyGestureHandler(@NonNull IKeyGestureHandler handler) { + public void registerKeyGestureHandler(int[] keyGesturesToHandle, + @NonNull IKeyGestureHandler handler) { enforceManageKeyGesturePermission(); Objects.requireNonNull(handler); - mKeyGestureController.registerKeyGestureHandler(handler, Binder.getCallingPid()); + Objects.requireNonNull(keyGesturesToHandle); + mKeyGestureController.registerKeyGestureHandler(keyGesturesToHandle, handler, + Binder.getCallingPid()); } @Override diff --git a/services/core/java/com/android/server/input/KeyGestureController.java b/services/core/java/com/android/server/input/KeyGestureController.java index 395c77322c04..5de432e5849b 100644 --- a/services/core/java/com/android/server/input/KeyGestureController.java +++ b/services/core/java/com/android/server/input/KeyGestureController.java @@ -58,6 +58,7 @@ import android.util.IndentingPrintWriter; import android.util.Log; import android.util.Slog; import android.util.SparseArray; +import android.util.SparseIntArray; import android.view.Display; import android.view.InputDevice; import android.view.KeyCharacterMap; @@ -79,11 +80,11 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.ArrayDeque; +import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Objects; import java.util.Set; -import java.util.TreeMap; /** * A thread-safe component of {@link InputManagerService} responsible for managing callbacks when a @@ -166,11 +167,14 @@ final class KeyGestureController { private final SparseArray<KeyGestureEventListenerRecord> mKeyGestureEventListenerRecords = new SparseArray<>(); - // List of currently registered key gesture event handler keyed by process pid. The map sorts - // in the order of preference of the handlers, and we prioritize handlers in system server - // over external handlers.. + // Map of currently registered key gesture event handlers keyed by pid. @GuardedBy("mKeyGestureHandlerRecords") - private final TreeMap<Integer, KeyGestureHandlerRecord> mKeyGestureHandlerRecords; + private final SparseArray<KeyGestureHandlerRecord> mKeyGestureHandlerRecords = + new SparseArray<>(); + + // Currently supported key gestures mapped to pid that registered the corresponding handler. + @GuardedBy("mKeyGestureHandlerRecords") + private final SparseIntArray mSupportedKeyGestureToPidMap = new SparseIntArray(); private final ArrayDeque<KeyGestureEvent> mLastHandledEvents = new ArrayDeque<>(); @@ -193,18 +197,6 @@ final class KeyGestureController { mHandler = new Handler(looper, this::handleMessage); mIoHandler = new Handler(ioLooper, this::handleIoMessage); mSystemPid = Process.myPid(); - mKeyGestureHandlerRecords = new TreeMap<>((p1, p2) -> { - if (Objects.equals(p1, p2)) { - return 0; - } - if (p1 == mSystemPid) { - return -1; - } else if (p2 == mSystemPid) { - return 1; - } else { - return Integer.compare(p1, p2); - } - }); mKeyCombinationManager = new KeyCombinationManager(mHandler); mSettingsObserver = new SettingsObserver(mHandler); mAppLaunchShortcutManager = new AppLaunchShortcutManager(mContext); @@ -450,8 +442,8 @@ final class KeyGestureController { public void systemRunning() { mSettingsObserver.observe(); - mAppLaunchShortcutManager.systemRunning(); - mInputGestureManager.systemRunning(); + mAppLaunchShortcutManager.init(); + mInputGestureManager.init(mAppLaunchShortcutManager.getBookmarks()); initKeyGestures(); int userId; @@ -465,22 +457,24 @@ final class KeyGestureController { @SuppressLint("MissingPermission") private void initKeyGestures() { InputManager im = Objects.requireNonNull(mContext.getSystemService(InputManager.class)); - im.registerKeyGestureEventHandler((event, focusedToken) -> { - switch (event.getKeyGestureType()) { - case KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT_CHORD: - if (event.getAction() == KeyGestureEvent.ACTION_GESTURE_START) { - mHandler.removeMessages(MSG_ACCESSIBILITY_SHORTCUT); - mHandler.sendMessageDelayed( - mHandler.obtainMessage(MSG_ACCESSIBILITY_SHORTCUT), - getAccessibilityShortcutTimeout()); + im.registerKeyGestureEventHandler( + List.of(KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT_CHORD), + (event, focusedToken) -> { + if (event.getKeyGestureType() + == KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT_CHORD) { + if (event.getAction() == KeyGestureEvent.ACTION_GESTURE_START) { + mHandler.removeMessages(MSG_ACCESSIBILITY_SHORTCUT); + mHandler.sendMessageDelayed( + mHandler.obtainMessage(MSG_ACCESSIBILITY_SHORTCUT), + getAccessibilityShortcutTimeout()); + } else { + mHandler.removeMessages(MSG_ACCESSIBILITY_SHORTCUT); + } } else { - mHandler.removeMessages(MSG_ACCESSIBILITY_SHORTCUT); + Log.w(TAG, "Received a key gesture " + event + + " that was not registered by this handler"); } - return true; - default: - return false; - } - }); + }); } public boolean interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) { @@ -590,10 +584,11 @@ final class KeyGestureController { return true; } if (result.appLaunchData() != null) { - return handleKeyGesture(deviceId, new int[]{keyCode}, metaState, + handleKeyGesture(deviceId, new int[]{keyCode}, metaState, KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION, - KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId, - focusedToken, /* flags = */0, result.appLaunchData()); + KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId, focusedToken, /* flags = */ + 0, result.appLaunchData()); + return true; } // Handle system shortcuts @@ -601,11 +596,11 @@ final class KeyGestureController { InputGestureData systemShortcut = mInputGestureManager.getSystemShortcutForKeyEvent( event); if (systemShortcut != null) { - return handleKeyGesture(deviceId, new int[]{keyCode}, metaState, + handleKeyGesture(deviceId, new int[]{keyCode}, metaState, systemShortcut.getAction().keyGestureType(), - KeyGestureEvent.ACTION_GESTURE_COMPLETE, - displayId, focusedToken, /* flags = */0, - systemShortcut.getAction().appLaunchData()); + KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId, + focusedToken, /* flags = */0, systemShortcut.getAction().appLaunchData()); + return true; } } @@ -687,11 +682,11 @@ final class KeyGestureController { return true; case KeyEvent.KEYCODE_SEARCH: if (firstDown && mSearchKeyBehavior == SEARCH_KEY_BEHAVIOR_TARGET_ACTIVITY) { - return handleKeyGesture(deviceId, new int[]{keyCode}, /* modifierState = */0, + handleKeyGesture(deviceId, new int[]{keyCode}, /* modifierState = */0, KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SEARCH, KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId, focusedToken, /* flags = */0, /* appLaunchData = */null); - + return true; } break; case KeyEvent.KEYCODE_SETTINGS: @@ -782,11 +777,12 @@ final class KeyGestureController { if (KeyEvent.metaStateHasModifiers( shiftlessModifiers, KeyEvent.META_ALT_ON)) { mPendingHideRecentSwitcher = true; - return handleKeyGesture(deviceId, new int[]{keyCode}, + handleKeyGesture(deviceId, new int[]{keyCode}, KeyEvent.META_ALT_ON, KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER, KeyGestureEvent.ACTION_GESTURE_START, displayId, focusedToken, /* flags = */0, /* appLaunchData = */null); + return true; } } } @@ -803,21 +799,23 @@ final class KeyGestureController { } else { if (mPendingHideRecentSwitcher) { mPendingHideRecentSwitcher = false; - return handleKeyGesture(deviceId, new int[]{KeyEvent.KEYCODE_TAB}, + handleKeyGesture(deviceId, new int[]{KeyEvent.KEYCODE_TAB}, KeyEvent.META_ALT_ON, KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER, KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId, focusedToken, /* flags = */0, /* appLaunchData = */null); + return true; } // Toggle Caps Lock on META-ALT. if (mPendingCapsLockToggle) { mPendingCapsLockToggle = false; - return handleKeyGesture(deviceId, new int[]{KeyEvent.KEYCODE_META_LEFT, + handleKeyGesture(deviceId, new int[]{KeyEvent.KEYCODE_META_LEFT, KeyEvent.KEYCODE_ALT_LEFT}, /* modifierState = */0, KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_CAPS_LOCK, KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId, focusedToken, /* flags = */0, /* appLaunchData = */null); + return true; } } break; @@ -885,11 +883,11 @@ final class KeyGestureController { if (customGesture == null) { return false; } - return handleKeyGesture(deviceId, new int[]{keyCode}, metaState, + handleKeyGesture(deviceId, new int[]{keyCode}, metaState, customGesture.getAction().keyGestureType(), - KeyGestureEvent.ACTION_GESTURE_COMPLETE, - displayId, focusedToken, /* flags = */0, - customGesture.getAction().appLaunchData()); + KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId, focusedToken, + /* flags = */0, customGesture.getAction().appLaunchData()); + return true; } return false; } @@ -908,7 +906,7 @@ final class KeyGestureController { // Handle keyboard layout switching. (CTRL + SPACE) if (KeyEvent.metaStateHasModifiers(metaState & ~KeyEvent.META_SHIFT_MASK, KeyEvent.META_CTRL_ON)) { - return handleKeyGesture(deviceId, new int[]{keyCode}, + handleKeyGesture(deviceId, new int[]{keyCode}, KeyEvent.META_CTRL_ON | (event.isShiftPressed() ? KeyEvent.META_SHIFT_ON : 0), KeyGestureEvent.KEY_GESTURE_TYPE_LANGUAGE_SWITCH, @@ -921,7 +919,7 @@ final class KeyGestureController { if (down && KeyEvent.metaStateHasModifiers(metaState, KeyEvent.META_CTRL_ON | KeyEvent.META_ALT_ON)) { // Intercept the Accessibility keychord (CTRL + ALT + Z) for keyboard users. - return handleKeyGesture(deviceId, new int[]{keyCode}, + handleKeyGesture(deviceId, new int[]{keyCode}, KeyEvent.META_CTRL_ON | KeyEvent.META_ALT_ON, KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT, KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId, @@ -930,7 +928,7 @@ final class KeyGestureController { break; case KeyEvent.KEYCODE_SYSRQ: if (down && repeatCount == 0) { - return handleKeyGesture(deviceId, new int[]{keyCode}, /* modifierState = */0, + handleKeyGesture(deviceId, new int[]{keyCode}, /* modifierState = */0, KeyGestureEvent.KEY_GESTURE_TYPE_TAKE_SCREENSHOT, KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId, focusedToken, /* flags = */0, /* appLaunchData = */null); @@ -938,7 +936,7 @@ final class KeyGestureController { break; case KeyEvent.KEYCODE_ESCAPE: if (down && KeyEvent.metaStateHasNoModifiers(metaState) && repeatCount == 0) { - return handleKeyGesture(deviceId, new int[]{keyCode}, /* modifierState = */0, + handleKeyGesture(deviceId, new int[]{keyCode}, /* modifierState = */0, KeyGestureEvent.KEY_GESTURE_TYPE_CLOSE_ALL_DIALOGS, KeyGestureEvent.ACTION_GESTURE_COMPLETE, displayId, focusedToken, /* flags = */0, /* appLaunchData = */null); @@ -964,29 +962,31 @@ final class KeyGestureController { } @VisibleForTesting - boolean handleKeyGesture(int deviceId, int[] keycodes, int modifierState, + void handleKeyGesture(int deviceId, int[] keycodes, int modifierState, @KeyGestureEvent.KeyGestureType int gestureType, int action, int displayId, @Nullable IBinder focusedToken, int flags, @Nullable AppLaunchData appLaunchData) { - return handleKeyGesture(createKeyGestureEvent(deviceId, keycodes, - modifierState, gestureType, action, displayId, flags, appLaunchData), focusedToken); + handleKeyGesture( + createKeyGestureEvent(deviceId, keycodes, modifierState, gestureType, action, + displayId, flags, appLaunchData), focusedToken); } - private boolean handleKeyGesture(AidlKeyGestureEvent event, @Nullable IBinder focusedToken) { + private void handleKeyGesture(AidlKeyGestureEvent event, @Nullable IBinder focusedToken) { if (mVisibleBackgroundUsersEnabled && event.displayId != DEFAULT_DISPLAY && shouldIgnoreGestureEventForVisibleBackgroundUser(event.gestureType, event.displayId)) { - return false; + return; } synchronized (mKeyGestureHandlerRecords) { - for (KeyGestureHandlerRecord handler : mKeyGestureHandlerRecords.values()) { - if (handler.handleKeyGesture(event, focusedToken)) { - Message msg = Message.obtain(mHandler, MSG_NOTIFY_KEY_GESTURE_EVENT, event); - mHandler.sendMessage(msg); - return true; - } + int index = mSupportedKeyGestureToPidMap.indexOfKey(event.gestureType); + if (index < 0) { + Log.i(TAG, "Key gesture: " + event.gestureType + " is not supported"); + return; } + int pid = mSupportedKeyGestureToPidMap.valueAt(index); + mKeyGestureHandlerRecords.get(pid).handleKeyGesture(event, focusedToken); + Message msg = Message.obtain(mHandler, MSG_NOTIFY_KEY_GESTURE_EVENT, event); + mHandler.sendMessage(msg); } - return false; } private boolean shouldIgnoreGestureEventForVisibleBackgroundUser( @@ -1285,12 +1285,23 @@ final class KeyGestureController { /** Register the key gesture event handler for a process. */ @BinderThread - public void registerKeyGestureHandler(IKeyGestureHandler handler, int pid) { + public void registerKeyGestureHandler(int[] keyGesturesToHandle, IKeyGestureHandler handler, + int pid) { synchronized (mKeyGestureHandlerRecords) { if (mKeyGestureHandlerRecords.get(pid) != null) { throw new IllegalStateException("The calling process has already registered " + "a KeyGestureHandler."); } + if (keyGesturesToHandle.length == 0) { + throw new IllegalArgumentException("No key gestures provided for pid = " + pid); + } + for (int gestureType : keyGesturesToHandle) { + if (mSupportedKeyGestureToPidMap.indexOfKey(gestureType) >= 0) { + throw new IllegalArgumentException( + "Key gesture " + gestureType + " is already registered by pid = " + + mSupportedKeyGestureToPidMap.get(gestureType)); + } + } KeyGestureHandlerRecord record = new KeyGestureHandlerRecord(pid, handler); try { handler.asBinder().linkToDeath(record, 0); @@ -1298,6 +1309,9 @@ final class KeyGestureController { throw new RuntimeException(ex); } mKeyGestureHandlerRecords.put(pid, record); + for (int gestureType : keyGesturesToHandle) { + mSupportedKeyGestureToPidMap.put(gestureType, pid); + } } } @@ -1315,7 +1329,7 @@ final class KeyGestureController { + "KeyGestureHandler."); } record.mKeyGestureHandler.asBinder().unlinkToDeath(record, 0); - mKeyGestureHandlerRecords.remove(pid); + onKeyGestureHandlerRemoved(pid); } } @@ -1328,9 +1342,14 @@ final class KeyGestureController { return mAppLaunchShortcutManager.getBookmarks(); } - private void onKeyGestureHandlerDied(int pid) { + private void onKeyGestureHandlerRemoved(int pid) { synchronized (mKeyGestureHandlerRecords) { mKeyGestureHandlerRecords.remove(pid); + for (int i = mSupportedKeyGestureToPidMap.size() - 1; i >= 0; i--) { + if (mSupportedKeyGestureToPidMap.valueAt(i) == pid) { + mSupportedKeyGestureToPidMap.removeAt(i); + } + } } } @@ -1369,18 +1388,17 @@ final class KeyGestureController { if (DEBUG) { Slog.d(TAG, "Key gesture event handler for pid " + mPid + " died."); } - onKeyGestureHandlerDied(mPid); + onKeyGestureHandlerRemoved(mPid); } - public boolean handleKeyGesture(AidlKeyGestureEvent event, IBinder focusedToken) { + public void handleKeyGesture(AidlKeyGestureEvent event, IBinder focusedToken) { try { - return mKeyGestureHandler.handleKeyGesture(event, focusedToken); + mKeyGestureHandler.handleKeyGesture(event, focusedToken); } catch (RemoteException ex) { Slog.w(TAG, "Failed to send key gesture to process " + mPid + ", assuming it died.", ex); binderDied(); } - return false; } } @@ -1479,18 +1497,21 @@ final class KeyGestureController { } } ipw.println("}"); - ipw.print("mKeyGestureHandlerRecords = {"); synchronized (mKeyGestureHandlerRecords) { - int i = mKeyGestureHandlerRecords.size() - 1; - for (int processId : mKeyGestureHandlerRecords.keySet()) { - ipw.print(processId); - if (i > 0) { + ipw.print("mKeyGestureHandlerRecords = {"); + int size = mKeyGestureHandlerRecords.size(); + for (int i = 0; i < size; i++) { + int pid = mKeyGestureHandlerRecords.keyAt(i); + ipw.print(pid); + if (i < size - 1) { ipw.print(", "); } - i--; } + ipw.println("}"); + ipw.println("mSupportedKeyGestures = " + Arrays.toString( + mSupportedKeyGestureToPidMap.copyKeys())); } - ipw.println("}"); + ipw.decreaseIndent(); ipw.println("Last handled KeyGestureEvents: "); ipw.increaseIndent(); diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index fde9165a84c6..2066dbc87a0d 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -1826,8 +1826,14 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. @NonNull UserData userData) { final int userId = userData.mUserId; if (userData.mCurClient == client) { - hideCurrentInputLocked(userData.mImeBindingState.mFocusedWindow, 0 /* flags */, - SoftInputShowHideReason.HIDE_REMOVE_CLIENT, userId); + if (Flags.refactorInsetsController()) { + final var statsToken = createStatsTokenForFocusedClient(false /* show */, + SoftInputShowHideReason.HIDE_REMOVE_CLIENT, userId); + setImeVisibilityOnFocusedWindowClient(false, userData, statsToken); + } else { + hideCurrentInputLocked(userData.mImeBindingState.mFocusedWindow, 0 /* flags */, + SoftInputShowHideReason.HIDE_REMOVE_CLIENT, userId); + } if (userData.mBoundToMethod) { userData.mBoundToMethod = false; final var userBindingController = userData.mBindingController; @@ -2097,8 +2103,14 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. } if (visibilityStateComputer.getImePolicy().isImeHiddenByDisplayPolicy()) { - hideCurrentInputLocked(userData.mImeBindingState.mFocusedWindow, 0 /* flags */, - SoftInputShowHideReason.HIDE_DISPLAY_IME_POLICY_HIDE, userId); + if (Flags.refactorInsetsController()) { + final var statsToken = createStatsTokenForFocusedClient(false /* show */, + SoftInputShowHideReason.HIDE_DISPLAY_IME_POLICY_HIDE, userId); + setImeVisibilityOnFocusedWindowClient(false, userData, statsToken); + } else { + hideCurrentInputLocked(userData.mImeBindingState.mFocusedWindow, 0 /* flags */, + SoftInputShowHideReason.HIDE_DISPLAY_IME_POLICY_HIDE, userId); + } return InputBindResult.NO_IME; } @@ -3855,8 +3867,17 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. Slog.w(TAG, "If you need to impersonate a foreground user/profile from" + " a background user, use EditorInfo.targetInputMethodUser with" + " INTERACT_ACROSS_USERS_FULL permission."); - hideCurrentInputLocked(userData.mImeBindingState.mFocusedWindow, - 0 /* flags */, SoftInputShowHideReason.HIDE_INVALID_USER, userId); + + if (Flags.refactorInsetsController()) { + final var statsToken = createStatsTokenForFocusedClient( + false /* show */, SoftInputShowHideReason.HIDE_INVALID_USER, + userId); + setImeVisibilityOnFocusedWindowClient(false, userData, statsToken); + } else { + hideCurrentInputLocked(userData.mImeBindingState.mFocusedWindow, + 0 /* flags */, SoftInputShowHideReason.HIDE_INVALID_USER, + userId); + } return InputBindResult.INVALID_USER; } @@ -4993,7 +5014,6 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. setImeVisibilityOnFocusedWindowClient(false, userData, null /* TODO(b/353463205) check statsToken */); } else { - hideCurrentInputLocked(userData.mImeBindingState.mFocusedWindow, 0 /* flags */, reason, userId); } @@ -6688,8 +6708,9 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. final InputMethodSettings settings = InputMethodSettingsRepository.get(userId); final var userData = getUserData(userId); if (Flags.refactorInsetsController()) { - setImeVisibilityOnFocusedWindowClient(false, userData, - null /* TODO(b329229469) initialize statsToken here? */); + final var statsToken = createStatsTokenForFocusedClient(false /* show */, + SoftInputShowHideReason.HIDE_RESET_SHELL_COMMAND, userId); + setImeVisibilityOnFocusedWindowClient(false, userData, statsToken); } else { hideCurrentInputLocked(userData.mImeBindingState.mFocusedWindow, 0 /* flags */, diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java b/services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java index ccb9e3ea5cbe..bbf7732c9596 100644 --- a/services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java +++ b/services/core/java/com/android/server/location/contexthub/ContextHubEndpointBroker.java @@ -33,6 +33,7 @@ import android.hardware.contexthub.MessageDeliveryStatus; import android.hardware.contexthub.Reason; import android.hardware.location.ContextHubTransaction; import android.hardware.location.IContextHubTransactionCallback; +import android.hardware.location.NanoAppState; import android.os.Binder; import android.os.IBinder; import android.os.PowerManager; @@ -48,6 +49,7 @@ import java.util.Collection; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; @@ -182,8 +184,11 @@ public class ContextHubEndpointBroker extends IContextHubEndpoint.Stub long expiryMillis = RELIABLE_MESSAGE_DUPLICATE_DETECTION_TIMEOUT.toMillis(); if (nowMillis >= nextEntry.getValue() + expiryMillis) { iterator.remove(); + } else { + // Safe to break since LinkedHashMap is insertion-ordered, so the next entry + // will have a later timestamp and will not be expired. + break; } - break; } return mRxMessageHistoryMap.containsKey(message.getMessageSequenceNumber()); @@ -276,6 +281,7 @@ public class ContextHubEndpointBroker extends IContextHubEndpoint.Stub int sessionId = mEndpointManager.reserveSessionId(); EndpointInfo halEndpointInfo = ContextHubServiceUtil.convertHalEndpointInfo(destination); + Log.d(TAG, "openSession: sessionId=" + sessionId); synchronized (mOpenSessionLock) { try { @@ -301,6 +307,7 @@ public class ContextHubEndpointBroker extends IContextHubEndpoint.Stub throw new IllegalArgumentException( "Unknown session ID in closeSession: id=" + sessionId); } + Log.d(TAG, "closeSession: sessionId=" + sessionId + " reason=" + reason); mEndpointManager.halCloseEndpointSession( sessionId, ContextHubServiceUtil.toHalReason(reason)); } @@ -373,12 +380,43 @@ public class ContextHubEndpointBroker extends IContextHubEndpoint.Stub try { mHubInterface.sendMessageToEndpoint(sessionId, halMessage); } catch (RemoteException e) { - Log.w(TAG, "Exception while sending message on session " + sessionId, e); + Log.e( + TAG, + "Exception while sending message on session " + + sessionId + + ", closing session", + e); + notifySessionClosedToBoth(sessionId, Reason.UNSPECIFIED); } } else { + IContextHubTransactionCallback wrappedCallback = + new IContextHubTransactionCallback.Stub() { + @Override + public void onQueryResponse(int result, List<NanoAppState> appStates) + throws RemoteException { + Log.w(TAG, "Unexpected onQueryResponse callback"); + } + + @Override + public void onTransactionComplete(int result) throws RemoteException { + callback.onTransactionComplete(result); + if (result != ContextHubTransaction.RESULT_SUCCESS) { + Log.e( + TAG, + "Failed to send reliable message " + + message + + ", closing session"); + notifySessionClosedToBoth(sessionId, Reason.UNSPECIFIED); + } + } + }; ContextHubServiceTransaction transaction = mTransactionManager.createSessionMessageTransaction( - mHubInterface, sessionId, halMessage, mPackageName, callback); + mHubInterface, + sessionId, + halMessage, + mPackageName, + wrappedCallback); try { mTransactionManager.addTransaction(transaction); info.setReliableMessagePending(transaction.getMessageSequenceNumber()); @@ -445,10 +483,7 @@ public class ContextHubEndpointBroker extends IContextHubEndpoint.Stub int id = mSessionMap.keyAt(i); HubEndpointInfo target = mSessionMap.get(id).getRemoteEndpointInfo(); if (!hasEndpointPermissions(target)) { - mEndpointManager.halCloseEndpointSessionNoThrow( - id, Reason.PERMISSION_DENIED); - onCloseEndpointSession(id, Reason.PERMISSION_DENIED); - // Resource cleanup is done in onCloseEndpointSession + notifySessionClosedToBoth(id, Reason.PERMISSION_DENIED); } } } @@ -532,8 +567,17 @@ public class ContextHubEndpointBroker extends IContextHubEndpoint.Stub /* package */ void onMessageReceived(int sessionId, HubMessage message) { byte errorCode = onMessageReceivedInternal(sessionId, message); - if (errorCode != ErrorCode.OK && message.isResponseRequired()) { - sendMessageDeliveryStatus(sessionId, message.getMessageSequenceNumber(), errorCode); + if (errorCode != ErrorCode.OK) { + Log.e(TAG, "Failed to send message to endpoint: " + message + ", closing session"); + if (message.isResponseRequired()) { + sendMessageDeliveryStatus(sessionId, message.getMessageSequenceNumber(), errorCode); + } else { + notifySessionClosedToBoth( + sessionId, + (errorCode == ErrorCode.PERMISSION_DENIED) + ? Reason.PERMISSION_DENIED + : Reason.UNSPECIFIED); + } } } @@ -800,4 +844,16 @@ public class ContextHubEndpointBroker extends IContextHubEndpoint.Stub + "-0x" + Long.toHexString(endpoint.getIdentifier().getEndpoint())); } + + /** + * Notifies to both the HAL and the app that a session has been closed. + * + * @param sessionId The ID of the session that was closed + * @param halReason The HAL reason for closing the session + */ + private void notifySessionClosedToBoth(int sessionId, byte halReason) { + Log.d(TAG, "notifySessionClosedToBoth: sessionId=" + sessionId + ", reason=" + halReason); + mEndpointManager.halCloseEndpointSessionNoThrow(sessionId, halReason); + onCloseEndpointSession(sessionId, halReason); + } } diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java index a8190269450a..07d1a65850e3 100644 --- a/services/core/java/com/android/server/locksettings/LockSettingsService.java +++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java @@ -1917,7 +1917,7 @@ public class LockSettingsService extends ILockSettings.Stub { * Set a new LSKF for the given user/profile. Only succeeds if the synthetic password for the * user is protected by the given {@param savedCredential}. * <p> - * When {@link android.security.Flags#clearStrongAuthOnAddPrimaryCredential()} is enabled and + * When {@link android.security.Flags#clearStrongAuthOnAddingPrimaryCredential()} is enabled and * setting a new credential where there was none, updates the strong auth state for * {@param userId} to <tt>STRONG_AUTH_NOT_REQUIRED</tt>. * @@ -1969,7 +1969,7 @@ public class LockSettingsService extends ILockSettings.Stub { onSyntheticPasswordUnlocked(userId, sp); setLockCredentialWithSpLocked(credential, sp, userId); - if (android.security.Flags.clearStrongAuthOnAddPrimaryCredential() + if (android.security.Flags.clearStrongAuthOnAddingPrimaryCredential() && savedCredential.isNone() && !credential.isNone()) { // Clear the strong auth value, since the LSKF has just been entered and set, // but only when the previous credential was None. diff --git a/services/core/java/com/android/server/media/MediaSessionService.java b/services/core/java/com/android/server/media/MediaSessionService.java index 58cf29b59961..c174451e8f5b 100644 --- a/services/core/java/com/android/server/media/MediaSessionService.java +++ b/services/core/java/com/android/server/media/MediaSessionService.java @@ -192,9 +192,15 @@ public class MediaSessionService extends SystemService implements Monitor { private final Map<Integer, Set<MediaSessionRecordImpl>> mUserEngagedSessionsForFgs = new HashMap<>(); - /* Maps uid with all media notifications associated to it */ + /** + * Maps UIDs to their associated media notifications: UID -> (Notification ID -> + * {@link android.service.notification.StatusBarNotification}). + * Each UID maps to a collection of notifications, identified by their + * {@link android.service.notification.StatusBarNotification#getId()}. + */ @GuardedBy("mLock") - private final Map<Integer, Set<StatusBarNotification>> mMediaNotifications = new HashMap<>(); + private final Map<Integer, Map<String, StatusBarNotification>> mMediaNotifications = + new HashMap<>(); // The FullUserRecord of the current users. (i.e. The foreground user that isn't a profile) // It's always not null after the MediaSessionService is started. @@ -737,7 +743,8 @@ public class MediaSessionService extends SystemService implements Monitor { } synchronized (mLock) { int uid = mediaSessionRecord.getUid(); - for (StatusBarNotification sbn : mMediaNotifications.getOrDefault(uid, Set.of())) { + for (StatusBarNotification sbn : mMediaNotifications.getOrDefault(uid, + Map.of()).values()) { if (mediaSessionRecord.isLinkedToNotification(sbn.getNotification())) { setFgsActiveLocked(mediaSessionRecord, sbn); return; @@ -771,7 +778,7 @@ public class MediaSessionService extends SystemService implements Monitor { int uid, MediaSessionRecordImpl record) { synchronized (mLock) { for (StatusBarNotification sbn : - mMediaNotifications.getOrDefault(uid, Set.of())) { + mMediaNotifications.getOrDefault(uid, Map.of()).values()) { if (record.isLinkedToNotification(sbn.getNotification())) { return sbn; } @@ -794,7 +801,8 @@ public class MediaSessionService extends SystemService implements Monitor { for (MediaSessionRecordImpl record : mUserEngagedSessionsForFgs.getOrDefault(uid, Set.of())) { for (StatusBarNotification sbn : - mMediaNotifications.getOrDefault(uid, Set.of())) { + mMediaNotifications.getOrDefault(uid, Map.of()).values()) { + // if (record.isLinkedToNotification(sbn.getNotification())) { // A user engaged session linked with a media notification is found. // We shouldn't call stop FGS in this case. @@ -3262,8 +3270,12 @@ public class MediaSessionService extends SystemService implements Monitor { return; } synchronized (mLock) { - mMediaNotifications.putIfAbsent(uid, new HashSet<>()); - mMediaNotifications.get(uid).add(sbn); + Map<String, StatusBarNotification> notifications = mMediaNotifications.get(uid); + if (notifications == null) { + notifications = new HashMap<>(); + mMediaNotifications.put(uid, notifications); + } + notifications.put(sbn.getKey(), sbn); MediaSessionRecordImpl userEngagedRecord = getUserEngagedMediaSessionRecordForNotification(uid, postedNotification); if (userEngagedRecord != null) { @@ -3287,10 +3299,10 @@ public class MediaSessionService extends SystemService implements Monitor { return; } synchronized (mLock) { - Set<StatusBarNotification> uidMediaNotifications = mMediaNotifications.get(uid); - if (uidMediaNotifications != null) { - uidMediaNotifications.remove(sbn); - if (uidMediaNotifications.isEmpty()) { + Map<String, StatusBarNotification> notifications = mMediaNotifications.get(uid); + if (notifications != null) { + notifications.remove(sbn.getKey()); + if (notifications.isEmpty()) { mMediaNotifications.remove(uid); } } diff --git a/services/core/java/com/android/server/media/quality/MediaQualityUtils.java b/services/core/java/com/android/server/media/quality/MediaQualityUtils.java index cf8b703a2641..05aac5587c2c 100644 --- a/services/core/java/com/android/server/media/quality/MediaQualityUtils.java +++ b/services/core/java/com/android/server/media/quality/MediaQualityUtils.java @@ -18,13 +18,20 @@ package com.android.server.media.quality; import android.content.ContentValues; import android.database.Cursor; +import android.hardware.tv.mediaquality.ColorRange; +import android.hardware.tv.mediaquality.ColorSpace; +import android.hardware.tv.mediaquality.ColorTemperature; import android.hardware.tv.mediaquality.DolbyAudioProcessing; import android.hardware.tv.mediaquality.DtsVirtualX; +import android.hardware.tv.mediaquality.Gamma; import android.hardware.tv.mediaquality.ParameterDefaultValue; import android.hardware.tv.mediaquality.ParameterName; import android.hardware.tv.mediaquality.ParameterRange; import android.hardware.tv.mediaquality.PictureParameter; +import android.hardware.tv.mediaquality.PictureQualityEventType; +import android.hardware.tv.mediaquality.QualityLevel; import android.hardware.tv.mediaquality.SoundParameter; +import android.media.quality.MediaQualityContract; import android.media.quality.MediaQualityContract.BaseParameters; import android.media.quality.MediaQualityContract.PictureQuality; import android.media.quality.MediaQualityContract.SoundQuality; @@ -371,7 +378,7 @@ public final class MediaQualityUtils { } List<PictureParameter> pictureParams = new ArrayList<>(); if (params.containsKey(PictureQuality.PARAMETER_BRIGHTNESS)) { - pictureParams.add(PictureParameter.brightness(params.getLong( + pictureParams.add(PictureParameter.brightness((float) params.getDouble( PictureQuality.PARAMETER_BRIGHTNESS))); params.remove(PictureQuality.PARAMETER_BRIGHTNESS); } @@ -441,28 +448,46 @@ public final class MediaQualityUtils { params.remove(PictureQuality.PARAMETER_COLOR_TUNER_BLUE_GAIN); } if (params.containsKey(PictureQuality.PARAMETER_NOISE_REDUCTION)) { - pictureParams.add(PictureParameter.noiseReduction( - (byte) params.getInt(PictureQuality.PARAMETER_NOISE_REDUCTION))); + String noiseReductionString = params.getString( + PictureQuality.PARAMETER_NOISE_REDUCTION); + if (noiseReductionString != null) { + byte noiseReductionByte = mapQualityLevel(noiseReductionString); + pictureParams.add(PictureParameter.noiseReduction(noiseReductionByte)); + } params.remove(PictureQuality.PARAMETER_NOISE_REDUCTION); } if (params.containsKey(PictureQuality.PARAMETER_MPEG_NOISE_REDUCTION)) { - pictureParams.add(PictureParameter.mpegNoiseReduction( - (byte) params.getInt(PictureQuality.PARAMETER_MPEG_NOISE_REDUCTION))); + String mpegNoiseReductionString = params.getString( + PictureQuality.PARAMETER_MPEG_NOISE_REDUCTION); + if (mpegNoiseReductionString != null) { + byte mpegNoiseReductionByte = mapQualityLevel(mpegNoiseReductionString); + pictureParams.add(PictureParameter.mpegNoiseReduction(mpegNoiseReductionByte)); + } params.remove(PictureQuality.PARAMETER_MPEG_NOISE_REDUCTION); } if (params.containsKey(PictureQuality.PARAMETER_FLESH_TONE)) { - pictureParams.add(PictureParameter.fleshTone( - (byte) params.getInt(PictureQuality.PARAMETER_FLESH_TONE))); + String fleshToneString = params.getString(PictureQuality.PARAMETER_FLESH_TONE); + if (fleshToneString != null) { + byte fleshToneByte = mapQualityLevel(fleshToneString); + pictureParams.add(PictureParameter.fleshTone(fleshToneByte)); + } params.remove(PictureQuality.PARAMETER_FLESH_TONE); } if (params.containsKey(PictureQuality.PARAMETER_DECONTOUR)) { - pictureParams.add(PictureParameter.deContour( - (byte) params.getInt(PictureQuality.PARAMETER_DECONTOUR))); + String decontourString = params.getString(PictureQuality.PARAMETER_DECONTOUR); + if (decontourString != null) { + byte decontourByte = mapQualityLevel(decontourString); + pictureParams.add(PictureParameter.deContour(decontourByte)); + } params.remove(PictureQuality.PARAMETER_DECONTOUR); } if (params.containsKey(PictureQuality.PARAMETER_DYNAMIC_LUMA_CONTROL)) { - pictureParams.add(PictureParameter.dynamicLumaControl( - (byte) params.getInt(PictureQuality.PARAMETER_DYNAMIC_LUMA_CONTROL))); + String dynamicLunaControlString = params.getString( + PictureQuality.PARAMETER_DYNAMIC_LUMA_CONTROL); + if (dynamicLunaControlString != null) { + byte dynamicLunaControlByte = mapQualityLevel(dynamicLunaControlString); + pictureParams.add(PictureParameter.dynamicLumaControl(dynamicLunaControlByte)); + } params.remove(PictureQuality.PARAMETER_DYNAMIC_LUMA_CONTROL); } if (params.containsKey(PictureQuality.PARAMETER_FILM_MODE)) { @@ -481,9 +506,48 @@ public final class MediaQualityUtils { params.remove(PictureQuality.PARAMETER_COLOR_TUNE); } if (params.containsKey(PictureQuality.PARAMETER_COLOR_TEMPERATURE)) { - pictureParams.add(PictureParameter.colorTemperature( - (byte) params.getInt( - PictureQuality.PARAMETER_COLOR_TEMPERATURE))); + String colorTemperatureString = params.getString( + PictureQuality.PARAMETER_COLOR_TEMPERATURE); + if (colorTemperatureString != null) { + byte colorTemperatureByte; + switch (colorTemperatureString) { + case MediaQualityContract.COLOR_TEMP_USER: + colorTemperatureByte = ColorTemperature.USER; + break; + case MediaQualityContract.COLOR_TEMP_COOL: + colorTemperatureByte = ColorTemperature.COOL; + break; + case MediaQualityContract.COLOR_TEMP_STANDARD: + colorTemperatureByte = ColorTemperature.STANDARD; + break; + case MediaQualityContract.COLOR_TEMP_WARM: + colorTemperatureByte = ColorTemperature.WARM; + break; + case MediaQualityContract.COLOR_TEMP_USER_HDR10PLUS: + colorTemperatureByte = ColorTemperature.USER_HDR10PLUS; + break; + case MediaQualityContract.COLOR_TEMP_COOL_HDR10PLUS: + colorTemperatureByte = ColorTemperature.COOL_HDR10PLUS; + break; + case MediaQualityContract.COLOR_TEMP_STANDARD_HDR10PLUS: + colorTemperatureByte = ColorTemperature.STANDARD_HDR10PLUS; + break; + case MediaQualityContract.COLOR_TEMP_WARM_HDR10PLUS: + colorTemperatureByte = ColorTemperature.WARM_HDR10PLUS; + break; + case MediaQualityContract.COLOR_TEMP_FMMSDR: + colorTemperatureByte = ColorTemperature.FMMSDR; + break; + case MediaQualityContract.COLOR_TEMP_FMMHDR: + colorTemperatureByte = ColorTemperature.FMMHDR; + break; + default: + colorTemperatureByte = ColorTemperature.STANDARD; + Log.e("PictureParams", "Invalid color_temp string: " + + colorTemperatureString); + } + pictureParams.add(PictureParameter.colorTemperature(colorTemperatureByte)); + } params.remove(PictureQuality.PARAMETER_COLOR_TEMPERATURE); } if (params.containsKey(PictureQuality.PARAMETER_GLOBAL_DIMMING)) { @@ -517,8 +581,26 @@ public final class MediaQualityUtils { params.remove(PictureQuality.PARAMETER_COLOR_TUNER_BLUE_GAIN); } if (params.containsKey(PictureQuality.PARAMETER_LEVEL_RANGE)) { - pictureParams.add(PictureParameter.levelRange( - (byte) params.getInt(PictureQuality.PARAMETER_LEVEL_RANGE))); + String levelRangeString = params.getString(PictureQuality.PARAMETER_LEVEL_RANGE); + if (levelRangeString != null) { + byte levelRangeByte; + switch (levelRangeString) { + case "AUTO": + levelRangeByte = ColorRange.AUTO; + break; + case "LIMITED": + levelRangeByte = ColorRange.LIMITED; + break; + case "FULL": + levelRangeByte = ColorRange.FULL; + break; + default: + levelRangeByte = ColorRange.AUTO; + Log.e("PictureParams", "Invalid color_range string: " + + levelRangeString); + } + pictureParams.add(PictureParameter.levelRange(levelRangeByte)); + } params.remove(PictureQuality.PARAMETER_LEVEL_RANGE); } if (params.containsKey(PictureQuality.PARAMETER_GAMUT_MAPPING)) { @@ -547,13 +629,61 @@ public final class MediaQualityUtils { params.remove(PictureQuality.PARAMETER_CVRR); } if (params.containsKey(PictureQuality.PARAMETER_HDMI_RGB_RANGE)) { - pictureParams.add(PictureParameter.hdmiRgbRange( - (byte) params.getInt(PictureQuality.PARAMETER_HDMI_RGB_RANGE))); + String hdmiRgbRangeString = params.getString(PictureQuality.PARAMETER_HDMI_RGB_RANGE); + if (hdmiRgbRangeString != null) { + byte hdmiRgbRangeByte; + switch (hdmiRgbRangeString) { + case "AUTO": + hdmiRgbRangeByte = ColorRange.AUTO; + break; + case "LIMITED": + hdmiRgbRangeByte = ColorRange.LIMITED; + break; + case "FULL": + hdmiRgbRangeByte = ColorRange.FULL; + break; + default: + hdmiRgbRangeByte = ColorRange.AUTO; + Log.e("PictureParams", "Invalid hdmi_rgb_range string: " + + hdmiRgbRangeByte); + } + pictureParams.add(PictureParameter.hdmiRgbRange(hdmiRgbRangeByte)); + } params.remove(PictureQuality.PARAMETER_HDMI_RGB_RANGE); } if (params.containsKey(PictureQuality.PARAMETER_COLOR_SPACE)) { - pictureParams.add(PictureParameter.colorSpace( - (byte) params.getInt(PictureQuality.PARAMETER_COLOR_SPACE))); + String colorSpaceString = params.getString(PictureQuality.PARAMETER_COLOR_SPACE); + if (colorSpaceString != null) { + byte colorSpaceByte; + switch (colorSpaceString) { + case "AUTO": + colorSpaceByte = ColorSpace.AUTO; + break; + case "S_RGB_BT_709": + colorSpaceByte = ColorSpace.S_RGB_BT_709; + break; + case "DCI": + colorSpaceByte = ColorSpace.DCI; + break; + case "ADOBE_RGB": + colorSpaceByte = ColorSpace.ADOBE_RGB; + break; + case "BT2020": + colorSpaceByte = ColorSpace.BT2020; + break; + case "ON": + colorSpaceByte = ColorSpace.ON; + break; + case "OFF": + colorSpaceByte = ColorSpace.OFF; + break; + default: + colorSpaceByte = ColorSpace.OFF; + Log.e("PictureParams", "Invalid color_space string: " + + colorSpaceString); + } + pictureParams.add(PictureParameter.colorSpace(colorSpaceByte)); + } params.remove(PictureQuality.PARAMETER_COLOR_SPACE); } if (params.containsKey(PictureQuality.PARAMETER_PANEL_INIT_MAX_LUMINCE_NITS)) { @@ -567,8 +697,25 @@ public final class MediaQualityUtils { params.remove(PictureQuality.PARAMETER_PANEL_INIT_MAX_LUMINCE_VALID); } if (params.containsKey(PictureQuality.PARAMETER_GAMMA)) { - pictureParams.add(PictureParameter.gamma( - (byte) params.getInt(PictureQuality.PARAMETER_GAMMA))); + String gammaString = params.getString(PictureQuality.PARAMETER_GAMMA); + if (gammaString != null) { + byte gammaByte; + switch (gammaString) { + case "DARK": + gammaByte = Gamma.DARK; + break; + case "MIDDLE": + gammaByte = Gamma.MIDDLE; + break; + case "BRIGHT": + gammaByte = Gamma.BRIGHT; + break; + default: + gammaByte = Gamma.DARK; + Log.e("PictureParams", "Invalid gamma string: " + gammaString); + } + pictureParams.add(PictureParameter.gamma(gammaByte)); + } params.remove(PictureQuality.PARAMETER_GAMMA); } if (params.containsKey(PictureQuality.PARAMETER_COLOR_TEMPERATURE_RED_OFFSET)) { @@ -602,13 +749,19 @@ public final class MediaQualityUtils { params.remove(PictureQuality.PARAMETER_ELEVEN_POINT_BLUE); } if (params.containsKey(PictureQuality.PARAMETER_LOW_BLUE_LIGHT)) { - pictureParams.add(PictureParameter.lowBlueLight( - (byte) params.getInt(PictureQuality.PARAMETER_LOW_BLUE_LIGHT))); + String lowBlueLightString = params.getString(PictureQuality.PARAMETER_LOW_BLUE_LIGHT); + if (lowBlueLightString != null) { + byte lowBlueLightByte = mapQualityLevel(lowBlueLightString); + pictureParams.add(PictureParameter.lowBlueLight(lowBlueLightByte)); + } params.remove(PictureQuality.PARAMETER_LOW_BLUE_LIGHT); } if (params.containsKey(PictureQuality.PARAMETER_LD_MODE)) { - pictureParams.add(PictureParameter.LdMode( - (byte) params.getInt(PictureQuality.PARAMETER_LD_MODE))); + String ldModeString = params.getString(PictureQuality.PARAMETER_LD_MODE); + if (ldModeString != null) { + byte ldModeByte = mapQualityLevel(ldModeString); + pictureParams.add(PictureParameter.LdMode(ldModeByte)); + } params.remove(PictureQuality.PARAMETER_LD_MODE); } if (params.containsKey(PictureQuality.PARAMETER_OSD_RED_GAIN)) { @@ -767,8 +920,44 @@ public final class MediaQualityUtils { params.remove(PictureQuality.PARAMETER_COLOR_TUNER_LUMINANCE_FLESH); } if (params.containsKey(PictureQuality.PARAMETER_PICTURE_QUALITY_EVENT_TYPE)) { - pictureParams.add(PictureParameter.pictureQualityEventType( - (byte) params.getInt(PictureQuality.PARAMETER_PICTURE_QUALITY_EVENT_TYPE))); + String pictureQualityEventTypeString = params.getString( + PictureQuality.PARAMETER_PICTURE_QUALITY_EVENT_TYPE); + if (pictureQualityEventTypeString != null) { + byte pictureQualityEventTypeByte; + switch (pictureQualityEventTypeString) { + case "NONE": + pictureQualityEventTypeByte = PictureQualityEventType.NONE; + break; + case "BBD_RESULT": + pictureQualityEventTypeByte = PictureQualityEventType.BBD_RESULT; + break; + case "VIDEO_DELAY_CHANGE": + pictureQualityEventTypeByte = PictureQualityEventType.VIDEO_DELAY_CHANGE; + break; + case "CAPTUREPOINT_INFO_CHANGE": + pictureQualityEventTypeByte = + PictureQualityEventType.CAPTUREPOINT_INFO_CHANGE; + break; + case "VIDEOPATH_CHANGE": + pictureQualityEventTypeByte = PictureQualityEventType.VIDEOPATH_CHANGE; + break; + case "EXTRA_FRAME_CHANGE": + pictureQualityEventTypeByte = PictureQualityEventType.EXTRA_FRAME_CHANGE; + break; + case "DOLBY_IQ_CHANGE": + pictureQualityEventTypeByte = PictureQualityEventType.DOLBY_IQ_CHANGE; + break; + case "DOLBY_APO_CHANGE": + pictureQualityEventTypeByte = PictureQualityEventType.DOLBY_APO_CHANGE; + break; + default: + pictureQualityEventTypeByte = PictureQualityEventType.NONE; + Log.e("PictureParams", "Invalid event type string: " + + pictureQualityEventTypeString); + } + pictureParams.add( + PictureParameter.pictureQualityEventType(pictureQualityEventTypeByte)); + } params.remove(PictureQuality.PARAMETER_PICTURE_QUALITY_EVENT_TYPE); } return pictureParams.toArray(new PictureParameter[0]); @@ -1657,6 +1846,19 @@ public final class MediaQualityUtils { return colIndex != -1 ? cursor.getString(colIndex) : null; } + private static byte mapQualityLevel(String qualityLevel) { + return switch (qualityLevel) { + case MediaQualityContract.LEVEL_OFF -> QualityLevel.OFF; + case MediaQualityContract.LEVEL_LOW -> QualityLevel.LOW; + case MediaQualityContract.LEVEL_MEDIUM -> QualityLevel.MEDIUM; + case MediaQualityContract.LEVEL_HIGH -> QualityLevel.HIGH; + default -> { + Log.e("PictureParams", "Invalid noise_reduction string: " + qualityLevel); + yield QualityLevel.OFF; + } + }; + } + private MediaQualityUtils() { } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 78554bdcf6aa..06fc9b083086 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -14544,23 +14544,23 @@ public class NotificationManagerService extends SystemService { */ private class NotificationTrampolineCallback implements BackgroundActivityStartCallback { @Override - public boolean isActivityStartAllowed(Collection<IBinder> tokens, int uid, - String packageName) { + public BackgroundActivityStartCallbackResult isActivityStartAllowed( + Collection<IBinder> tokens, int uid, String packageName) { checkArgument(!tokens.isEmpty()); for (IBinder token : tokens) { if (token != ALLOWLIST_TOKEN) { // We only block or warn if the start is exclusively due to notification - return true; + return RESULT_TRUE; } } String logcatMessage = "Indirect notification activity start (trampoline) from " + packageName; if (blockTrampoline(uid)) { Slog.e(TAG, logcatMessage + " blocked"); - return false; + return RESULT_FALSE; } else { Slog.w(TAG, logcatMessage + ", this should be avoided for performance reasons"); - return true; + return new BackgroundActivityStartCallbackResult(true, ALLOWLIST_TOKEN); } } diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java index acdc79fb9922..e02ec6a9e3b4 100644 --- a/services/core/java/com/android/server/pm/InstallPackageHelper.java +++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java @@ -3077,7 +3077,8 @@ final class InstallPackageHelper { } if (succeeded) { - Slog.i(TAG, "installation completed:" + packageName); + Slog.i(TAG, "installation completed for package:" + packageName + + ". Final code path: " + pkgSetting.getPath().getPath()); if (Flags.aslInApkAppMetadataSource() && pkgSetting.getAppMetadataSource() == APP_METADATA_SOURCE_APK) { diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java index 38d458767015..2744721c3a46 100644 --- a/services/core/java/com/android/server/policy/PhoneWindowManager.java +++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java @@ -115,7 +115,6 @@ import static com.android.server.wm.WindowManagerPolicyProto.SCREEN_ON_FULLY; import static com.android.server.wm.WindowManagerPolicyProto.WINDOW_MANAGER_DRAW_COMPLETE; import android.accessibilityservice.AccessibilityService; -import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SuppressLint; import android.app.ActivityManager; @@ -268,6 +267,7 @@ import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.io.PrintWriter; +import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; @@ -311,6 +311,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { static final int SHORT_PRESS_POWER_LOCK_OR_SLEEP = 6; static final int SHORT_PRESS_POWER_DREAM_OR_SLEEP = 7; static final int SHORT_PRESS_POWER_HUB_OR_DREAM_OR_SLEEP = 8; + static final int SHORT_PRESS_POWER_DREAM_OR_AWAKE_OR_SLEEP = 9; // must match: config_LongPressOnPowerBehavior in config.xml // The config value can be overridden using Settings.Global.POWER_BUTTON_LONG_PRESS @@ -1234,8 +1235,10 @@ public class PhoneWindowManager implements WindowManagerPolicy { break; } case SHORT_PRESS_POWER_DREAM_OR_SLEEP: { - attemptToDreamFromShortPowerButtonPress( - true, + attemptToDreamOrAwakeFromShortPowerButtonPress( + /* isScreenOn */ true, + /* awakeWhenDream */ false, + /* noDreamAction */ () -> sleepDefaultDisplayFromPowerButton(eventTime, 0)); break; } @@ -1269,13 +1272,22 @@ public class PhoneWindowManager implements WindowManagerPolicy { lockNow(options); } else { // If the hub cannot be run, attempt to dream instead. - attemptToDreamFromShortPowerButtonPress( + attemptToDreamOrAwakeFromShortPowerButtonPress( /* isScreenOn */ true, + /* awakeWhenDream */ false, /* noDreamAction */ () -> sleepDefaultDisplayFromPowerButton(eventTime, 0)); } break; } + case SHORT_PRESS_POWER_DREAM_OR_AWAKE_OR_SLEEP: { + attemptToDreamOrAwakeFromShortPowerButtonPress( + /* isScreenOn */ true, + /* awakeWhenDream */ true, + /* noDreamAction */ + () -> sleepDefaultDisplayFromPowerButton(eventTime, 0)); + break; + } } } } @@ -1319,15 +1331,18 @@ public class PhoneWindowManager implements WindowManagerPolicy { } /** - * Attempt to dream from a power button press. + * Attempt to dream, awake or sleep from a power button press. * * @param isScreenOn Whether the screen is currently on. + * @param awakeWhenDream When it's set to {@code true}, awake the device from dreaming. + * Otherwise, go to sleep. * @param noDreamAction The action to perform if dreaming is not possible. */ - private void attemptToDreamFromShortPowerButtonPress( - boolean isScreenOn, Runnable noDreamAction) { + private void attemptToDreamOrAwakeFromShortPowerButtonPress( + boolean isScreenOn, boolean awakeWhenDream, Runnable noDreamAction) { if (mShortPressOnPowerBehavior != SHORT_PRESS_POWER_DREAM_OR_SLEEP - && mShortPressOnPowerBehavior != SHORT_PRESS_POWER_HUB_OR_DREAM_OR_SLEEP) { + && mShortPressOnPowerBehavior != SHORT_PRESS_POWER_HUB_OR_DREAM_OR_SLEEP + && mShortPressOnPowerBehavior != SHORT_PRESS_POWER_DREAM_OR_AWAKE_OR_SLEEP) { // If the power button behavior isn't one that should be able to trigger the dream, give // up. noDreamAction.run(); @@ -1335,9 +1350,24 @@ public class PhoneWindowManager implements WindowManagerPolicy { } final DreamManagerInternal dreamManagerInternal = getDreamManagerInternal(); - if (dreamManagerInternal == null || !dreamManagerInternal.canStartDreaming(isScreenOn)) { - Slog.d(TAG, "Can't start dreaming when attempting to dream from short power" - + " press (isScreenOn=" + isScreenOn + ")"); + if (dreamManagerInternal == null) { + Slog.d(TAG, + "Can't access dream manager dreaming when attempting to start or stop dream " + + "from short power press (isScreenOn=" + + isScreenOn + ", awakeWhenDream=" + awakeWhenDream + ")"); + noDreamAction.run(); + return; + } + + if (!dreamManagerInternal.canStartDreaming(isScreenOn)) { + if (awakeWhenDream && dreamManagerInternal.isDreaming()) { + dreamManagerInternal.stopDream(false /*immediate*/, "short press power" /*reason*/); + return; + } + Slog.d(TAG, + "Can't start dreaming and the device is not dreaming when attempting to start " + + "or stop dream from short power press (isScreenOn=" + + isScreenOn + ", awakeWhenDream=" + awakeWhenDream + ")"); noDreamAction.run(); return; } @@ -2312,6 +2342,10 @@ public class PhoneWindowManager implements WindowManagerPolicy { return ActivityManager.getService(); } + LockPatternUtils getLockPatternUtils() { + return new LockPatternUtils(mContext); + } + ButtonOverridePermissionChecker getButtonOverridePermissionChecker() { return new ButtonOverridePermissionChecker(); } @@ -2360,7 +2394,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { mAccessibilityShortcutController = injector.getAccessibilityShortcutController( mContext, new Handler(), mCurrentUserId); mGlobalActionsFactory = injector.getGlobalActionsFactory(); - mLockPatternUtils = new LockPatternUtils(mContext); + mLockPatternUtils = injector.getLockPatternUtils(); mLogger = new MetricsLogger(); Resources res = mContext.getResources(); @@ -4240,19 +4274,51 @@ public class PhoneWindowManager implements WindowManagerPolicy { if (!useKeyGestureEventHandler()) { return; } - mInputManager.registerKeyGestureEventHandler((event, focusedToken) -> { - boolean handled = PhoneWindowManager.this.handleKeyGestureEvent(event, - focusedToken); - if (handled && !event.isCancelled() && Arrays.stream(event.getKeycodes()).anyMatch( - (keycode) -> keycode == KeyEvent.KEYCODE_POWER)) { - mPowerKeyHandled = true; - } - return handled; - }); + List<Integer> supportedGestures = new ArrayList<>(List.of( + KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS, + KeyGestureEvent.KEY_GESTURE_TYPE_APP_SWITCH, + KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_ASSISTANT, + KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_VOICE_ASSISTANT, + KeyGestureEvent.KEY_GESTURE_TYPE_HOME, + KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS, + KeyGestureEvent.KEY_GESTURE_TYPE_LOCK_SCREEN, + KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL, + KeyGestureEvent.KEY_GESTURE_TYPE_TAKE_SCREENSHOT, + KeyGestureEvent.KEY_GESTURE_TYPE_TRIGGER_BUG_REPORT, + KeyGestureEvent.KEY_GESTURE_TYPE_BACK, + KeyGestureEvent.KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION, + KeyGestureEvent.KEY_GESTURE_TYPE_DESKTOP_MODE, + KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_LEFT, + KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_RIGHT, + KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER, + KeyGestureEvent.KEY_GESTURE_TYPE_BRIGHTNESS_UP, + KeyGestureEvent.KEY_GESTURE_TYPE_BRIGHTNESS_DOWN, + KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER, + KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS, + KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS, + KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SEARCH, + KeyGestureEvent.KEY_GESTURE_TYPE_LANGUAGE_SWITCH, + KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT, + KeyGestureEvent.KEY_GESTURE_TYPE_CLOSE_ALL_DIALOGS, + KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION, + KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_DO_NOT_DISTURB, + KeyGestureEvent.KEY_GESTURE_TYPE_SCREENSHOT_CHORD, + KeyGestureEvent.KEY_GESTURE_TYPE_RINGER_TOGGLE_CHORD, + KeyGestureEvent.KEY_GESTURE_TYPE_GLOBAL_ACTIONS, + KeyGestureEvent.KEY_GESTURE_TYPE_TV_TRIGGER_BUG_REPORT + )); + if (enableTalkbackAndMagnifierKeyGestures()) { + supportedGestures.add(KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_TALKBACK); + } + if (enableVoiceAccessKeyGestures()) { + supportedGestures.add(KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_VOICE_ACCESS); + } + mInputManager.registerKeyGestureEventHandler(supportedGestures, + PhoneWindowManager.this::handleKeyGestureEvent); } @VisibleForTesting - boolean handleKeyGestureEvent(KeyGestureEvent event, IBinder focusedToken) { + void handleKeyGestureEvent(KeyGestureEvent event, IBinder focusedToken) { boolean start = event.getAction() == KeyGestureEvent.ACTION_GESTURE_START; boolean complete = event.getAction() == KeyGestureEvent.ACTION_GESTURE_COMPLETE && !event.isCancelled(); @@ -4262,12 +4328,16 @@ public class PhoneWindowManager implements WindowManagerPolicy { int modifierState = event.getModifierState(); boolean keyguardOn = keyguardOn(); boolean canLaunchApp = isUserSetupComplete() && !keyguardOn; + if (!event.isCancelled() && Arrays.stream(event.getKeycodes()).anyMatch( + (keycode) -> keycode == KeyEvent.KEYCODE_POWER)) { + mPowerKeyHandled = true; + } switch (gestureType) { case KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS: if (complete) { showRecentApps(false); } - return true; + break; case KeyGestureEvent.KEY_GESTURE_TYPE_APP_SWITCH: if (!keyguardOn) { if (start) { @@ -4276,7 +4346,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { toggleRecentApps(); } } - return true; + break; case KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_ASSISTANT: case KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_VOICE_ASSISTANT: if (complete && canLaunchApp) { @@ -4284,33 +4354,33 @@ public class PhoneWindowManager implements WindowManagerPolicy { deviceId, SystemClock.uptimeMillis(), AssistUtils.INVOCATION_TYPE_UNKNOWN); } - return true; + break; case KeyGestureEvent.KEY_GESTURE_TYPE_HOME: if (complete) { // Post to main thread to avoid blocking input pipeline. mHandler.post(() -> handleShortPressOnHome(displayId)); } - return true; + break; case KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS: if (complete && canLaunchApp) { showSystemSettings(); } - return true; + break; case KeyGestureEvent.KEY_GESTURE_TYPE_LOCK_SCREEN: if (complete) { lockNow(null /* options */); } - return true; + break; case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL: if (complete) { toggleNotificationPanel(); } - return true; + break; case KeyGestureEvent.KEY_GESTURE_TYPE_TAKE_SCREENSHOT: if (complete) { interceptScreenshotChord(SCREENSHOT_KEY_OTHER, 0 /*pressDelay*/); } - return true; + break; case KeyGestureEvent.KEY_GESTURE_TYPE_TRIGGER_BUG_REPORT: if (complete && mEnableBugReportKeyboardShortcut) { try { @@ -4321,12 +4391,12 @@ public class PhoneWindowManager implements WindowManagerPolicy { Slog.d(TAG, "Error taking bugreport", e); } } - return true; + break; case KeyGestureEvent.KEY_GESTURE_TYPE_BACK: if (complete) { injectBackGesture(SystemClock.uptimeMillis()); } - return true; + break; case KeyGestureEvent.KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION: if (complete) { StatusBarManagerInternal statusbar = getStatusBarManagerInternal(); @@ -4335,7 +4405,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { getTargetDisplayIdForKeyGestureEvent(event)); } } - return true; + break; case KeyGestureEvent.KEY_GESTURE_TYPE_DESKTOP_MODE: if (complete) { StatusBarManagerInternal statusbar = getStatusBarManagerInternal(); @@ -4344,24 +4414,24 @@ public class PhoneWindowManager implements WindowManagerPolicy { getTargetDisplayIdForKeyGestureEvent(event)); } } - return true; + break; case KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_LEFT: if (complete) { moveFocusedTaskToStageSplit(getTargetDisplayIdForKeyGestureEvent(event), true /* leftOrTop */); } - return true; + break; case KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_RIGHT: if (complete) { moveFocusedTaskToStageSplit(getTargetDisplayIdForKeyGestureEvent(event), false /* leftOrTop */); } - return true; + break; case KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER: if (complete) { toggleKeyboardShortcutsMenu(deviceId); } - return true; + break; case KeyGestureEvent.KEY_GESTURE_TYPE_BRIGHTNESS_UP: case KeyGestureEvent.KEY_GESTURE_TYPE_BRIGHTNESS_DOWN: if (complete) { @@ -4369,32 +4439,32 @@ public class PhoneWindowManager implements WindowManagerPolicy { gestureType == KeyGestureEvent.KEY_GESTURE_TYPE_BRIGHTNESS_UP ? 1 : -1; changeDisplayBrightnessValue(displayId, direction); } - return true; + break; case KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER: if (start) { showRecentApps(true); } else { hideRecentApps(true, false); } - return true; + break; case KeyGestureEvent.KEY_GESTURE_TYPE_ALL_APPS: case KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_ALL_APPS: if (complete && isKeyEventForCurrentUser(event.getDisplayId(), event.getKeycodes()[0], "launchAllAppsViaA11y")) { launchAllAppsAction(); } - return true; + break; case KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SEARCH: if (complete && canLaunchApp) { launchTargetSearchActivity(); } - return true; + break; case KeyGestureEvent.KEY_GESTURE_TYPE_LANGUAGE_SWITCH: if (complete) { int direction = (modifierState & KeyEvent.META_SHIFT_MASK) != 0 ? -1 : 1; sendSwitchKeyboardLayout(displayId, focusedToken, direction); } - return true; + break; case KeyGestureEvent.KEY_GESTURE_TYPE_SCREENSHOT_CHORD: if (start) { // Screenshot chord is pressed: Wait for long press delay before taking @@ -4404,14 +4474,14 @@ public class PhoneWindowManager implements WindowManagerPolicy { } else { cancelPendingScreenshotChordAction(); } - return true; + break; case KeyGestureEvent.KEY_GESTURE_TYPE_RINGER_TOGGLE_CHORD: if (start) { interceptRingerToggleChord(); } else { cancelPendingRingerToggleChordAction(); } - return true; + break; case KeyGestureEvent.KEY_GESTURE_TYPE_GLOBAL_ACTIONS: if (start) { performHapticFeedback( @@ -4421,40 +4491,34 @@ public class PhoneWindowManager implements WindowManagerPolicy { } else { cancelGlobalActionsAction(); } - return true; + break; case KeyGestureEvent.KEY_GESTURE_TYPE_TV_TRIGGER_BUG_REPORT: if (start) { interceptBugreportGestureTv(); } else { cancelBugreportGestureTv(); } - return true; + break; case KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT: if (complete && mAccessibilityShortcutController.isAccessibilityShortcutAvailable( isKeyguardLocked())) { mHandler.sendMessage(mHandler.obtainMessage(MSG_ACCESSIBILITY_SHORTCUT)); } - return true; + break; case KeyGestureEvent.KEY_GESTURE_TYPE_CLOSE_ALL_DIALOGS: if (complete) { mContext.closeSystemDialogs(); } - return true; + break; case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_TALKBACK: - if (enableTalkbackAndMagnifierKeyGestures()) { - if (complete) { - mTalkbackShortcutController.toggleTalkback(mCurrentUserId, - TalkbackShortcutController.ShortcutSource.KEYBOARD); - } - return true; + if (complete) { + mTalkbackShortcutController.toggleTalkback(mCurrentUserId, + TalkbackShortcutController.ShortcutSource.KEYBOARD); } break; case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_VOICE_ACCESS: - if (enableVoiceAccessKeyGestures()) { - if (complete) { - mVoiceAccessShortcutController.toggleVoiceAccess(mCurrentUserId); - } - return true; + if (complete) { + mVoiceAccessShortcutController.toggleVoiceAccess(mCurrentUserId); } break; case KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_APPLICATION: @@ -4463,7 +4527,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { && mModifierShortcutManager.launchApplication(data)) { dismissKeyboardShortcutsMenu(); } - return true; + break; case KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_DO_NOT_DISTURB: NotificationManager nm = getNotificationService(); if (nm != null) { @@ -4472,9 +4536,12 @@ public class PhoneWindowManager implements WindowManagerPolicy { : Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS, null, "Key gesture DND", true); } - return true; + break; + default: + Log.w(TAG, "Received a key gesture " + event + + " that was not registered by this handler"); + break; } - return false; } private void changeDisplayBrightnessValue(int displayId, int direction) { diff --git a/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java b/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java index f5daa8036726..8c3b7c606f04 100644 --- a/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java +++ b/services/core/java/com/android/server/security/advancedprotection/AdvancedProtectionService.java @@ -17,11 +17,13 @@ package com.android.server.security.advancedprotection; import static android.provider.Settings.Secure.ADVANCED_PROTECTION_MODE; +import static com.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR; import android.Manifest; import android.annotation.EnforcePermission; import android.annotation.NonNull; import android.annotation.Nullable; +import android.app.StatsManager; import android.content.Context; import android.content.SharedPreferences; import android.os.Binder; @@ -45,6 +47,7 @@ import android.security.advancedprotection.IAdvancedProtectionService; import android.security.advancedprotection.AdvancedProtectionProtoEnums; import android.util.ArrayMap; import android.util.Slog; +import android.util.StatsEvent; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.DumpUtils; @@ -137,6 +140,15 @@ public class AdvancedProtectionService extends IAdvancedProtectionService.Stub mProviders.add(new DisallowWepAdvancedProtectionProvider()); } + private void initLogging() { + StatsManager statsManager = mContext.getSystemService(StatsManager.class); + statsManager.setPullAtomCallback( + FrameworkStatsLog.ADVANCED_PROTECTION_STATE_INFO, + null, // use default PullAtomMetadata values + DIRECT_EXECUTOR, + new AdvancedProtectionStatePullAtomCallback()); + } + // Only for tests @VisibleForTesting AdvancedProtectionService(@NonNull Context context, @NonNull AdvancedProtectionStore store, @@ -399,6 +411,7 @@ public class AdvancedProtectionService extends IAdvancedProtectionService.Stub Slog.i(TAG, "Advanced protection is enabled"); } mService.initFeatures(enabled); + mService.initLogging(); } } } @@ -500,4 +513,22 @@ public class AdvancedProtectionService extends IAdvancedProtectionService.Stub } } } + + private class AdvancedProtectionStatePullAtomCallback + implements StatsManager.StatsPullAtomCallback { + + @Override + public int onPullAtom(int atomTag, List<StatsEvent> data) { + if (atomTag != FrameworkStatsLog.ADVANCED_PROTECTION_STATE_INFO) { + return StatsManager.PULL_SKIP; + } + + data.add( + FrameworkStatsLog.buildStatsEvent( + FrameworkStatsLog.ADVANCED_PROTECTION_STATE_INFO, + /*enabled*/ isAdvancedProtectionEnabledInternal(), + /*hours_since_enabled*/ hoursSinceLastChange())); + return StatsManager.PULL_SUCCESS; + } + } } diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java index 798c794edaf5..0f6cc24f1fc9 100644 --- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java +++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java @@ -87,6 +87,7 @@ import android.service.quicksettings.TileService; import android.text.TextUtils; import android.util.ArrayMap; import android.util.IndentingPrintWriter; +import android.util.IntArray; import android.util.Pair; import android.util.Slog; import android.util.SparseArray; @@ -102,6 +103,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.inputmethod.SoftInputShowHideReason; import com.android.internal.logging.InstanceId; import com.android.internal.os.TransferPipe; +import com.android.internal.statusbar.DisableStates; import com.android.internal.statusbar.IAddTileResultCallback; import com.android.internal.statusbar.ISessionListener; import com.android.internal.statusbar.IStatusBar; @@ -124,6 +126,7 @@ import com.android.server.policy.GlobalActionsProvider; import com.android.server.power.ShutdownCheckPoints; import com.android.server.power.ShutdownThread; import com.android.server.wm.ActivityTaskManagerInternal; +import com.android.systemui.shared.Flags; import java.io.FileDescriptor; import java.io.PrintWriter; @@ -1344,48 +1347,76 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D return mTracingEnabled; } - // TODO(b/117478341): make it aware of multi-display if needed. + /** + * Disable status bar features. Pass the bitwise-or of the {@code #DISABLE_*} flags. + * To re-enable everything, pass {@code #DISABLE_NONE}. + * + * Warning: Only pass {@code #DISABLE_*} flags into this function, do not use + * {@code #DISABLE2_*} flags. + */ @Override public void disable(int what, IBinder token, String pkg) { disableForUser(what, token, pkg, mCurrentUserId); } - // TODO(b/117478341): make it aware of multi-display if needed. + /** + * Disable status bar features for a given user. Pass the bitwise-or of the + * {@code #DISABLE_*} flags. To re-enable everything, pass {@code #DISABLE_NONE}. + * + * Warning: Only pass {@code #DISABLE_*} flags into this function, do not use + * {@code #DISABLE2_*} flags. + */ @Override public void disableForUser(int what, IBinder token, String pkg, int userId) { enforceStatusBar(); enforceValidCallingUser(); synchronized (mLock) { - disableLocked(DEFAULT_DISPLAY, userId, what, token, pkg, 1); + if (Flags.statusBarConnectedDisplays()) { + IntArray displayIds = new IntArray(); + for (int i = 0; i < mDisplayUiState.size(); i++) { + displayIds.add(mDisplayUiState.keyAt(i)); + } + disableAllDisplaysLocked(displayIds, userId, what, token, pkg, /* whichFlag= */ 1); + } else { + disableLocked(DEFAULT_DISPLAY, userId, what, token, pkg, /* whichFlag= */ 1); + } } } - // TODO(b/117478341): make it aware of multi-display if needed. /** - * Disable additional status bar features. Pass the bitwise-or of the DISABLE2_* flags. - * To re-enable everything, pass {@link #DISABLE2_NONE}. + * Disable additional status bar features. Pass the bitwise-or of the {@code #DISABLE2_*} flags. + * To re-enable everything, pass {@code #DISABLE2_NONE}. * - * Warning: Only pass DISABLE2_* flags into this function, do not use DISABLE_* flags. + * Warning: Only pass {@code #DISABLE2_*} flags into this function, do not use + * {@code #DISABLE_*} flags. */ @Override public void disable2(int what, IBinder token, String pkg) { disable2ForUser(what, token, pkg, mCurrentUserId); } - // TODO(b/117478341): make it aware of multi-display if needed. /** - * Disable additional status bar features for a given user. Pass the bitwise-or of the - * DISABLE2_* flags. To re-enable everything, pass {@link #DISABLE_NONE}. + * Disable additional status bar features for a given user. Pass the bitwise-or + * of the {@code #DISABLE2_*} flags. To re-enable everything, pass {@code #DISABLE2_NONE}. * - * Warning: Only pass DISABLE2_* flags into this function, do not use DISABLE_* flags. + * Warning: Only pass {@code #DISABLE2_*} flags into this function, do not use + * {@code #DISABLE_*} flags. */ @Override public void disable2ForUser(int what, IBinder token, String pkg, int userId) { enforceStatusBar(); synchronized (mLock) { - disableLocked(DEFAULT_DISPLAY, userId, what, token, pkg, 2); + if (Flags.statusBarConnectedDisplays()) { + IntArray displayIds = new IntArray(); + for (int i = 0; i < mDisplayUiState.size(); i++) { + displayIds.add(mDisplayUiState.keyAt(i)); + } + disableAllDisplaysLocked(displayIds, userId, what, token, pkg, /* whichFlag= */ 2); + } else { + disableLocked(DEFAULT_DISPLAY, userId, what, token, pkg, /* whichFlag= */ 2); + } } } @@ -1414,6 +1445,42 @@ public class StatusBarManagerService extends IStatusBarService.Stub implements D } } + // This method batches disable state across all displays into a single remote call + // (IStatusBar#disableForAllDisplays) for efficiency and calls + // NotificationDelegate#onSetDisabled only if any display's disable state changes. + private void disableAllDisplaysLocked(IntArray displayIds, int userId, int what, IBinder token, + String pkg, int whichFlag) { + // It's important that the the callback and the call to mBar get done + // in the same order when multiple threads are calling this function + // so they are paired correctly. The messages on the handler will be + // handled in the order they were enqueued, but will be outside the lock. + manageDisableListLocked(userId, what, token, pkg, whichFlag); + + // Ensure state for the current user is applied, even if passed a non-current user. + final int net1 = gatherDisableActionsLocked(mCurrentUserId, 1); + final int net2 = gatherDisableActionsLocked(mCurrentUserId, 2); + + IStatusBar bar = mBar; + Map<Integer, Pair<Integer, Integer>> displaysWithNewDisableStates = new HashMap<>(); + for (int displayId : displayIds.toArray()) { + final UiState state = getUiState(displayId); + if (!state.disableEquals(net1, net2)) { + state.setDisabled(net1, net2); + displaysWithNewDisableStates.put(displayId, new Pair(net1, net2)); + } + } + if (bar != null) { + try { + bar.disableForAllDisplays(new DisableStates(displaysWithNewDisableStates)); + } catch (RemoteException ex) { + Slog.e(TAG, "Unable to disable Status bar.", ex); + } + } + if (!displaysWithNewDisableStates.isEmpty()) { + mHandler.post(() -> mNotificationDelegate.onSetDisabled(net1)); + } + } + /** * Get the currently applied disable flags, in the form of one Pair<Integer, Integer>. * diff --git a/services/core/java/com/android/server/wallpaper/WallpaperCropper.java b/services/core/java/com/android/server/wallpaper/WallpaperCropper.java index 424439df3c4b..e22bc7d0719e 100644 --- a/services/core/java/com/android/server/wallpaper/WallpaperCropper.java +++ b/services/core/java/com/android/server/wallpaper/WallpaperCropper.java @@ -863,7 +863,7 @@ public class WallpaperCropper { double maxDisplayToImageRatio = Math.max((double) displaySize.x / croppedImageBound.width(), (double) displaySize.y / croppedImageBound.height()); - if (maxDisplayToImageRatio > 1.3) { + if (maxDisplayToImageRatio > 1.5) { return false; } diff --git a/services/core/java/com/android/server/wm/AbsAppSnapshotController.java b/services/core/java/com/android/server/wm/AbsAppSnapshotController.java index a731bf7c64b3..70fc6bace868 100644 --- a/services/core/java/com/android/server/wm/AbsAppSnapshotController.java +++ b/services/core/java/com/android/server/wm/AbsAppSnapshotController.java @@ -82,6 +82,7 @@ abstract class AbsAppSnapshotController<TYPE extends WindowContainer, */ @VisibleForTesting static final int SNAPSHOT_MODE_NONE = 2; + static final float THEME_SNAPSHOT_MIN_Length = 128.0f; protected final WindowManagerService mService; protected final float mHighResSnapshotScale; @@ -436,14 +437,21 @@ abstract class AbsAppSnapshotController<TYPE extends WindowContainer, final Rect taskBounds = source.getBounds(); final InsetsState insetsState = mainWindow.getInsetsStateWithVisibilityOverride(); final Rect systemBarInsets = getSystemBarInsets(mainWindow.getFrame(), insetsState); + final int taskWidth = taskBounds.width(); + final int taskHeight = taskBounds.height(); + float scale = mHighResSnapshotScale; + if (Flags.reduceTaskSnapshotMemoryUsage()) { + final int minLength = Math.min(taskWidth, taskHeight); + if (THEME_SNAPSHOT_MIN_Length < minLength) { + scale = Math.min(THEME_SNAPSHOT_MIN_Length / minLength, scale); + } + } final SnapshotDrawerUtils.SystemBarBackgroundPainter decorPainter = new SnapshotDrawerUtils.SystemBarBackgroundPainter(attrs.flags, attrs.privateFlags, attrs.insetsFlags.appearance, taskDescription, - mHighResSnapshotScale, mainWindow.getRequestedVisibleTypes()); - final int taskWidth = taskBounds.width(); - final int taskHeight = taskBounds.height(); - final int width = (int) (taskWidth * mHighResSnapshotScale); - final int height = (int) (taskHeight * mHighResSnapshotScale); + scale, mainWindow.getRequestedVisibleTypes()); + final int width = (int) (taskWidth * scale); + final int height = (int) (taskHeight * scale); final RenderNode node = RenderNode.create("SnapshotController", null); node.setLeftTopRightBottom(0, 0, width, height); node.setClipToBounds(false); diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index b76b23161e78..b9ab863a2805 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -158,7 +158,6 @@ import static com.android.server.wm.ActivityRecordProto.FRONT_OF_TASK; import static com.android.server.wm.ActivityRecordProto.IN_SIZE_COMPAT_MODE; import static com.android.server.wm.ActivityRecordProto.IS_ANIMATING; import static com.android.server.wm.ActivityRecordProto.IS_USER_FULLSCREEN_OVERRIDE_ENABLED; -import static com.android.server.wm.ActivityRecordProto.LAST_ALL_DRAWN; import static com.android.server.wm.ActivityRecordProto.LAST_DROP_INPUT_MODE; import static com.android.server.wm.ActivityRecordProto.LAST_SURFACE_SHOWING; import static com.android.server.wm.ActivityRecordProto.MIN_ASPECT_RATIO; @@ -723,7 +722,6 @@ final class ActivityRecord extends WindowToken { private int mNumInterestingWindows; private int mNumDrawnWindows; boolean allDrawn; - private boolean mLastAllDrawn; /** * Solely for reporting to ActivityMetricsLogger. Just tracks whether, the last time this @@ -1148,13 +1146,11 @@ final class ActivityRecord extends WindowToken { if (mAppStopped) { pw.print(prefix); pw.print("mAppStopped="); pw.println(mAppStopped); } - if (mNumInterestingWindows != 0 || mNumDrawnWindows != 0 - || allDrawn || mLastAllDrawn) { + if (mNumInterestingWindows != 0 || mNumDrawnWindows != 0 || allDrawn) { pw.print(prefix); pw.print("mNumInterestingWindows="); pw.print(mNumInterestingWindows); pw.print(" mNumDrawnWindows="); pw.print(mNumDrawnWindows); pw.print(" allDrawn="); pw.print(allDrawn); - pw.print(" lastAllDrawn="); pw.print(mLastAllDrawn); pw.println(")"); } if (mStartingData != null || firstWindowDrawn) { @@ -3665,13 +3661,6 @@ final class ActivityRecord extends WindowToken { if (endTask) { mAtmService.getLockTaskController().clearLockedTask(task); - // This activity was in the top focused root task and this is the last - // activity in that task, give this activity a higher layer so it can stay on - // top before the closing task transition be executed. - if (mayAdjustTop) { - mNeedsZBoost = true; - mDisplayContent.assignWindowLayers(false /* setLayoutNeeded */); - } } } else if (!isState(PAUSING)) { if (mVisibleRequested) { @@ -5155,7 +5144,6 @@ final class ActivityRecord extends WindowToken { void clearAllDrawn() { allDrawn = false; - mLastAllDrawn = false; } /** @@ -6599,35 +6587,6 @@ final class ActivityRecord extends WindowToken { nowVisible = false; } - @Override - void checkAppWindowsReadyToShow() { - if (allDrawn == mLastAllDrawn) { - return; - } - - mLastAllDrawn = allDrawn; - if (!allDrawn) { - return; - } - - setAppLayoutChanges(FINISH_LAYOUT_REDO_ANIM, "checkAppWindowsReadyToShow"); - - // We can now show all of the drawn windows! - if (canShowWindows()) { - showAllWindowsLocked(); - } - } - - /** - * This must be called while inside a transaction. - */ - void showAllWindowsLocked() { - forAllWindows(windowState -> { - if (DEBUG_VISIBILITY) Slog.v(TAG, "performing show on: " + windowState); - windowState.performShowLocked(); - }, false /* traverseTopToBottom */); - } - void updateReportedVisibilityLocked() { if (DEBUG_VISIBILITY) Slog.v(TAG, "Update reported visibility: " + this); final int count = mChildren.size(); @@ -7241,11 +7200,6 @@ final class ActivityRecord extends WindowToken { } @Override - boolean needsZBoost() { - return mNeedsZBoost || super.needsZBoost(); - } - - @Override public SurfaceControl getAnimationLeashParent() { // For transitions in the root pinned task (menu activity) we just let them occur as a child // of the root pinned task. @@ -9393,7 +9347,6 @@ final class ActivityRecord extends WindowToken { proto.write(NUM_INTERESTING_WINDOWS, mNumInterestingWindows); proto.write(NUM_DRAWN_WINDOWS, mNumDrawnWindows); proto.write(ALL_DRAWN, allDrawn); - proto.write(LAST_ALL_DRAWN, mLastAllDrawn); if (mStartingWindow != null) { mStartingWindow.writeIdentifierToProto(proto, STARTING_WINDOW); } diff --git a/services/core/java/com/android/server/wm/AsyncRotationController.java b/services/core/java/com/android/server/wm/AsyncRotationController.java index d3fd0e3199a3..f75b17fa1569 100644 --- a/services/core/java/com/android/server/wm/AsyncRotationController.java +++ b/services/core/java/com/android/server/wm/AsyncRotationController.java @@ -234,7 +234,7 @@ class AsyncRotationController extends FadeAnimationController implements Consume } for (int i = mTargetWindowTokens.size() - 1; i >= 0; i--) { final Operation op = mTargetWindowTokens.valueAt(i); - if (op.mIsCompletionPending || op.mAction == Operation.ACTION_SEAMLESS) { + if (op.mIsCompletionPending || op.mActions == Operation.ACTION_SEAMLESS) { // Skip completed target. And seamless windows use the signal from blast sync. continue; } @@ -264,17 +264,18 @@ class AsyncRotationController extends FadeAnimationController implements Consume op.mDrawTransaction = null; if (DEBUG) Slog.d(TAG, "finishOp merge transaction " + windowToken.getTopChild()); } - if (op.mAction == Operation.ACTION_TOGGLE_IME) { + if (op.mActions == Operation.ACTION_TOGGLE_IME) { if (DEBUG) Slog.d(TAG, "finishOp fade-in IME " + windowToken.getTopChild()); fadeWindowToken(true /* show */, windowToken, ANIMATION_TYPE_TOKEN_TRANSFORM, (type, anim) -> mDisplayContent.getInsetsStateController() .getImeSourceProvider().reportImeDrawnForOrganizer()); - } else if (op.mAction == Operation.ACTION_FADE) { + } else if ((op.mActions & Operation.ACTION_FADE) != 0) { if (DEBUG) Slog.d(TAG, "finishOp fade-in " + windowToken.getTopChild()); // The previous animation leash will be dropped when preparing fade-in animation, so // simply apply new animation without restoring the transformation. fadeWindowToken(true /* show */, windowToken, ANIMATION_TYPE_TOKEN_TRANSFORM); - } else if (op.isValidSeamless()) { + } + if (op.isValidSeamless()) { if (DEBUG) Slog.d(TAG, "finishOp undo seamless " + windowToken.getTopChild()); final SurfaceControl.Transaction t = windowToken.getSyncTransaction(); clearTransform(t, op.mLeash); @@ -339,7 +340,7 @@ class AsyncRotationController extends FadeAnimationController implements Consume } if (mTransitionOp == OP_APP_SWITCH && token.mTransitionController.inTransition()) { final Operation op = mTargetWindowTokens.get(token); - if (op != null && op.mAction == Operation.ACTION_FADE) { + if (op != null && op.mActions == Operation.ACTION_FADE) { // Defer showing to onTransitionFinished(). if (DEBUG) Slog.d(TAG, "Defer completion " + token.getTopChild()); return false; @@ -367,11 +368,12 @@ class AsyncRotationController extends FadeAnimationController implements Consume for (int i = mTargetWindowTokens.size() - 1; i >= 0; i--) { final WindowToken windowToken = mTargetWindowTokens.keyAt(i); final Operation op = mTargetWindowTokens.valueAt(i); - if (op.mAction == Operation.ACTION_FADE || op.mAction == Operation.ACTION_TOGGLE_IME) { + if ((op.mActions & Operation.ACTION_FADE) != 0 + || op.mActions == Operation.ACTION_TOGGLE_IME) { fadeWindowToken(false /* show */, windowToken, ANIMATION_TYPE_TOKEN_TRANSFORM); op.mLeash = windowToken.getAnimationLeash(); if (DEBUG) Slog.d(TAG, "Start fade-out " + windowToken.getTopChild()); - } else if (op.mAction == Operation.ACTION_SEAMLESS) { + } else if (op.mActions == Operation.ACTION_SEAMLESS) { op.mLeash = windowToken.mSurfaceControl; if (DEBUG) Slog.d(TAG, "Start seamless " + windowToken.getTopChild()); } @@ -481,13 +483,13 @@ class AsyncRotationController extends FadeAnimationController implements Consume /** Returns {@code true} if the controller will run fade animations on the window. */ boolean hasFadeOperation(WindowToken token) { final Operation op = mTargetWindowTokens.get(token); - return op != null && op.mAction == Operation.ACTION_FADE; + return op != null && (op.mActions & Operation.ACTION_FADE) != 0; } /** Returns {@code true} if the window is un-rotated to original rotation. */ boolean hasSeamlessOperation(WindowToken token) { final Operation op = mTargetWindowTokens.get(token); - return op != null && op.mAction == Operation.ACTION_SEAMLESS; + return op != null && (op.mActions & Operation.ACTION_SEAMLESS) != 0; } /** @@ -541,7 +543,7 @@ class AsyncRotationController extends FadeAnimationController implements Consume final Operation op = mTargetWindowTokens.valueAt(i); final SurfaceControl leash = op.mLeash; if (leash == null || !leash.isValid()) continue; - if (mHasScreenRotationAnimation && op.mAction == Operation.ACTION_FADE) { + if (mHasScreenRotationAnimation && op.mActions == Operation.ACTION_FADE) { // Hide the windows immediately because a screenshot layer should cover the screen. t.setAlpha(leash, 0f); if (DEBUG) { @@ -707,7 +709,7 @@ class AsyncRotationController extends FadeAnimationController implements Consume * start transaction of rotation transition is applied. */ private boolean canDrawBeforeStartTransaction(Operation op) { - return op.mAction != Operation.ACTION_SEAMLESS; + return (op.mActions & Operation.ACTION_SEAMLESS) == 0; } void dump(PrintWriter pw, String prefix) { @@ -723,14 +725,14 @@ class AsyncRotationController extends FadeAnimationController implements Consume /** The operation to control the rotation appearance associated with window token. */ private static class Operation { @Retention(RetentionPolicy.SOURCE) - @IntDef(value = { ACTION_SEAMLESS, ACTION_FADE, ACTION_TOGGLE_IME }) + @IntDef(flag = true, value = { ACTION_SEAMLESS, ACTION_FADE, ACTION_TOGGLE_IME }) @interface Action {} static final int ACTION_SEAMLESS = 1; - static final int ACTION_FADE = 2; - /** The action to toggle the IME window appearance */ - static final int ACTION_TOGGLE_IME = 3; - final @Action int mAction; + static final int ACTION_FADE = 1 << 1; + /** The action to toggle the IME window appearance. It can only be used exclusively. */ + static final int ACTION_TOGGLE_IME = 1 << 2; + final @Action int mActions; /** The leash of window token. It can be animation leash or the token itself. */ SurfaceControl mLeash; /** Whether the window is drawn before the transition starts. */ @@ -744,17 +746,17 @@ class AsyncRotationController extends FadeAnimationController implements Consume */ SurfaceControl.Transaction mDrawTransaction; - Operation(@Action int action) { - mAction = action; + Operation(@Action int actions) { + mActions = actions; } boolean isValidSeamless() { - return mAction == ACTION_SEAMLESS && mLeash != null && mLeash.isValid(); + return (mActions & ACTION_SEAMLESS) != 0 && mLeash != null && mLeash.isValid(); } @Override public String toString() { - return "Operation{a=" + mAction + " pending=" + mIsCompletionPending + '}'; + return "Operation{a=" + mActions + " pending=" + mIsCompletionPending + '}'; } } } diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartCallback.java b/services/core/java/com/android/server/wm/BackgroundActivityStartCallback.java index eec648dae24a..826d5fb1c333 100644 --- a/services/core/java/com/android/server/wm/BackgroundActivityStartCallback.java +++ b/services/core/java/com/android/server/wm/BackgroundActivityStartCallback.java @@ -24,6 +24,16 @@ import java.util.Collection; * Callback to decide activity starts and related operations based on originating tokens. */ public interface BackgroundActivityStartCallback { + BackgroundActivityStartCallbackResult RESULT_FALSE = + new BackgroundActivityStartCallbackResult(false, null); + BackgroundActivityStartCallbackResult RESULT_TRUE = + new BackgroundActivityStartCallbackResult(true, null); + + record BackgroundActivityStartCallbackResult( + boolean allowed, + IBinder token + ) {} + /** * Returns true if the background activity start originating from {@code tokens} should be * allowed or not. @@ -34,7 +44,8 @@ public interface BackgroundActivityStartCallback { * This will be called holding the WM and local lock, don't do anything costly or invoke AM/WM * methods here directly. */ - boolean isActivityStartAllowed(Collection<IBinder> tokens, int uid, String packageName); + BackgroundActivityStartCallbackResult isActivityStartAllowed(Collection<IBinder> tokens, + int uid, String packageName); /** * Returns whether {@code uid} can send {@link android.content.Intent diff --git a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java index f50a68cc5389..f8a50b3fda04 100644 --- a/services/core/java/com/android/server/wm/BackgroundActivityStartController.java +++ b/services/core/java/com/android/server/wm/BackgroundActivityStartController.java @@ -51,6 +51,7 @@ import static com.android.window.flags.Flags.balRequireOptInByPendingIntentCreat import static com.android.window.flags.Flags.balShowToastsBlocked; import static com.android.window.flags.Flags.balStrictModeGracePeriod; import static com.android.window.flags.Flags.balStrictModeRo; +import static com.android.window.flags.Flags.balAdditionalLogging; import static java.lang.annotation.RetentionPolicy.SOURCE; import static java.util.Objects.requireNonNull; @@ -1939,6 +1940,7 @@ public class BackgroundActivityStartController { } } logIfOnlyAllowedBy(finalVerdict, state, BAL_ALLOW_NON_APP_VISIBLE_WINDOW); + logIfOnlyAllowedBy(finalVerdict, state, BAL_ALLOW_TOKEN); if (balImprovedMetrics()) { if (shouldLogStats(finalVerdict, state)) { @@ -1998,8 +2000,10 @@ public class BackgroundActivityStartController { return false; } else { // log to determine grace period length distribution - Slog.wtf(TAG, "Activity start ONLY allowed by " + balCodeToString(balCode) + " " - + finalVerdict.mMessage + ": " + state); + if (balAdditionalLogging()) { + Slog.wtf(TAG, "Activity start ONLY allowed by " + balCodeToString(balCode) + " " + + finalVerdict.mMessage + ": " + state); + } return true; } } diff --git a/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java b/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java index 31b239421baf..2605310db6f4 100644 --- a/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java +++ b/services/core/java/com/android/server/wm/BackgroundLaunchProcessController.java @@ -128,11 +128,16 @@ class BackgroundLaunchProcessController { return new BalVerdict(BAL_ALLOW_PERMISSION, /*background*/ "process instrumenting with background activity starts privileges"); } - // Allow if the flag was explicitly set. - if (checkConfiguration.checkOtherExemptions && isBackgroundStartAllowedByToken(uid, - packageName, checkConfiguration.isCheckingForFgsStart)) { - return new BalVerdict(balImprovedMetrics() ? BAL_ALLOW_TOKEN : BAL_ALLOW_PERMISSION, - /*background*/ "process allowed by token"); + // Allow if the token is explicitly allowed. + if (checkConfiguration.checkOtherExemptions) { + BalVerdict tokenVerdict = isBackgroundStartAllowedByToken(uid, + packageName, checkConfiguration.isCheckingForFgsStart); + if (tokenVerdict.allows()) { + if (!balImprovedMetrics()) { + return new BalVerdict(BAL_ALLOW_PERMISSION, tokenVerdict.toString()); + } + return tokenVerdict; + } } // Allow if the caller is bound by a UID that's currently foreground. // But still respect the appSwitchState. @@ -174,42 +179,53 @@ class BackgroundLaunchProcessController { * isCheckingForFgsStart is false, we ask the callback if the start is allowed for these tokens, * otherwise if there is no callback we allow. */ - private boolean isBackgroundStartAllowedByToken(int uid, String packageName, + private BalVerdict isBackgroundStartAllowedByToken(int uid, String packageName, boolean isCheckingForFgsStart) { synchronized (this) { if (mBackgroundStartPrivileges == null || mBackgroundStartPrivileges.isEmpty()) { // no tokens to allow anything - return false; + return BalVerdict.BLOCK; } if (isCheckingForFgsStart) { // check if any token allows foreground service starts for (int i = mBackgroundStartPrivileges.size(); i-- > 0; ) { if (mBackgroundStartPrivileges.valueAt(i).allowsBackgroundFgsStarts()) { - return true; + return new BalVerdict(BAL_ALLOW_TOKEN, "process allowed by token"); } } - return false; + return BalVerdict.BLOCK; } if (mBackgroundActivityStartCallback == null) { // without a callback just check if any token allows background activity starts for (int i = mBackgroundStartPrivileges.size(); i-- > 0; ) { if (mBackgroundStartPrivileges.valueAt(i) .allowsBackgroundActivityStarts()) { - return true; + return new BalVerdict(BAL_ALLOW_TOKEN, "process allowed by token"); } } - return false; + return BalVerdict.BLOCK; } List<IBinder> binderTokens = getOriginatingTokensThatAllowBal(); if (binderTokens.isEmpty()) { // no tokens to allow anything - return false; + return BalVerdict.BLOCK; } // The callback will decide. - return mBackgroundActivityStartCallback.isActivityStartAllowed( + BackgroundActivityStartCallback.BackgroundActivityStartCallbackResult + activityStartAllowed = mBackgroundActivityStartCallback.isActivityStartAllowed( binderTokens, uid, packageName); + if (!activityStartAllowed.allowed()) { + return BalVerdict.BLOCK; + } + if (activityStartAllowed.token() == null) { + return new BalVerdict(BAL_ALLOW_TOKEN, + "process allowed by callback (token ignored) tokens: " + binderTokens); + } + return new BalVerdict(BAL_ALLOW_TOKEN, + "process allowed by callback (token: " + activityStartAllowed.token() + + ") tokens: " + binderTokens); } } diff --git a/services/core/java/com/android/server/wm/DesktopAppCompatAspectRatioPolicy.java b/services/core/java/com/android/server/wm/DesktopAppCompatAspectRatioPolicy.java index fee5566af484..d8087265c1d3 100644 --- a/services/core/java/com/android/server/wm/DesktopAppCompatAspectRatioPolicy.java +++ b/services/core/java/com/android/server/wm/DesktopAppCompatAspectRatioPolicy.java @@ -69,11 +69,11 @@ public class DesktopAppCompatAspectRatioPolicy { * Calculates the final aspect ratio of an launching activity based on the task it will be * launched in. Takes into account any min or max aspect ratio constraints. */ - float calculateAspectRatio(@NonNull Task task) { + float calculateAspectRatio(@NonNull Task task, boolean hasOrientationMismatch) { final float maxAspectRatio = getMaxAspectRatio(); final float minAspectRatio = getMinAspectRatio(task); float desiredAspectRatio = 0; - desiredAspectRatio = getDesiredAspectRatio(task); + desiredAspectRatio = getDesiredAspectRatio(task, hasOrientationMismatch); if (maxAspectRatio >= 1 && desiredAspectRatio > maxAspectRatio) { desiredAspectRatio = maxAspectRatio; } else if (minAspectRatio >= 1 && desiredAspectRatio < minAspectRatio) { @@ -87,13 +87,14 @@ public class DesktopAppCompatAspectRatioPolicy { * any min or max aspect ratio constraints. */ @VisibleForTesting - float getDesiredAspectRatio(@NonNull Task task) { + float getDesiredAspectRatio(@NonNull Task task, boolean hasOrientationMismatch) { final float letterboxAspectRatioOverride = getFixedOrientationLetterboxAspectRatio(task); // Aspect ratio as suggested by the system. Apps requested mix/max aspect ratio will // be respected in #calculateAspectRatio. if (isDefaultMultiWindowLetterboxAspectRatioDesired(task)) { return DEFAULT_LETTERBOX_ASPECT_RATIO_FOR_MULTI_WINDOW; - } else if (letterboxAspectRatioOverride > MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO) { + } else if (hasOrientationMismatch + && letterboxAspectRatioOverride > MIN_FIXED_ORIENTATION_LETTERBOX_ASPECT_RATIO) { return letterboxAspectRatioOverride; } return AppCompatUtils.computeAspectRatio(task.getDisplayArea().getBounds()); diff --git a/services/core/java/com/android/server/wm/DesktopModeBoundsCalculator.java b/services/core/java/com/android/server/wm/DesktopModeBoundsCalculator.java index d9354323ae7c..83ca5f6f83f4 100644 --- a/services/core/java/com/android/server/wm/DesktopModeBoundsCalculator.java +++ b/services/core/java/com/android/server/wm/DesktopModeBoundsCalculator.java @@ -17,7 +17,6 @@ package com.android.server.wm; import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LOCKED; -import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; import static android.content.pm.ActivityInfo.isFixedOrientation; import static android.content.pm.ActivityInfo.isFixedOrientationLandscape; import static android.content.pm.ActivityInfo.isFixedOrientationPortrait; @@ -32,8 +31,8 @@ import static com.android.server.wm.LaunchParamsUtil.calculateLayoutBounds; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityOptions; -import android.content.pm.ActivityInfo.ScreenOrientation; import android.content.pm.ActivityInfo.WindowLayout; +import android.content.res.Configuration; import android.graphics.Rect; import android.os.SystemProperties; import android.util.Size; @@ -152,19 +151,25 @@ public final class DesktopModeBoundsCalculator { } final DesktopAppCompatAspectRatioPolicy desktopAppCompatAspectRatioPolicy = activity.mAppCompatController.getDesktopAspectRatioPolicy(); - float appAspectRatio = desktopAppCompatAspectRatioPolicy.calculateAspectRatio(task); + final int stableBoundsOrientation = stableBounds.height() >= stableBounds.width() + ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE; + int activityOrientation = getActivityConfigurationOrientation( + activity, task, stableBoundsOrientation); + // Use orientation mismatch to resolve aspect ratio to match fixed orientation letterboxing + // policy in {@link ActivityRecord.resolveFixedOrientationConfiguration} + final boolean hasOrientationMismatch = stableBoundsOrientation != activityOrientation; + float appAspectRatio = desktopAppCompatAspectRatioPolicy.calculateAspectRatio( + task, hasOrientationMismatch); final float tdaWidth = stableBounds.width(); final float tdaHeight = stableBounds.height(); - final int taskConfigOrientation = task.getConfiguration().orientation; - final int activityOrientation = getActivityOrientation(activity, task); - final Size initialSize = switch (taskConfigOrientation) { + final Size initialSize = switch (stableBoundsOrientation) { case ORIENTATION_LANDSCAPE -> { // Device in landscape orientation. if (appAspectRatio == 0) { appAspectRatio = 1; } if (canChangeAspectRatio(desktopAppCompatAspectRatioPolicy, task)) { - if (isFixedOrientationPortrait(activityOrientation)) { + if (hasOrientationMismatch) { // For portrait resizeable activities, respect apps fullscreen width but // apply ideal size height. yield new Size((int) ((tdaHeight / appAspectRatio) + 0.5f), @@ -183,7 +188,7 @@ public final class DesktopModeBoundsCalculator { final int customPortraitWidthForLandscapeApp = screenBounds.width() - (DESKTOP_MODE_LANDSCAPE_APP_PADDING * 2); if (canChangeAspectRatio(desktopAppCompatAspectRatioPolicy, task)) { - if (isFixedOrientationLandscape(activityOrientation)) { + if (hasOrientationMismatch) { if (appAspectRatio == 0) { appAspectRatio = tdaWidth / (tdaWidth - 1); } @@ -198,7 +203,7 @@ public final class DesktopModeBoundsCalculator { if (appAspectRatio == 0) { appAspectRatio = 1; } - if (isFixedOrientationLandscape(activityOrientation)) { + if (hasOrientationMismatch) { // For landscape unresizeable activities, apply custom app width to ideal size // and calculate maximum size with this area while maintaining original aspect // ratio. @@ -230,19 +235,23 @@ public final class DesktopModeBoundsCalculator { && !desktopAppCompatAspectRatioPolicy.hasMinAspectRatioOverride(task); } - private static @ScreenOrientation int getActivityOrientation( - @NonNull ActivityRecord activity, @NonNull Task task) { + private static @Configuration.Orientation int getActivityConfigurationOrientation( + @NonNull ActivityRecord activity, @NonNull Task task, + @Configuration.Orientation int stableBoundsOrientation) { final int activityOrientation = activity.getOverrideOrientation(); final DesktopAppCompatAspectRatioPolicy desktopAppCompatAspectRatioPolicy = activity.mAppCompatController.getDesktopAspectRatioPolicy(); - if (desktopAppCompatAspectRatioPolicy.shouldApplyUserMinAspectRatioOverride(task) + if ((desktopAppCompatAspectRatioPolicy.shouldApplyUserMinAspectRatioOverride(task) && (!isFixedOrientation(activityOrientation) - || activityOrientation == SCREEN_ORIENTATION_LOCKED)) { + || activityOrientation == SCREEN_ORIENTATION_LOCKED)) + || isFixedOrientationPortrait(activityOrientation)) { // If a user aspect ratio override should be applied, treat the activity as portrait if // it has not specified a fix orientation. - return SCREEN_ORIENTATION_PORTRAIT; + return ORIENTATION_PORTRAIT; } - return activityOrientation; + // If activity orientation is undefined inherit task orientation. + return isFixedOrientationLandscape(activityOrientation) + ? ORIENTATION_LANDSCAPE : stableBoundsOrientation; } /** @@ -252,7 +261,7 @@ public final class DesktopModeBoundsCalculator { // TODO(b/400617906): Merge duplicate initial bounds calculations to shared class. @NonNull private static Size maximizeSizeGivenAspectRatio( - @ScreenOrientation int orientation, + @Configuration.Orientation int orientation, @NonNull Size targetArea, float aspectRatio, int captionHeight @@ -261,7 +270,7 @@ public final class DesktopModeBoundsCalculator { final int targetWidth = targetArea.getWidth(); final int finalHeight; final int finalWidth; - if (isFixedOrientationPortrait(orientation)) { + if (orientation == ORIENTATION_PORTRAIT) { // Portrait activity. // Calculate required width given ideal height and aspect ratio. int tempWidth = (int) (targetHeight / aspectRatio); diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java index 6718ae435cd9..d7d5b44ed210 100644 --- a/services/core/java/com/android/server/wm/DisplayArea.java +++ b/services/core/java/com/android/server/wm/DisplayArea.java @@ -332,12 +332,6 @@ public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> { } @Override - boolean needsZBoost() { - // Z Boost should only happen at or below the ActivityStack level. - return false; - } - - @Override boolean fillsParent() { return true; } diff --git a/services/core/java/com/android/server/wm/OWNERS b/services/core/java/com/android/server/wm/OWNERS index 243a5326b545..0989fc05e0bb 100644 --- a/services/core/java/com/android/server/wm/OWNERS +++ b/services/core/java/com/android/server/wm/OWNERS @@ -26,7 +26,7 @@ mcarli@google.com per-file Background*Start* = set noparent per-file Background*Start* = file:/BAL_OWNERS per-file Background*Start* = ogunwale@google.com, louischang@google.com -per-file BackgroundLaunchProcessController.java = file:/BAL_OWNERS +per-file BackgroundLaunchProcessController*.java = file:/BAL_OWNERS # File related to activity callers per-file ActivityCallerState.java = file:/core/java/android/app/COMPONENT_CALLER_OWNERS diff --git a/services/core/java/com/android/server/wm/SnapshotPersistQueue.java b/services/core/java/com/android/server/wm/SnapshotPersistQueue.java index eafc8be7bf77..016cebac2b86 100644 --- a/services/core/java/com/android/server/wm/SnapshotPersistQueue.java +++ b/services/core/java/com/android/server/wm/SnapshotPersistQueue.java @@ -24,6 +24,10 @@ import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; import android.annotation.NonNull; import android.graphics.Bitmap; +import android.graphics.PixelFormat; +import android.hardware.HardwareBuffer; +import android.media.Image; +import android.media.ImageReader; import android.os.Process; import android.os.SystemClock; import android.os.Trace; @@ -33,10 +37,12 @@ import android.window.TaskSnapshot; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.policy.TransitionAnimation; import com.android.server.LocalServices; import com.android.server.pm.UserManagerInternal; import com.android.server.wm.BaseAppSnapshotPersister.PersistInfoProvider; import com.android.server.wm.nano.WindowManagerProtos.TaskSnapshotProto; +import com.android.window.flags.Flags; import java.io.File; import java.io.FileOutputStream; @@ -400,23 +406,20 @@ class SnapshotPersistQueue { Slog.e(TAG, "Invalid task snapshot hw buffer, taskId=" + mId); return false; } - final Bitmap bitmap = Bitmap.wrapHardwareBuffer( - mSnapshot.getHardwareBuffer(), mSnapshot.getColorSpace()); - if (bitmap == null) { - Slog.e(TAG, "Invalid task snapshot hw bitmap"); - return false; - } - final Bitmap swBitmap = bitmap.copy(Bitmap.Config.ARGB_8888, false /* isMutable */); + final HardwareBuffer hwBuffer = mSnapshot.getHardwareBuffer(); + final int width = hwBuffer.getWidth(); + final int height = hwBuffer.getHeight(); + final int pixelFormat = hwBuffer.getFormat(); + final Bitmap swBitmap = !Flags.reduceTaskSnapshotMemoryUsage() + || (pixelFormat != PixelFormat.RGB_565 && pixelFormat != PixelFormat.RGBA_8888) + || !mSnapshot.isRealSnapshot() + || TransitionAnimation.hasProtectedContent(hwBuffer) + ? copyToSwBitmapReadBack() + : copyToSwBitmapDirect(width, height, pixelFormat); if (swBitmap == null) { - Slog.e(TAG, "Bitmap conversion from (config=" + bitmap.getConfig() + ", isMutable=" - + bitmap.isMutable() + ") to (config=ARGB_8888, isMutable=false) failed."); return false; } - final int width = bitmap.getWidth(); - final int height = bitmap.getHeight(); - bitmap.recycle(); - final File file = mPersistInfoProvider.getHighResolutionBitmapFile(mId, mUserId); try (FileOutputStream fos = new FileOutputStream(file)) { swBitmap.compress(JPEG, COMPRESS_QUALITY, fos); @@ -448,6 +451,58 @@ class SnapshotPersistQueue { return true; } + private Bitmap copyToSwBitmapReadBack() { + final Bitmap bitmap = Bitmap.wrapHardwareBuffer( + mSnapshot.getHardwareBuffer(), mSnapshot.getColorSpace()); + if (bitmap == null) { + Slog.e(TAG, "Invalid task snapshot hw bitmap"); + return null; + } + + final Bitmap swBitmap = bitmap.copy(Bitmap.Config.ARGB_8888, false /* isMutable */); + if (swBitmap == null) { + Slog.e(TAG, "Bitmap conversion from (config=" + bitmap.getConfig() + + ", isMutable=" + bitmap.isMutable() + + ") to (config=ARGB_8888, isMutable=false) failed."); + return null; + } + bitmap.recycle(); + return swBitmap; + } + + /** + * Use ImageReader to create the software bitmap, so SkImage won't create an extra texture. + */ + private Bitmap copyToSwBitmapDirect(int width, int height, int pixelFormat) { + try (ImageReader ir = ImageReader.newInstance(width, height, + pixelFormat, 1 /* maxImages */)) { + ir.getSurface().attachAndQueueBufferWithColorSpace(mSnapshot.getHardwareBuffer(), + mSnapshot.getColorSpace()); + try (Image image = ir.acquireLatestImage()) { + if (image == null || image.getPlaneCount() < 1) { + Slog.e(TAG, "Image reader cannot acquire image"); + return null; + } + + final Image.Plane[] planes = image.getPlanes(); + if (planes.length != 1) { + Slog.e(TAG, "Image reader cannot get plane"); + return null; + } + final Image.Plane plane = planes[0]; + final int rowPadding = plane.getRowStride() - plane.getPixelStride() + * image.getWidth(); + final Bitmap swBitmap = Bitmap.createBitmap( + image.getWidth() + rowPadding / plane.getPixelStride() /* width */, + image.getHeight() /* height */, + pixelFormat == PixelFormat.RGB_565 + ? Bitmap.Config.RGB_565 : Bitmap.Config.ARGB_8888); + swBitmap.copyPixelsFromBuffer(plane.getBuffer()); + return swBitmap; + } + } + } + @Override public boolean equals(Object o) { if (o == null || getClass() != o.getClass()) return false; diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index 8587b5a9c7ca..ec17d131958b 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -35,8 +35,6 @@ import static android.content.Intent.FLAG_ACTIVITY_RETAIN_IN_RECENTS; import static android.content.Intent.FLAG_ACTIVITY_TASK_ON_HOME; import static android.content.pm.ActivityInfo.FLAG_RELINQUISH_TASK_IDENTITY; import static android.content.pm.ActivityInfo.FLAG_SHOW_FOR_ALL_USERS; -import static android.content.pm.ActivityInfo.FORCE_NON_RESIZE_APP; -import static android.content.pm.ActivityInfo.FORCE_RESIZE_APP; import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_LANDSCAPE_ONLY; import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PORTRAIT_ONLY; import static android.content.pm.ActivityInfo.RESIZE_MODE_FORCE_RESIZABLE_PRESERVE_ORIENTATION; @@ -51,7 +49,6 @@ import static android.view.Display.INVALID_DISPLAY; import static android.view.SurfaceControl.METADATA_TASK_ID; import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING; import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; -import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES; import static android.view.WindowManager.TRANSIT_CLOSE; import static android.view.WindowManager.TRANSIT_FLAG_APP_CRASHED; import static android.view.WindowManager.TRANSIT_OPEN; @@ -132,7 +129,6 @@ import android.app.IActivityController; import android.app.PictureInPictureParams; import android.app.TaskInfo; import android.app.WindowConfiguration; -import android.app.compat.CompatChanges; import android.content.ComponentName; import android.content.Intent; import android.content.pm.ActivityInfo; @@ -514,10 +510,16 @@ class Task extends TaskFragment { boolean mIsPerceptible = false; /** - * Whether the compatibility overrides that change the resizability of the app should be allowed - * for the specific app. + * Whether the task has been forced resizable, which is determined by the + * activity that started this task. */ - boolean mAllowForceResizeOverride = true; + private boolean mForceResizeOverride; + + /** + * Whether the task has been forced non-resizable, which is determined by + * the activity that started this task. + */ + private boolean mForceNonResizeOverride; private static final int TRANSLUCENT_TIMEOUT_MSG = FIRST_ACTIVITY_TASK_MSG + 1; @@ -675,7 +677,6 @@ class Task extends TaskFragment { intent = _intent; mMinWidth = minWidth; mMinHeight = minHeight; - updateAllowForceResizeOverride(); } mAtmService.getTaskChangeNotificationController().notifyTaskCreated(_taskId, realActivity); mHandler = new ActivityTaskHandler(mTaskSupervisor.mLooper); @@ -946,6 +947,7 @@ class Task extends TaskFragment { mCallingPackage = r.launchedFromPackage; mCallingFeatureId = r.launchedFromFeatureId; setIntent(intent != null ? intent : r.intent, info != null ? info : r.info); + updateForceResizeOverrides(r); } setLockTaskAuth(r); } @@ -1038,7 +1040,6 @@ class Task extends TaskFragment { mTaskSupervisor.mRecentTasks.remove(this); mTaskSupervisor.mRecentTasks.add(this); } - updateAllowForceResizeOverride(); } /** Sets the original minimal width and height. */ @@ -1832,6 +1833,17 @@ class Task extends TaskFragment { && supportsMultiWindowInDisplayArea(tda); } + /** Returns true if the task bounds should persist across power cycles. */ + private static boolean persistTaskBounds(@NonNull WindowConfiguration configuration) { + return configuration.getWindowingMode() == WINDOWING_MODE_FREEFORM; + } + + /** Returns true if the nested task is allowed to have independent bounds from its parent. */ + private static boolean allowIndependentBoundsFromParent( + @NonNull WindowConfiguration configuration) { + return configuration.getWindowingMode() == WINDOWING_MODE_FREEFORM; + } + /** * Check whether this task can be launched on the specified display. * @@ -1844,15 +1856,14 @@ class Task extends TaskFragment { -1 /* don't check PID */, -1 /* don't check UID */, this); } - private void updateAllowForceResizeOverride() { - try { - mAllowForceResizeOverride = mAtmService.mContext.getPackageManager().getPropertyAsUser( - PROPERTY_COMPAT_ALLOW_RESIZEABLE_ACTIVITY_OVERRIDES, - getBasePackageName(), null /* className */, mUserId).getBoolean(); - } catch (PackageManager.NameNotFoundException e) { - // Package not found or property not defined, reset to default value. - mAllowForceResizeOverride = true; - } + private void updateForceResizeOverrides(@NonNull ActivityRecord r) { + final AppCompatResizeOverrides resizeOverrides = r.mAppCompatController + .getResizeOverrides(); + mForceResizeOverride = resizeOverrides.shouldOverrideForceResizeApp() + || r.isUniversalResizeable() + || r.mAppCompatController.getAspectRatioOverrides() + .hasFullscreenOverride(); + mForceNonResizeOverride = resizeOverrides.shouldOverrideForceNonResizeApp(); } /** @@ -1996,11 +2007,11 @@ class Task extends TaskFragment { private void onConfigurationChangedInner(Configuration newParentConfig) { // Check if the new configuration supports persistent bounds (eg. is Freeform) and if so // restore the last recorded non-fullscreen bounds. - final boolean prevPersistTaskBounds = getWindowConfiguration().persistTaskBounds(); - boolean nextPersistTaskBounds = - getRequestedOverrideConfiguration().windowConfiguration.persistTaskBounds(); + final boolean prevPersistTaskBounds = persistTaskBounds(getWindowConfiguration()); + boolean nextPersistTaskBounds = persistTaskBounds( + getRequestedOverrideConfiguration().windowConfiguration); if (getRequestedOverrideWindowingMode() == WINDOWING_MODE_UNDEFINED) { - nextPersistTaskBounds = newParentConfig.windowConfiguration.persistTaskBounds(); + nextPersistTaskBounds = persistTaskBounds(newParentConfig.windowConfiguration); } // Only restore to the last non-fullscreen bounds when the requested override bounds // have not been explicitly set already. @@ -2042,7 +2053,7 @@ class Task extends TaskFragment { // If the configuration supports persistent bounds (eg. Freeform), keep track of the // current (non-fullscreen) bounds for persistence. - if (getWindowConfiguration().persistTaskBounds()) { + if (persistTaskBounds(getWindowConfiguration())) { final Rect currentBounds = getRequestedOverrideBounds(); if (!currentBounds.isEmpty()) { setLastNonFullscreenBounds(currentBounds); @@ -2383,33 +2394,48 @@ class Task extends TaskFragment { } void updateOverrideConfigurationFromLaunchBounds() { - // If the task is controlled by another organized task, do not set override - // configurations and let its parent (organized task) to control it; final Task rootTask = getRootTask(); - boolean shouldInheritBounds = rootTask != this && rootTask.isOrganized(); - if (DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue()) { - // Only inherit from organized parent when this task is not organized. - shouldInheritBounds &= !isOrganized(); + final boolean hasParentTask = rootTask != this; + final int windowingMode = getWindowingMode(); + final boolean isNonStandardOrFullscreen = !isActivityTypeStandardOrUndefined() + || windowingMode == WINDOWING_MODE_FULLSCREEN; + if (!Flags.nestedTasksWithIndependentBounds() + && !DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue()) { + final Rect bounds; + if (hasParentTask && rootTask.isOrganized()) { + bounds = null; + } else if (isNonStandardOrFullscreen) { + bounds = isResizeable() ? rootTask.getRequestedOverrideBounds() : null; + } else if (!persistTaskBounds(getWindowConfiguration())) { + bounds = rootTask.getRequestedOverrideBounds(); + } else { + bounds = mLastNonFullscreenBounds; + } + setBounds(bounds); + return; } - final Rect bounds = shouldInheritBounds ? null : getLaunchBounds(); - setBounds(bounds); - } - /** Returns the bounds that should be used to launch this task. */ - Rect getLaunchBounds() { - final Task rootTask = getRootTask(); - if (rootTask == null) { - return null; + // Non-standard/fullscreen unresizable tasks should always inherit. + boolean shouldInheritBounds = isNonStandardOrFullscreen && !isResizeable(); + // Task itself is not organized (e.g. Home), just inherit from its organized parent. + shouldInheritBounds |= hasParentTask && rootTask.isOrganized() && !isOrganized(); + // Nested tasks should inherit when they're not allowed to have independent bounds, such as + // in multi-window split-screen. + shouldInheritBounds |= hasParentTask + && !(allowIndependentBoundsFromParent(getWindowConfiguration()) + && persistTaskBounds(getWindowConfiguration())); + if (shouldInheritBounds) { + setBounds(null); + return; } - - final int windowingMode = getWindowingMode(); - if (!isActivityTypeStandardOrUndefined() - || windowingMode == WINDOWING_MODE_FULLSCREEN) { - return isResizeable() ? rootTask.getRequestedOverrideBounds() : null; - } else if (!getWindowConfiguration().persistTaskBounds()) { - return rootTask.getRequestedOverrideBounds(); + if (!hasParentTask && !persistTaskBounds(getWindowConfiguration())) { + // Non-nested, non-persistable tasks such as PIP or multi-window floating windows. + return; } - return mLastNonFullscreenBounds; + // Non-nested, persisted tasks (e.g. top-level freeform) or nested persisted tasks that + // allow independent bounds from parent (e.g. nested freeform) should use launch-params + // bounds set to |mLastNonFullscreenBounds|. + setBounds(mLastNonFullscreenBounds); } void setRootProcess(WindowProcessController proc) { @@ -2856,17 +2882,8 @@ class Task extends TaskFragment { final boolean forceResizable = mAtmService.mForceResizableActivities && getActivityType() == ACTIVITY_TYPE_STANDARD; if (forceResizable) return true; - - final UserHandle userHandle = UserHandle.getUserHandleForUid(mUserId); - final boolean forceResizableOverride = mAllowForceResizeOverride - && CompatChanges.isChangeEnabled( - FORCE_RESIZE_APP, getBasePackageName(), userHandle); - final boolean forceNonResizableOverride = mAllowForceResizeOverride - && CompatChanges.isChangeEnabled( - FORCE_NON_RESIZE_APP, getBasePackageName(), userHandle); - - if (forceNonResizableOverride) return false; - return forceResizableOverride || ActivityInfo.isResizeableMode(mResizeMode) + if (mForceNonResizeOverride) return false; + return mForceResizeOverride || ActivityInfo.isResizeableMode(mResizeMode) || (mSupportsPictureInPicture && checkPictureInPictureSupport); } @@ -3607,43 +3624,39 @@ class Task extends TaskFragment { int layer = 0; boolean decorSurfacePlaced = false; - // We use two passes as a way to promote children which - // need Z-boosting to the end of the list. for (int j = 0; j < mChildren.size(); ++j) { final WindowContainer wc = mChildren.get(j); wc.assignChildLayers(t); - if (!wc.needsZBoost()) { - // Place the decor surface under any untrusted content. - if (mDecorSurfaceContainer != null - && !mDecorSurfaceContainer.mIsBoosted - && !decorSurfacePlaced - && shouldPlaceDecorSurfaceBelowContainer(wc)) { - mDecorSurfaceContainer.assignLayer(t, layer++); - decorSurfacePlaced = true; - } - wc.assignLayer(t, layer++); - - // Boost the adjacent TaskFragment for dimmer if needed. - final TaskFragment taskFragment = wc.asTaskFragment(); - if (taskFragment != null && taskFragment.isEmbedded() - && taskFragment.hasAdjacentTaskFragment()) { - final int[] nextLayer = { layer }; - taskFragment.forOtherAdjacentTaskFragments(adjacentTf -> { - if (adjacentTf.shouldBoostDimmer()) { - adjacentTf.assignLayer(t, nextLayer[0]++); - } - }); - layer = nextLayer[0]; - } + // Place the decor surface under any untrusted content. + if (mDecorSurfaceContainer != null + && !mDecorSurfaceContainer.mIsBoosted + && !decorSurfacePlaced + && shouldPlaceDecorSurfaceBelowContainer(wc)) { + mDecorSurfaceContainer.assignLayer(t, layer++); + decorSurfacePlaced = true; + } + wc.assignLayer(t, layer++); + + // Boost the adjacent TaskFragment for dimmer if needed. + final TaskFragment taskFragment = wc.asTaskFragment(); + if (taskFragment != null && taskFragment.isEmbedded() + && taskFragment.hasAdjacentTaskFragment()) { + final int[] nextLayer = { layer }; + taskFragment.forOtherAdjacentTaskFragments(adjacentTf -> { + if (adjacentTf.shouldBoostDimmer()) { + adjacentTf.assignLayer(t, nextLayer[0]++); + } + }); + layer = nextLayer[0]; + } - // Place the decor surface just above the owner TaskFragment. - if (mDecorSurfaceContainer != null - && !mDecorSurfaceContainer.mIsBoosted - && !decorSurfacePlaced - && wc == mDecorSurfaceContainer.mOwnerTaskFragment) { - mDecorSurfaceContainer.assignLayer(t, layer++); - decorSurfacePlaced = true; - } + // Place the decor surface just above the owner TaskFragment. + if (mDecorSurfaceContainer != null + && !mDecorSurfaceContainer.mIsBoosted + && !decorSurfacePlaced + && wc == mDecorSurfaceContainer.mOwnerTaskFragment) { + mDecorSurfaceContainer.assignLayer(t, layer++); + decorSurfacePlaced = true; } } @@ -3653,12 +3666,6 @@ class Task extends TaskFragment { mDecorSurfaceContainer.assignLayer(t, layer++); } - for (int j = 0; j < mChildren.size(); ++j) { - final WindowContainer wc = mChildren.get(j); - if (wc.needsZBoost()) { - wc.assignLayer(t, layer++); - } - } if (mOverlayHost != null) { mOverlayHost.setLayer(t, layer++); } diff --git a/services/core/java/com/android/server/wm/TaskDisplayArea.java b/services/core/java/com/android/server/wm/TaskDisplayArea.java index fb7bab4b3e26..1de139696c07 100644 --- a/services/core/java/com/android/server/wm/TaskDisplayArea.java +++ b/services/core/java/com/android/server/wm/TaskDisplayArea.java @@ -48,7 +48,6 @@ import android.content.pm.ActivityInfo.ScreenOrientation; import android.content.res.Configuration; import android.graphics.Color; import android.os.UserHandle; -import android.util.IntArray; import android.util.Slog; import android.view.SurfaceControl; import android.view.WindowManager; @@ -102,9 +101,6 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> { private final ArrayList<WindowContainer> mTmpAlwaysOnTopChildren = new ArrayList<>(); private final ArrayList<WindowContainer> mTmpNormalChildren = new ArrayList<>(); private final ArrayList<WindowContainer> mTmpHomeChildren = new ArrayList<>(); - private final IntArray mTmpNeedsZBoostIndexes = new IntArray(); - - private ArrayList<Task> mTmpTasks = new ArrayList<>(); private ActivityTaskManagerService mAtmService; @@ -740,40 +736,14 @@ final class TaskDisplayArea extends DisplayArea<WindowContainer> { */ private int adjustRootTaskLayer(SurfaceControl.Transaction t, ArrayList<WindowContainer> children, int startLayer) { - mTmpNeedsZBoostIndexes.clear(); final int childCount = children.size(); - boolean hasAdjacentTask = false; for (int i = 0; i < childCount; i++) { final WindowContainer child = children.get(i); - final TaskDisplayArea childTda = child.asTaskDisplayArea(); - final boolean childNeedsZBoost = childTda != null - ? childTda.childrenNeedZBoost() - : child.needsZBoost(); - - if (childNeedsZBoost) { - mTmpNeedsZBoostIndexes.add(i); - continue; - } - - child.assignLayer(t, startLayer++); - } - - final int zBoostSize = mTmpNeedsZBoostIndexes.size(); - for (int i = 0; i < zBoostSize; i++) { - final WindowContainer child = children.get(mTmpNeedsZBoostIndexes.get(i)); child.assignLayer(t, startLayer++); } return startLayer; } - private boolean childrenNeedZBoost() { - final boolean[] needsZBoost = new boolean[1]; - forAllRootTasks(task -> { - needsZBoost[0] |= task.needsZBoost(); - }); - return needsZBoost[0]; - } - void setBackgroundColor(@ColorInt int colorInt) { setBackgroundColor(colorInt, false /* restore */); } diff --git a/services/core/java/com/android/server/wm/TaskFpsCallbackController.java b/services/core/java/com/android/server/wm/TaskFpsCallbackController.java index 8c798759c890..665c5cffd9ff 100644 --- a/services/core/java/com/android/server/wm/TaskFpsCallbackController.java +++ b/services/core/java/com/android/server/wm/TaskFpsCallbackController.java @@ -16,7 +16,6 @@ package com.android.server.wm; -import android.content.Context; import android.os.IBinder; import android.os.RemoteException; import android.window.ITaskFpsCallback; @@ -25,12 +24,10 @@ import java.util.HashMap; final class TaskFpsCallbackController { - private final Context mContext; private final HashMap<IBinder, Long> mTaskFpsCallbacks; private final HashMap<IBinder, IBinder.DeathRecipient> mDeathRecipients; - TaskFpsCallbackController(Context context) { - mContext = context; + TaskFpsCallbackController() { mTaskFpsCallbacks = new HashMap<>(); mDeathRecipients = new HashMap<>(); } diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java index 9b3b4451a746..51c3da098020 100644 --- a/services/core/java/com/android/server/wm/TransitionController.java +++ b/services/core/java/com/android/server/wm/TransitionController.java @@ -1284,13 +1284,14 @@ class TransitionController { // ignore ourself obviously if (mPlayingTransitions.get(i) == transition) continue; if (getIsIndependent(mPlayingTransitions.get(i), transition)) continue; - if (track >= 0) { + if (track < 0) { + track = mPlayingTransitions.get(i).mAnimationTrack; + } else if (track != mPlayingTransitions.get(i).mAnimationTrack) { // At this point, transition overlaps with multiple tracks, so just wait for // everything sync = true; break; } - track = mPlayingTransitions.get(i).mAnimationTrack; } if (sync) { track = 0; diff --git a/services/core/java/com/android/server/wm/WindowAnimator.java b/services/core/java/com/android/server/wm/WindowAnimator.java index 3f2b40c1d7c9..e50545d41655 100644 --- a/services/core/java/com/android/server/wm/WindowAnimator.java +++ b/services/core/java/com/android/server/wm/WindowAnimator.java @@ -18,10 +18,7 @@ package com.android.server.wm; import static com.android.internal.protolog.WmProtoLogGroups.WM_SHOW_TRANSACTIONS; import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_ALL; -import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION; -import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_SCREEN_ROTATION; import static com.android.server.wm.WindowContainer.AnimationFlags.CHILDREN; -import static com.android.server.wm.WindowContainer.AnimationFlags.TRANSITION; import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WINDOW_TRACE; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; @@ -57,9 +54,6 @@ public class WindowAnimator { /** Is any window animating? */ private boolean mLastRootAnimating; - /** True if we are running any animations that require expensive composition. */ - private boolean mRunningExpensiveAnimations; - final Choreographer.FrameCallback mAnimationFrameCallback; /** Time of current animation step. Reset on each iteration */ @@ -138,8 +132,6 @@ public class WindowAnimator { scheduleAnimation(); final RootWindowContainer root = mService.mRoot; - final boolean useShellTransition = root.mTransitionController.isShellTransitionsEnabled(); - final int animationFlags = useShellTransition ? CHILDREN : (TRANSITION | CHILDREN); boolean rootAnimating = false; mCurrentTime = frameTimeNs / TimeUtils.NANOS_PER_MS; if (DEBUG_WINDOW_TRACE) { @@ -164,17 +156,13 @@ public class WindowAnimator { for (int i = 0; i < numDisplays; i++) { final DisplayContent dc = root.getChildAt(i); - - if (!useShellTransition) { - dc.checkAppWindowsReadyToShow(); - } if (accessibilityController.hasCallbacks()) { accessibilityController .recomputeMagnifiedRegionAndDrawMagnifiedRegionBorderIfNeeded( dc.mDisplayId); } - if (dc.isAnimating(animationFlags, ANIMATION_TYPE_ALL)) { + if (dc.isAnimating(CHILDREN, ANIMATION_TYPE_ALL)) { rootAnimating = true; if (!dc.mLastContainsRunningSurfaceAnimator) { dc.mLastContainsRunningSurfaceAnimator = true; @@ -211,11 +199,6 @@ public class WindowAnimator { } mLastRootAnimating = rootAnimating; - // APP_TRANSITION, SCREEN_ROTATION, TYPE_RECENTS are handled by shell transition. - if (!useShellTransition) { - updateRunningExpensiveAnimationsLegacy(); - } - final ArrayList<Runnable> afterPrepareSurfacesRunnables = mAfterPrepareSurfacesRunnables; if (!afterPrepareSurfacesRunnables.isEmpty()) { mAfterPrepareSurfacesRunnables = new ArrayList<>(); @@ -244,21 +227,6 @@ public class WindowAnimator { } } - private void updateRunningExpensiveAnimationsLegacy() { - final boolean runningExpensiveAnimations = - mService.mRoot.isAnimating(TRANSITION | CHILDREN /* flags */, - ANIMATION_TYPE_APP_TRANSITION - | ANIMATION_TYPE_SCREEN_ROTATION /* typesToCheck */); - if (runningExpensiveAnimations && !mRunningExpensiveAnimations) { - mService.mSnapshotController.setPause(true); - mTransaction.setEarlyWakeupStart(); - } else if (!runningExpensiveAnimations && mRunningExpensiveAnimations) { - mService.mSnapshotController.setPause(false); - mTransaction.setEarlyWakeupEnd(); - } - mRunningExpensiveAnimations = runningExpensiveAnimations; - } - public void dumpLocked(PrintWriter pw, String prefix, boolean dumpAll) { final String subPrefix = " " + prefix; diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java index 5cbba355a06f..5b4870b0c0c7 100644 --- a/services/core/java/com/android/server/wm/WindowContainer.java +++ b/services/core/java/com/android/server/wm/WindowContainer.java @@ -279,9 +279,6 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< */ int mTransitFlags; - /** Whether this container should be boosted at the top of all its siblings. */ - @VisibleForTesting boolean mNeedsZBoost; - /** Layer used to constrain the animation to a container's stack bounds. */ SurfaceControl mAnimationBoundsLayer; @@ -1476,14 +1473,6 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< return stillDeferringRemoval; } - /** Checks if all windows in an app are all drawn and shows them if needed. */ - void checkAppWindowsReadyToShow() { - for (int i = mChildren.size() - 1; i >= 0; --i) { - final WindowContainer wc = mChildren.get(i); - wc.checkAppWindowsReadyToShow(); - } - } - /** * Called when this container or one of its descendants changed its requested orientation, and * wants this container to handle it or pass it to its parent. @@ -2744,15 +2733,7 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< for (int j = 0; j < mChildren.size(); ++j) { final WindowContainer wc = mChildren.get(j); wc.assignChildLayers(t); - if (!wc.needsZBoost()) { - wc.assignLayer(t, layer++); - } - } - for (int j = 0; j < mChildren.size(); ++j) { - final WindowContainer wc = mChildren.get(j); - if (wc.needsZBoost()) { - wc.assignLayer(t, layer++); - } + wc.assignLayer(t, layer++); } if (mOverlayHost != null) { mOverlayHost.setLayer(t, layer++); @@ -2764,16 +2745,6 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< scheduleAnimation(); } - boolean needsZBoost() { - if (mNeedsZBoost) return true; - for (int i = 0; i < mChildren.size(); i++) { - if (mChildren.get(i).needsZBoost()) { - return true; - } - } - return false; - } - /** * Write to a protocol buffer output stream. Protocol buffer message definition is at * {@link com.android.server.wm.WindowContainerProto}. @@ -3114,7 +3085,6 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< public void onAnimationLeashLost(Transaction t) { mLastLayer = -1; mAnimationLeash = null; - mNeedsZBoost = false; reassignLayer(t); updateSurfacePosition(t); } @@ -3140,7 +3110,6 @@ class WindowContainer<E extends WindowContainer> extends ConfigurationContainer< protected void onAnimationFinished(@AnimationType int type, AnimationAdapter anim) { doAnimationFinished(type, anim); mWmService.onAnimationFinished(); - mNeedsZBoost = false; } /** diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index fb38c581d222..a9bb690d4e53 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -1449,7 +1449,7 @@ public class WindowManagerService extends IWindowManager.Stub mPresentationController = new PresentationController(); mBlurController = new BlurController(mContext, mPowerManager); - mTaskFpsCallbackController = new TaskFpsCallbackController(mContext); + mTaskFpsCallbackController = new TaskFpsCallbackController(); mAccessibilityController = new AccessibilityController(this); mScreenRecordingCallbackController = new ScreenRecordingCallbackController(this); mSystemPerformanceHinter = new SystemPerformanceHinter(mContext, displayId -> { @@ -2602,6 +2602,14 @@ public class WindowManagerService extends IWindowManager.Stub // in the new out values right now we need to force a layout. mWindowPlacerLocked.performSurfacePlacement(true /* force */); + if (!win.mHaveFrame && displayContent.mWaitingForConfig) { + // We just forcibly triggered the layout, but this could still be intercepted by + // mWaitingForConfig. Here, we are forcefully marking a value for mLayoutSeq to + // ensure that the resize can occur properly later. Otherwise, the window's frame + // will remain empty forever. + win.mLayoutSeq = displayContent.mLayoutSeq; + } + if (shouldRelayout) { Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "relayoutWindow: viewVisibility_1"); diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index af5200102fc0..22ddd5f39b24 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -4995,18 +4995,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP return true; } - @Override - boolean needsZBoost() { - final InsetsControlTarget target = getDisplayContent().getImeTarget(IME_TARGET_LAYERING); - if (mIsImWindow && target != null) { - final ActivityRecord activity = target.getWindow().mActivityRecord; - if (activity != null) { - return activity.needsZBoost(); - } - } - return false; - } - private boolean isStartingWindowAssociatedToTask() { return mStartingData != null && mStartingData.mAssociatedTask != null; } diff --git a/services/credentials/java/com/android/server/credentials/MetricUtilities.java b/services/credentials/java/com/android/server/credentials/MetricUtilities.java index ac4aac694c3a..11edb93dffea 100644 --- a/services/credentials/java/com/android/server/credentials/MetricUtilities.java +++ b/services/credentials/java/com/android/server/credentials/MetricUtilities.java @@ -383,7 +383,9 @@ public class MetricUtilities { /* api_name */ initialPhaseMetric.getApiName(), /* primary_candidates_indicated */ - candidatePrimaryProviderList + candidatePrimaryProviderList, + /* api_prepared */ + initialPhaseMetric.hasApiUsedPrepareFlow() ); } catch (Exception e) { Slog.w(TAG, "Unexpected error during candidate provider uid metric emit: " + e); @@ -442,7 +444,9 @@ public class MetricUtilities { /* autofill_session_id */ initialPhaseMetric.getAutofillSessionId(), /* autofill_request_id */ - initialPhaseMetric.getAutofillRequestId() + initialPhaseMetric.getAutofillRequestId(), + /* api_prepared */ + initialPhaseMetric.hasApiUsedPrepareFlow() ); } catch (Exception e) { Slog.w(TAG, "Unexpected error during initial metric emit: " + e); diff --git a/services/credentials/java/com/android/server/credentials/PrepareGetRequestSession.java b/services/credentials/java/com/android/server/credentials/PrepareGetRequestSession.java index d60807c7b001..2d4360edf3a8 100644 --- a/services/credentials/java/com/android/server/credentials/PrepareGetRequestSession.java +++ b/services/credentials/java/com/android/server/credentials/PrepareGetRequestSession.java @@ -27,6 +27,7 @@ import android.credentials.GetCredentialRequest; import android.credentials.IGetCredentialCallback; import android.credentials.IPrepareGetCredentialCallback; import android.credentials.PrepareGetCredentialResponseInternal; +import android.credentials.flags.Flags; import android.credentials.selection.GetCredentialProviderData; import android.credentials.selection.ProviderData; import android.credentials.selection.RequestInfo; @@ -60,7 +61,12 @@ public class PrepareGetRequestSession extends GetRequestSession { int numTypes = (request.getCredentialOptions().stream() .map(CredentialOption::getType).collect( Collectors.toSet())).size(); // Dedupe type strings - mRequestSessionMetric.collectGetFlowInitialMetricInfo(request); + if (!Flags.fixMetricDuplicationEmits()) { + mRequestSessionMetric.collectGetFlowInitialMetricInfo(request); + } else { + mRequestSessionMetric.collectGetFlowInitialMetricInfo(request, + /*isApiPrepared=*/ true); + } mPrepareGetCredentialCallback = prepareGetCredentialCallback; Slog.i(TAG, "PrepareGetRequestSession constructed."); diff --git a/services/credentials/java/com/android/server/credentials/metrics/InitialPhaseMetric.java b/services/credentials/java/com/android/server/credentials/metrics/InitialPhaseMetric.java index 8a4e86c440b3..811b97a5bf03 100644 --- a/services/credentials/java/com/android/server/credentials/metrics/InitialPhaseMetric.java +++ b/services/credentials/java/com/android/server/credentials/metrics/InitialPhaseMetric.java @@ -55,6 +55,9 @@ public class InitialPhaseMetric { // The request id of autofill if the request is from autofill, defaults to -1 private int mAutofillRequestId = -1; + // Indicates if this API call used the prepare flow, defaults to false + private boolean mApiUsedPrepareFlow = false; + public InitialPhaseMetric(int sessionIdTrackOne) { mSessionIdCaller = sessionIdTrackOne; @@ -173,4 +176,17 @@ public class InitialPhaseMetric { public int[] getUniqueRequestCounts() { return mRequestCounts.values().stream().mapToInt(Integer::intValue).toArray(); } + + /* ------ API Prepared ------ */ + + public void setApiUsedPrepareFlow(boolean apiUsedPrepareFlow) { + mApiUsedPrepareFlow = apiUsedPrepareFlow; + } + + /** + * @return a boolean indicating if this API call utilized a prepare flow + */ + public boolean hasApiUsedPrepareFlow() { + return mApiUsedPrepareFlow; + } } diff --git a/services/credentials/java/com/android/server/credentials/metrics/RequestSessionMetric.java b/services/credentials/java/com/android/server/credentials/metrics/RequestSessionMetric.java index 619a56846e95..dc1747f803ea 100644 --- a/services/credentials/java/com/android/server/credentials/metrics/RequestSessionMetric.java +++ b/services/credentials/java/com/android/server/credentials/metrics/RequestSessionMetric.java @@ -225,6 +225,22 @@ public class RequestSessionMetric { } /** + * Collects initializations for Get flow metrics. + * + * @param request the get credential request containing information to parse for metrics + * @param isApiPrepared indicates this API flow utilized the 'prepare' flow + */ + public void collectGetFlowInitialMetricInfo(GetCredentialRequest request, + boolean isApiPrepared) { + try { + collectGetFlowInitialMetricInfo(request); + mInitialPhaseMetric.setApiUsedPrepareFlow(isApiPrepared); + } catch (Exception e) { + Slog.i(TAG, "Unexpected error collecting get flow initial metric: " + e); + } + } + + /** * During browsing, where multiple entries can be selected, this collects the browsing phase * metric information. This is emitted together with the final phase, and the recursive path * with authentication entries, which may occur in rare circumstances, are captured. diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 788b3b883160..56ec27a0ba87 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -121,6 +121,7 @@ import com.android.internal.util.EmergencyAffordanceManager; import com.android.internal.util.FrameworkStatsLog; import com.android.internal.widget.ILockSettings; import com.android.internal.widget.LockSettingsInternal; +import com.android.modules.utils.build.SdkLevel; import com.android.server.accessibility.AccessibilityManagerService; import com.android.server.accounts.AccountManagerService; import com.android.server.adb.AdbService; @@ -2265,19 +2266,27 @@ public final class SystemServer implements Dumpable { Slog.i(TAG, "Not starting VpnManagerService"); } - t.traceBegin("StartVcnManagementService"); - try { - if (VcnLocation.IS_VCN_IN_MAINLINE) { - mSystemServiceManager.startServiceFromJar( - CONNECTIVITY_SERVICE_INITIALIZER_B_CLASS, - CONNECTIVITY_SERVICE_APEX_PATH); - } else { - mSystemServiceManager.startService(CONNECTIVITY_SERVICE_INITIALIZER_B_CLASS); + // TODO: b/374174952 In the end state, VCN registration will be moved to Tethering + // module. Thus the following code block should be removed after Baklava is released + if (!VcnLocation.IS_VCN_IN_MAINLINE || !SdkLevel.isAtLeastB()) { + t.traceBegin("StartVcnManagementService"); + + try { + if (!VcnLocation.IS_VCN_IN_MAINLINE) { + mSystemServiceManager.startService( + CONNECTIVITY_SERVICE_INITIALIZER_B_CLASS); + } else { + // When VCN is in mainline but the SDK level is B-, start the service with + // the apex path. This path can only be hit on an unfinalized B platform + mSystemServiceManager.startServiceFromJar( + CONNECTIVITY_SERVICE_INITIALIZER_B_CLASS, + CONNECTIVITY_SERVICE_APEX_PATH); + } + } catch (Throwable e) { + reportWtf("starting VCN Management Service", e); } - } catch (Throwable e) { - reportWtf("starting VCN Management Service", e); + t.traceEnd(); } - t.traceEnd(); t.traceBegin("StartSystemUpdateManagerService"); try { diff --git a/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt index 232bb83fdf9f..5a140d53a4d8 100644 --- a/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt +++ b/services/permission/java/com/android/server/permission/access/permission/AppIdPermissionPolicy.kt @@ -1753,6 +1753,13 @@ class AppIdPermissionPolicy : SchemePolicy() { } val appIdPermissionFlags = newState.mutateUserState(userId)!!.mutateAppIdPermissionFlags() val permissionFlags = appIdPermissionFlags.mutateOrPut(appId) { MutableIndexedMap() } + // for debugging possible races TODO(b/401768134) + oldState.userStates[userId]?.appIdPermissionFlags[appId]?.map?.let { + if (permissionFlags.map === it) { + throw IllegalStateException("Unexpected sharing between old/new state") + } + } + permissionFlags.putWithDefault(permissionName, newFlags, 0) if (permissionFlags.isEmpty()) { appIdPermissionFlags -= appId diff --git a/services/tests/displayservicetests/Android.bp b/services/tests/displayservicetests/Android.bp index 36ea24195789..c85053d13e68 100644 --- a/services/tests/displayservicetests/Android.bp +++ b/services/tests/displayservicetests/Android.bp @@ -51,6 +51,7 @@ android_test { data: [ ":DisplayManagerTestApp", + ":TopologyTestApp", ], certificate: "platform", diff --git a/services/tests/displayservicetests/AndroidManifest.xml b/services/tests/displayservicetests/AndroidManifest.xml index 205ff058275a..76f219b7433b 100644 --- a/services/tests/displayservicetests/AndroidManifest.xml +++ b/services/tests/displayservicetests/AndroidManifest.xml @@ -29,6 +29,7 @@ <uses-permission android:name="android.permission.READ_DEVICE_CONFIG" /> <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" /> <uses-permission android:name="android.permission.MANAGE_USB" /> + <uses-permission android:name="android.permission.MANAGE_DISPLAYS" /> <!-- Permissions needed for DisplayTransformManagerTest --> <uses-permission android:name="android.permission.CHANGE_CONFIGURATION" /> diff --git a/services/tests/displayservicetests/AndroidTest.xml b/services/tests/displayservicetests/AndroidTest.xml index f3697bbffd5c..2fe37233870f 100644 --- a/services/tests/displayservicetests/AndroidTest.xml +++ b/services/tests/displayservicetests/AndroidTest.xml @@ -28,6 +28,7 @@ <option name="cleanup-apks" value="true" /> <option name="install-arg" value="-t" /> <option name="test-file-name" value="DisplayManagerTestApp.apk" /> + <option name="test-file-name" value="TopologyTestApp.apk" /> </target_preparer> <option name="test-tag" value="DisplayServiceTests" /> diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayEventDeliveryTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayEventDeliveryTest.java index 1f45792e5097..bf4b61347bab 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/DisplayEventDeliveryTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayEventDeliveryTest.java @@ -16,7 +16,6 @@ package com.android.server.display; -import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED; import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY; import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC; import static android.util.DisplayMetrics.DENSITY_HIGH; @@ -27,19 +26,11 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.fail; import static org.junit.Assume.assumeTrue; -import android.app.ActivityManager; -import android.app.Instrumentation; -import android.content.Context; import android.content.Intent; -import android.hardware.display.DisplayManager; import android.hardware.display.VirtualDisplay; -import android.os.BinderProxy; import android.os.Handler; -import android.os.HandlerThread; import android.os.Looper; import android.os.Message; -import android.os.Messenger; -import android.platform.test.annotations.AppModeSdkSandbox; import android.platform.test.annotations.RequiresFlagsEnabled; import android.platform.test.flag.junit.CheckFlagsRule; import android.platform.test.flag.junit.DeviceFlagsValueProvider; @@ -48,10 +39,7 @@ import android.util.SparseArray; import androidx.annotation.GuardedBy; import androidx.annotation.NonNull; -import androidx.test.platform.app.InstrumentationRegistry; -import com.android.compatibility.common.util.SystemUtil; -import com.android.compatibility.common.util.TestUtils; import com.android.server.am.Flags; import org.junit.After; @@ -63,9 +51,7 @@ import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameter; import org.junit.runners.Parameterized.Parameters; -import java.io.IOException; import java.util.Arrays; -import java.util.concurrent.CountDownLatch; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; @@ -73,8 +59,7 @@ import java.util.concurrent.TimeUnit; * Tests that applications can receive display events correctly. */ @RunWith(Parameterized.class) -@AppModeSdkSandbox(reason = "Allow test in the SDK sandbox (does not prevent other modes).") -public class DisplayEventDeliveryTest { +public class DisplayEventDeliveryTest extends EventDeliveryTestBase { private static final String TAG = "DisplayEventDeliveryTest"; @Rule @@ -85,37 +70,17 @@ public class DisplayEventDeliveryTest { private static final int WIDTH = 720; private static final int HEIGHT = 480; - private static final int MESSAGE_LAUNCHED = 1; - private static final int MESSAGE_CALLBACK = 2; - private static final int DISPLAY_ADDED = 1; private static final int DISPLAY_CHANGED = 2; private static final int DISPLAY_REMOVED = 3; - private static final long DISPLAY_EVENT_TIMEOUT_MSEC = 100; - private static final long TEST_FAILURE_TIMEOUT_MSEC = 10000; - private static final String TEST_PACKAGE = "com.android.servicestests.apps.displaymanagertestapp"; private static final String TEST_ACTIVITY = TEST_PACKAGE + ".DisplayEventActivity"; private static final String TEST_DISPLAYS = "DISPLAYS"; - private static final String TEST_MESSENGER = "MESSENGER"; private final Object mLock = new Object(); - private Instrumentation mInstrumentation; - private Context mContext; - private DisplayManager mDisplayManager; - private ActivityManager mActivityManager; - private ActivityManager.OnUidImportanceListener mUidImportanceListener; - private CountDownLatch mLatchActivityLaunch; - private CountDownLatch mLatchActivityCached; - private HandlerThread mHandlerThread; - private Handler mHandler; - private Messenger mMessenger; - private int mPid; - private int mUid; - /** * Array of DisplayBundle. The test handler uses it to check if certain display events have * been sent to DisplayEventActivity. @@ -167,7 +132,7 @@ public class DisplayEventDeliveryTest { */ public void assertNoDisplayEvents() { try { - assertNull(mExpectations.poll(DISPLAY_EVENT_TIMEOUT_MSEC, TimeUnit.MILLISECONDS)); + assertNull(mExpectations.poll(EVENT_TIMEOUT_MSEC, TimeUnit.MILLISECONDS)); } catch (InterruptedException e) { throw new RuntimeException(e); } @@ -239,37 +204,17 @@ public class DisplayEventDeliveryTest { } @Before - public void setUp() throws Exception { - mInstrumentation = InstrumentationRegistry.getInstrumentation(); - mContext = mInstrumentation.getContext(); - mDisplayManager = mContext.getSystemService(DisplayManager.class); - mLatchActivityLaunch = new CountDownLatch(1); - mLatchActivityCached = new CountDownLatch(1); - mActivityManager = mContext.getSystemService(ActivityManager.class); - mUidImportanceListener = (uid, importance) -> { - if (uid == mUid && importance == IMPORTANCE_CACHED) { - Log.d(TAG, "Listener " + uid + " becomes " + importance); - mLatchActivityCached.countDown(); - } - }; - SystemUtil.runWithShellPermissionIdentity(() -> - mActivityManager.addOnUidImportanceListener(mUidImportanceListener, - IMPORTANCE_CACHED)); + public void setUp() { + super.setUp(); // The lock is not functionally necessary but eliminates lint error messages. synchronized (mLock) { mDisplayBundles = new SparseArray<>(); } - mHandlerThread = new HandlerThread("handler"); - mHandlerThread.start(); - mHandler = new TestHandler(mHandlerThread.getLooper()); - mMessenger = new Messenger(mHandler); - mPid = 0; } @After public void tearDown() throws Exception { - mActivityManager.removeOnUidImportanceListener(mUidImportanceListener); - mHandlerThread.quitSafely(); + super.tearDown(); synchronized (mLock) { for (int i = 0; i < mDisplayBundles.size(); i++) { DisplayBundle bundle = mDisplayBundles.valueAt(i); @@ -278,7 +223,31 @@ public class DisplayEventDeliveryTest { } mDisplayBundles.clear(); } - SystemUtil.runShellCommand(mInstrumentation, "am force-stop " + TEST_PACKAGE); + } + + @Override + protected String getTag() { + return TAG; + } + + @Override + protected Handler getHandler(Looper looper) { + return new TestHandler(looper); + } + + @Override + protected String getTestPackage() { + return TEST_PACKAGE; + } + + @Override + protected String getTestActivity() { + return TEST_ACTIVITY; + } + + @Override + protected void putExtra(Intent intent) { + intent.putExtra(TEST_DISPLAYS, mDisplayCount); } /** @@ -291,42 +260,8 @@ public class DisplayEventDeliveryTest { } /** - * Return true if the freezer is enabled on this platform and if freezer notifications are - * supported. It is not enough to test that the freezer notification feature is enabled - * because some devices do not have the necessary kernel support. - */ - private boolean isAppFreezerEnabled() { - try { - return mActivityManager.getService().isAppFreezerEnabled() - && android.os.Flags.binderFrozenStateChangeCallback() - && BinderProxy.isFrozenStateChangeCallbackSupported(); - } catch (Exception e) { - Log.e(TAG, "isAppFreezerEnabled() failed: " + e); - return false; - } - } - - private void waitForProcessFreeze(int pid, long timeoutMs) { - // TODO: Add a listener to monitor freezer state changes. - SystemUtil.runWithShellPermissionIdentity(() -> { - TestUtils.waitUntil("Timed out waiting for test process to be frozen; pid=" + pid, - (int) TimeUnit.MILLISECONDS.toSeconds(timeoutMs), - () -> mActivityManager.isProcessFrozen(pid)); - }); - } - - private void waitForProcessUnfreeze(int pid, long timeoutMs) { - // TODO: Add a listener to monitor freezer state changes. - SystemUtil.runWithShellPermissionIdentity(() -> { - TestUtils.waitUntil("Timed out waiting for test process to be frozen; pid=" + pid, - (int) TimeUnit.MILLISECONDS.toSeconds(timeoutMs), - () -> !mActivityManager.isProcessFrozen(pid)); - }); - } - - /** - * Create virtual displays, change their configurations and release them. The number of - * displays is set by the {@link #mDisplays} variable. + * Create virtual displays, change their configurations and release them. The number of + * displays is set by the {@link #data()} parameter. */ private void testDisplayEventsInternal(boolean cached, boolean frozen) { Log.d(TAG, "Start test testDisplayEvents " + mDisplayCount + " " + cached + " " + frozen); @@ -445,110 +380,6 @@ public class DisplayEventDeliveryTest { } /** - * Launch the test activity that would listen to display events. Return its process ID. - */ - private int launchTestActivity() { - Intent intent = new Intent(Intent.ACTION_MAIN); - intent.setClassName(TEST_PACKAGE, TEST_ACTIVITY); - intent.putExtra(TEST_MESSENGER, mMessenger); - intent.putExtra(TEST_DISPLAYS, mDisplayCount); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - SystemUtil.runWithShellPermissionIdentity( - () -> { - mContext.startActivity(intent); - }, - android.Manifest.permission.START_ACTIVITIES_FROM_SDK_SANDBOX); - waitLatch(mLatchActivityLaunch); - - try { - String cmd = "pidof " + TEST_PACKAGE; - String result = SystemUtil.runShellCommand(mInstrumentation, cmd); - return Integer.parseInt(result.trim()); - } catch (IOException e) { - fail("failed to get pid of test package"); - return 0; - } catch (NumberFormatException e) { - fail("failed to parse pid " + e); - return 0; - } - } - - /** - * Bring the test activity back to top - */ - private void bringTestActivityTop() { - Intent intent = new Intent(Intent.ACTION_MAIN); - intent.setClassName(TEST_PACKAGE, TEST_ACTIVITY); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); - SystemUtil.runWithShellPermissionIdentity( - () -> { - mContext.startActivity(intent); - }, - android.Manifest.permission.START_ACTIVITIES_FROM_SDK_SANDBOX); - } - - /** - * Bring the test activity into cached mode by launching another 2 apps - */ - private void makeTestActivityCached() { - // Launch another activity to bring the test activity into background - Intent intent = new Intent(Intent.ACTION_MAIN); - intent.setClass(mContext, SimpleActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); - - // Launch another activity to bring the test activity into cached mode - Intent intent2 = new Intent(Intent.ACTION_MAIN); - intent2.setClass(mContext, SimpleActivity2.class); - intent2.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - SystemUtil.runWithShellPermissionIdentity( - () -> { - mInstrumentation.startActivitySync(intent); - mInstrumentation.startActivitySync(intent2); - }, - android.Manifest.permission.START_ACTIVITIES_FROM_SDK_SANDBOX); - waitLatch(mLatchActivityCached); - } - - // Sleep, ignoring interrupts. - private void pause(int s) { - try { Thread.sleep(s * 1000); } catch (Exception e) { } - } - - /** - * Freeze the test activity. - */ - private void makeTestActivityFrozen(int pid) { - // The delay here is meant to allow pending binder transactions to drain. A process - // cannot be frozen if it has pending binder transactions, and attempting to freeze such a - // process more than a few times will result in the system killing the process. - pause(5); - try { - String cmd = "am freeze --sticky "; - SystemUtil.runShellCommand(mInstrumentation, cmd + TEST_PACKAGE); - } catch (IOException e) { - fail(e.toString()); - } - // Wait for the freeze to complete in the kernel and for the frozen process - // notification to settle out. - waitForProcessFreeze(pid, 5 * 1000); - } - - /** - * Freeze the test activity. - */ - private void makeTestActivityUnfrozen(int pid) { - try { - String cmd = "am unfreeze --sticky "; - SystemUtil.runShellCommand(mInstrumentation, cmd + TEST_PACKAGE); - } catch (IOException e) { - fail(e.toString()); - } - // Wait for the freeze to complete in the kernel and for the frozen process - // notification to settle out. - waitForProcessUnfreeze(pid, 5 * 1000); - } - - /** * Create a virtual display * * @param name The name of the new virtual display @@ -560,15 +391,4 @@ public class DisplayEventDeliveryTest { VIRTUAL_DISPLAY_FLAG_PUBLIC | VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY /* flags: a public virtual display that another app can access */); } - - /** - * Wait for CountDownLatch with timeout - */ - private void waitLatch(CountDownLatch latch) { - try { - latch.await(TEST_FAILURE_TIMEOUT_MSEC, TimeUnit.MILLISECONDS); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - } } diff --git a/services/tests/displayservicetests/src/com/android/server/display/EventDeliveryTestBase.java b/services/tests/displayservicetests/src/com/android/server/display/EventDeliveryTestBase.java new file mode 100644 index 000000000000..2911b9bb35c7 --- /dev/null +++ b/services/tests/displayservicetests/src/com/android/server/display/EventDeliveryTestBase.java @@ -0,0 +1,259 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.display; + +import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED; + +import static org.junit.Assert.fail; + +import android.app.ActivityManager; +import android.app.Instrumentation; +import android.content.Context; +import android.content.Intent; +import android.hardware.display.DisplayManager; +import android.os.BinderProxy; +import android.os.Handler; +import android.os.HandlerThread; +import android.os.Looper; +import android.os.Messenger; +import android.platform.test.annotations.AppModeSdkSandbox; +import android.util.Log; + +import androidx.test.platform.app.InstrumentationRegistry; + +import com.android.compatibility.common.util.SystemUtil; +import com.android.compatibility.common.util.TestUtils; + +import java.io.IOException; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +@AppModeSdkSandbox(reason = "Allow test in the SDK sandbox (does not prevent other modes).") +public abstract class EventDeliveryTestBase { + protected static final int MESSAGE_LAUNCHED = 1; + protected static final int MESSAGE_CALLBACK = 2; + + protected static final long EVENT_TIMEOUT_MSEC = 100; + protected static final long TEST_FAILURE_TIMEOUT_MSEC = 10000; + + private static final String TEST_MESSENGER = "MESSENGER"; + + private Instrumentation mInstrumentation; + private Context mContext; + protected DisplayManager mDisplayManager; + private ActivityManager mActivityManager; + private ActivityManager.OnUidImportanceListener mUidImportanceListener; + protected CountDownLatch mLatchActivityLaunch; + private CountDownLatch mLatchActivityCached; + private HandlerThread mHandlerThread; + private Handler mHandler; + private Messenger mMessenger; + protected int mPid; + protected int mUid; + + protected abstract String getTag(); + + protected abstract Handler getHandler(Looper looper); + + protected abstract String getTestPackage(); + + protected abstract String getTestActivity(); + + protected abstract void putExtra(Intent intent); + + protected void setUp() { + mInstrumentation = InstrumentationRegistry.getInstrumentation(); + mContext = mInstrumentation.getContext(); + mDisplayManager = mContext.getSystemService(DisplayManager.class); + mLatchActivityLaunch = new CountDownLatch(1); + mLatchActivityCached = new CountDownLatch(1); + mActivityManager = mContext.getSystemService(ActivityManager.class); + mUidImportanceListener = (uid, importance) -> { + if (uid == mUid && importance == IMPORTANCE_CACHED) { + Log.d(getTag(), "Listener " + uid + " becomes " + importance); + mLatchActivityCached.countDown(); + } + }; + SystemUtil.runWithShellPermissionIdentity(() -> + mActivityManager.addOnUidImportanceListener(mUidImportanceListener, + IMPORTANCE_CACHED)); + mHandlerThread = new HandlerThread("handler"); + mHandlerThread.start(); + mHandler = getHandler(mHandlerThread.getLooper()); + mMessenger = new Messenger(mHandler); + mPid = 0; + } + + protected void tearDown() throws Exception { + mActivityManager.removeOnUidImportanceListener(mUidImportanceListener); + mHandlerThread.quitSafely(); + SystemUtil.runShellCommand(mInstrumentation, "am force-stop " + getTestPackage()); + } + + /** + * Return true if the freezer is enabled on this platform and if freezer notifications are + * supported. It is not enough to test that the freezer notification feature is enabled + * because some devices do not have the necessary kernel support. + */ + protected boolean isAppFreezerEnabled() { + try { + return ActivityManager.getService().isAppFreezerEnabled() + && android.os.Flags.binderFrozenStateChangeCallback() + && BinderProxy.isFrozenStateChangeCallbackSupported(); + } catch (Exception e) { + Log.e(getTag(), "isAppFreezerEnabled() failed: " + e); + return false; + } + } + + private void waitForProcessFreeze(int pid, long timeoutMs) { + // TODO: Add a listener to monitor freezer state changes. + SystemUtil.runWithShellPermissionIdentity(() -> { + TestUtils.waitUntil( + "Timed out waiting for test process to be frozen; pid=" + pid, + (int) TimeUnit.MILLISECONDS.toSeconds(timeoutMs), + () -> mActivityManager.isProcessFrozen(pid)); + }); + } + + private void waitForProcessUnfreeze(int pid, long timeoutMs) { + // TODO: Add a listener to monitor freezer state changes. + SystemUtil.runWithShellPermissionIdentity(() -> { + TestUtils.waitUntil("Timed out waiting for test process to be frozen; pid=" + pid, + (int) TimeUnit.MILLISECONDS.toSeconds(timeoutMs), + () -> !mActivityManager.isProcessFrozen(pid)); + }); + } + + /** + * Launch the test activity that would listen to events. Return its process ID. + */ + protected int launchTestActivity() { + Intent intent = new Intent(Intent.ACTION_MAIN); + intent.setClassName(getTestPackage(), getTestActivity()); + intent.putExtra(TEST_MESSENGER, mMessenger); + putExtra(intent); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + SystemUtil.runWithShellPermissionIdentity( + () -> { + mContext.startActivity(intent); + }, + android.Manifest.permission.START_ACTIVITIES_FROM_SDK_SANDBOX); + waitLatch(mLatchActivityLaunch); + + try { + String cmd = "pidof " + getTestPackage(); + String result = SystemUtil.runShellCommand(mInstrumentation, cmd); + return Integer.parseInt(result.trim()); + } catch (IOException e) { + fail("failed to get pid of test package"); + return 0; + } catch (NumberFormatException e) { + fail("failed to parse pid " + e); + return 0; + } + } + + /** + * Bring the test activity back to top + */ + protected void bringTestActivityTop() { + Intent intent = new Intent(Intent.ACTION_MAIN); + intent.setClassName(getTestPackage(), getTestActivity()); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); + SystemUtil.runWithShellPermissionIdentity( + () -> { + mContext.startActivity(intent); + }, + android.Manifest.permission.START_ACTIVITIES_FROM_SDK_SANDBOX); + } + + + /** + * Bring the test activity into cached mode by launching another 2 apps + */ + protected void makeTestActivityCached() { + // Launch another activity to bring the test activity into background + Intent intent = new Intent(Intent.ACTION_MAIN); + intent.setClass(mContext, SimpleActivity.class); + intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); + + // Launch another activity to bring the test activity into cached mode + Intent intent2 = new Intent(Intent.ACTION_MAIN); + intent2.setClass(mContext, SimpleActivity2.class); + intent2.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + SystemUtil.runWithShellPermissionIdentity( + () -> { + mInstrumentation.startActivitySync(intent); + mInstrumentation.startActivitySync(intent2); + }, + android.Manifest.permission.START_ACTIVITIES_FROM_SDK_SANDBOX); + waitLatch(mLatchActivityCached); + } + + // Sleep, ignoring interrupts. + private void pause(int s) { + try { + Thread.sleep(s * 1000L); + } catch (Exception ignored) { } + } + + /** + * Freeze the test activity. + */ + protected void makeTestActivityFrozen(int pid) { + // The delay here is meant to allow pending binder transactions to drain. A process + // cannot be frozen if it has pending binder transactions, and attempting to freeze such a + // process more than a few times will result in the system killing the process. + pause(5); + try { + String cmd = "am freeze --sticky "; + SystemUtil.runShellCommand(mInstrumentation, cmd + getTestPackage()); + } catch (IOException e) { + fail(e.toString()); + } + // Wait for the freeze to complete in the kernel and for the frozen process + // notification to settle out. + waitForProcessFreeze(pid, 5 * 1000); + } + + /** + * Freeze the test activity. + */ + protected void makeTestActivityUnfrozen(int pid) { + try { + String cmd = "am unfreeze --sticky "; + SystemUtil.runShellCommand(mInstrumentation, cmd + getTestPackage()); + } catch (IOException e) { + fail(e.toString()); + } + // Wait for the freeze to complete in the kernel and for the frozen process + // notification to settle out. + waitForProcessUnfreeze(pid, 5 * 1000); + } + + /** + * Wait for CountDownLatch with timeout + */ + private void waitLatch(CountDownLatch latch) { + try { + latch.await(TEST_FAILURE_TIMEOUT_MSEC, TimeUnit.MILLISECONDS); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } +} diff --git a/services/tests/displayservicetests/src/com/android/server/display/TopologyUpdateDeliveryTest.java b/services/tests/displayservicetests/src/com/android/server/display/TopologyUpdateDeliveryTest.java new file mode 100644 index 000000000000..5fd248dba53f --- /dev/null +++ b/services/tests/displayservicetests/src/com/android/server/display/TopologyUpdateDeliveryTest.java @@ -0,0 +1,243 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.display; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.fail; +import static org.junit.Assume.assumeTrue; + +import android.content.Intent; +import android.hardware.display.DisplayTopology; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.platform.test.annotations.RequiresFlagsEnabled; +import android.platform.test.flag.junit.CheckFlagsRule; +import android.platform.test.flag.junit.DeviceFlagsValueProvider; +import android.util.Log; + +import androidx.annotation.NonNull; + +import org.junit.After; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; + +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; + +/** + * Tests that applications can receive topology updates correctly. + */ +public class TopologyUpdateDeliveryTest extends EventDeliveryTestBase { + private static final String TAG = TopologyUpdateDeliveryTest.class.getSimpleName(); + + @Rule + public final CheckFlagsRule mCheckFlagsRule = + DeviceFlagsValueProvider.createCheckFlagsRule(); + + private static final String TEST_PACKAGE = "com.android.servicestests.apps.topologytestapp"; + private static final String TEST_ACTIVITY = TEST_PACKAGE + ".TopologyUpdateActivity"; + + // Topology updates we expect to receive before timeout + private final LinkedBlockingQueue<DisplayTopology> mExpectations = new LinkedBlockingQueue<>(); + + /** + * Add the received topology update from the test activity to the queue + * + * @param topology The corresponding topology update + */ + private void addTopologyUpdate(DisplayTopology topology) { + Log.d(TAG, "Received " + topology); + mExpectations.offer(topology); + } + + /** + * Assert that there isn't any unexpected display event from the test activity + */ + private void assertNoTopologyUpdates() { + try { + assertNull(mExpectations.poll(EVENT_TIMEOUT_MSEC, TimeUnit.MILLISECONDS)); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + /** + * Wait for the expected topology update from the test activity + * + * @param expect The expected topology update + */ + private void waitTopologyUpdate(DisplayTopology expect) { + while (true) { + try { + DisplayTopology update = mExpectations.poll(TEST_FAILURE_TIMEOUT_MSEC, + TimeUnit.MILLISECONDS); + assertNotNull(update); + if (expect.equals(update)) { + Log.d(TAG, "Found " + update); + return; + } + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + } + + private class TestHandler extends Handler { + TestHandler(Looper looper) { + super(looper); + } + + @Override + public void handleMessage(@NonNull Message msg) { + switch (msg.what) { + case MESSAGE_LAUNCHED: + mPid = msg.arg1; + mUid = msg.arg2; + Log.d(TAG, "Launched " + mPid + " " + mUid); + mLatchActivityLaunch.countDown(); + break; + case MESSAGE_CALLBACK: + DisplayTopology topology = (DisplayTopology) msg.obj; + Log.d(TAG, "Callback " + topology); + addTopologyUpdate(topology); + break; + default: + fail("Unexpected value: " + msg.what); + break; + } + } + } + + @Before + public void setUp() { + super.setUp(); + } + + @After + public void tearDown() throws Exception { + super.tearDown(); + } + + @Override + protected String getTag() { + return TAG; + } + + @Override + protected Handler getHandler(Looper looper) { + return new TestHandler(looper); + } + + @Override + protected String getTestPackage() { + return TEST_PACKAGE; + } + + @Override + protected String getTestActivity() { + return TEST_ACTIVITY; + } + + @Override + protected void putExtra(Intent intent) { } + + private void testTopologyUpdateInternal(boolean cached, boolean frozen) { + Log.d(TAG, "Start test testTopologyUpdate " + cached + " " + frozen); + // Launch activity and start listening to topology updates + int pid = launchTestActivity(); + + // The test activity in cached or frozen mode won't receive the pending topology updates. + if (cached) { + makeTestActivityCached(); + } + if (frozen) { + makeTestActivityFrozen(pid); + } + + // Change the topology + int primaryDisplayId = 3; + DisplayTopology.TreeNode root = new DisplayTopology.TreeNode(primaryDisplayId, + /* width= */ 600, /* height= */ 400, DisplayTopology.TreeNode.POSITION_LEFT, + /* offset= */ 0); + DisplayTopology.TreeNode child = new DisplayTopology.TreeNode(/* displayId= */ 1, + /* width= */ 800, /* height= */ 600, DisplayTopology.TreeNode.POSITION_LEFT, + /* offset= */ 0); + root.addChild(child); + DisplayTopology topology = new DisplayTopology(root, primaryDisplayId); + mDisplayManager.setDisplayTopology(topology); + + if (cached || frozen) { + assertNoTopologyUpdates(); + } else { + waitTopologyUpdate(topology); + } + + // Unfreeze the test activity, if it was frozen. + if (frozen) { + makeTestActivityUnfrozen(pid); + } + + if (cached || frozen) { + // Always ensure the test activity is not cached. + bringTestActivityTop(); + + // The test activity becomes non-cached and should receive the pending topology updates + waitTopologyUpdate(topology); + } + } + + @Test + @RequiresFlagsEnabled(com.android.server.display.feature.flags.Flags.FLAG_DISPLAY_TOPOLOGY) + public void testTopologyUpdate() { + testTopologyUpdateInternal(false, false); + } + + /** + * The app is moved to cached and the test verifies that no updates are delivered to the cached + * app. + */ + @Test + @RequiresFlagsEnabled(com.android.server.display.feature.flags.Flags.FLAG_DISPLAY_TOPOLOGY) + public void testTopologyUpdateCached() { + testTopologyUpdateInternal(true, false); + } + + /** + * The app is frozen and the test verifies that no updates are delivered to the frozen app. + */ + @RequiresFlagsEnabled({com.android.server.am.Flags.FLAG_DEFER_DISPLAY_EVENTS_WHEN_FROZEN, + com.android.server.display.feature.flags.Flags.FLAG_DISPLAY_TOPOLOGY}) + @Test + public void testTopologyUpdateFrozen() { + assumeTrue(isAppFreezerEnabled()); + testTopologyUpdateInternal(false, true); + } + + /** + * The app is cached and frozen and the test verifies that no updates are delivered to the app. + */ + @RequiresFlagsEnabled({com.android.server.am.Flags.FLAG_DEFER_DISPLAY_EVENTS_WHEN_FROZEN, + com.android.server.display.feature.flags.Flags.FLAG_DISPLAY_TOPOLOGY}) + @Test + public void testTopologyUpdateCachedFrozen() { + assumeTrue(isAppFreezerEnabled()); + testTopologyUpdateInternal(true, true); + } +} diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/AppRequestObserverTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/AppRequestObserverTest.kt index 1f3f19fa3ea8..218728541774 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/mode/AppRequestObserverTest.kt +++ b/services/tests/displayservicetests/src/com/android/server/display/mode/AppRequestObserverTest.kt @@ -89,6 +89,39 @@ class AppRequestObserverTest { assertThat(renderRateVote).isEqualTo(testCase.expectedRenderRateVote) } + @Test + fun testAppRequestVote_externalDisplay() { + val displayModeDirector = DisplayModeDirector( + context, testHandler, mockInjector, mockFlags, mockDisplayDeviceConfigProvider) + val modes = arrayOf( + Display.Mode(1, 1000, 1000, 60f), + Display.Mode(2, 1000, 1000, 90f), + ) + + displayModeDirector.injectAppSupportedModesByDisplay( + SparseArray<Array<Display.Mode>>().apply { + append(Display.DEFAULT_DISPLAY, modes) + }) + displayModeDirector.injectDefaultModeByDisplay(SparseArray<Display.Mode>().apply { + append(Display.DEFAULT_DISPLAY, modes[0]) + }) + displayModeDirector.addExternalDisplayId(Display.DEFAULT_DISPLAY) + + displayModeDirector.appRequestObserver.setAppRequest(Display.DEFAULT_DISPLAY, 1, 0f, 0f, 0f) + + val baseModeVote = displayModeDirector.getVote(Display.DEFAULT_DISPLAY, + Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE) + assertThat(baseModeVote).isEqualTo(BaseModeRefreshRateVote(60f)) + + val sizeVote = displayModeDirector.getVote(Display.DEFAULT_DISPLAY, + Vote.PRIORITY_APP_REQUEST_SIZE) + assertThat(sizeVote).isNull() + + val renderRateVote = displayModeDirector.getVote(Display.DEFAULT_DISPLAY, + Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE) + assertThat(renderRateVote).isNull() + } + enum class AppRequestTestCase( val ignoreRefreshRateRequest: Boolean, val modeId: Int, diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/ModeChangeObserverTest.kt b/services/tests/displayservicetests/src/com/android/server/display/mode/ModeChangeObserverTest.kt new file mode 100644 index 000000000000..a5fb6cdc8d4f --- /dev/null +++ b/services/tests/displayservicetests/src/com/android/server/display/mode/ModeChangeObserverTest.kt @@ -0,0 +1,187 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.display.mode + +import android.os.Looper +import android.view.Display +import android.view.DisplayAddress +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.junit.MockitoJUnit +import org.mockito.kotlin.mock +import org.mockito.kotlin.whenever + +private const val PHYSICAL_DISPLAY_ID_1 = 1L +private const val PHYSICAL_DISPLAY_ID_2 = 2L +private const val MODE_ID_1 = 3 +private const val MODE_ID_2 = 4 +private const val LOGICAL_DISPLAY_ID = 5 +private val physicalAddress1 = DisplayAddress.fromPhysicalDisplayId(PHYSICAL_DISPLAY_ID_1) +private val physicalAddress2 = DisplayAddress.fromPhysicalDisplayId(PHYSICAL_DISPLAY_ID_2) + +/** + * Tests for ModeChangeObserver, comply with changes in b/31925610 + */ +@SmallTest +@RunWith(AndroidJUnit4::class) +public class ModeChangeObserverTest { + @get:Rule + val mockitoRule = MockitoJUnit.rule() + + // System Under Test + private lateinit var modeChangeObserver: ModeChangeObserver + + // Non Mocks + private val looper = Looper.getMainLooper() + private val votesStorage = VotesStorage({}, null) + + // Mocks + private val mockInjector = mock<DisplayModeDirector.Injector>() + private val mockDisplay1 = mock<Display>() + private val mockDisplay2 = mock<Display>() + + @Before + fun setUp() { + whenever(mockInjector.getDisplay(LOGICAL_DISPLAY_ID)).thenReturn(mockDisplay1) + whenever(mockDisplay1.getAddress()).thenReturn(physicalAddress1) + whenever(mockInjector.getDisplays()).thenReturn(arrayOf<Display>()) + modeChangeObserver = ModeChangeObserver(votesStorage, mockInjector, looper) + modeChangeObserver.observe() + } + + @Test + fun testOnModeRejectedBeforeDisplayAdded() { + val rejectedModes = HashSet<Int>() + rejectedModes.add(MODE_ID_1) + rejectedModes.add(MODE_ID_2) + + // ModeRejected is called before display is mapped, hence votes are null + modeChangeObserver.mModeChangeListener.onModeRejected(PHYSICAL_DISPLAY_ID_1, MODE_ID_1) + modeChangeObserver.mModeChangeListener.onModeRejected(PHYSICAL_DISPLAY_ID_1, MODE_ID_2) + val votes = votesStorage.getVotes(LOGICAL_DISPLAY_ID) + assertThat(votes.size()).isEqualTo(0) + + // Display is mapped to a Logical Display Id, now the Rejected Mode Votes get updated + modeChangeObserver.mDisplayListener.onDisplayAdded(LOGICAL_DISPLAY_ID) + val newVotes = votesStorage.getVotes(LOGICAL_DISPLAY_ID) + assertThat(newVotes.size()).isEqualTo(1) + val vote = newVotes.get(Vote.PRIORITY_REJECTED_MODES) + assertThat(vote).isInstanceOf(RejectedModesVote::class.java) + val rejectedModesVote = vote as RejectedModesVote + assertThat(rejectedModesVote.mModeIds.size).isEqualTo(rejectedModes.size) + assertThat(rejectedModesVote.mModeIds).contains(MODE_ID_1) + assertThat(rejectedModesVote.mModeIds).contains(MODE_ID_2) + } + + @Test + fun testOnDisplayAddedBeforeOnModeRejected() { + // Display is mapped to the corresponding Logical Id, but Mode Rejected no received yet + // Verify that the Vote is still Null + modeChangeObserver.mDisplayListener.onDisplayAdded(LOGICAL_DISPLAY_ID) + val votes = votesStorage.getVotes(LOGICAL_DISPLAY_ID) + assertThat(votes.size()).isEqualTo(0) + + // ModeRejected Event received for the mapped display + modeChangeObserver.mModeChangeListener.onModeRejected(PHYSICAL_DISPLAY_ID_1, MODE_ID_1) + val newVotes = votesStorage.getVotes(LOGICAL_DISPLAY_ID) + assertThat(newVotes.size()).isEqualTo(1) + val vote = newVotes.get(Vote.PRIORITY_REJECTED_MODES) + assertThat(vote).isInstanceOf(RejectedModesVote::class.java) + val rejectedModesVote = vote as RejectedModesVote + assertThat(rejectedModesVote.mModeIds.size).isEqualTo(1) + assertThat(rejectedModesVote.mModeIds).contains(MODE_ID_1) + } + + @Test + fun testOnDisplayAddedThenRejectedThenRemoved() { + // Display is mapped to its Logical Display Id + modeChangeObserver.mDisplayListener.onDisplayAdded(LOGICAL_DISPLAY_ID) + val votes = votesStorage.getVotes(LOGICAL_DISPLAY_ID) + assertThat(votes.size()).isEqualTo(0) + + // ModeRejected Event is received for mapped display + modeChangeObserver.mModeChangeListener.onModeRejected(PHYSICAL_DISPLAY_ID_1, MODE_ID_1) + val newVotes = votesStorage.getVotes(LOGICAL_DISPLAY_ID) + assertThat(newVotes.size()).isEqualTo(1) + val vote = newVotes.get(Vote.PRIORITY_REJECTED_MODES) + assertThat(vote).isInstanceOf(RejectedModesVote::class.java) + val rejectedModesVote = vote as RejectedModesVote + assertThat(rejectedModesVote.mModeIds.size).isEqualTo(1) + assertThat(rejectedModesVote.mModeIds).contains(MODE_ID_1) + + // Display mapping is removed, hence remove the votes + modeChangeObserver.mDisplayListener.onDisplayRemoved(LOGICAL_DISPLAY_ID) + val finalVotes = votesStorage.getVotes(LOGICAL_DISPLAY_ID) + assertThat(finalVotes.size()).isEqualTo(0) + } + + @Test + fun testForModesRejectedAfterDisplayChanged() { + // Mock Display 1 is mapped to logicalId + modeChangeObserver.mDisplayListener.onDisplayAdded(LOGICAL_DISPLAY_ID) + val votes = votesStorage.getVotes(LOGICAL_DISPLAY_ID) + assertThat(votes.size()).isEqualTo(0) + + // Mode Rejected received for PhysicalId2 not mapped yet, so votes are null + whenever(mockInjector.getDisplay(LOGICAL_DISPLAY_ID)).thenReturn(mockDisplay2) + whenever(mockDisplay2.getAddress()).thenReturn(physicalAddress2) + modeChangeObserver.mModeChangeListener.onModeRejected(PHYSICAL_DISPLAY_ID_2, MODE_ID_2) + val changedVotes = votesStorage.getVotes(LOGICAL_DISPLAY_ID) + assertThat(changedVotes.size()).isEqualTo(0) + + // Display mapping changed, now PhysicalId2 is mapped to the LogicalId, votes get updated + modeChangeObserver.mDisplayListener.onDisplayChanged(LOGICAL_DISPLAY_ID) + val finalVotes = votesStorage.getVotes(LOGICAL_DISPLAY_ID) + assertThat(finalVotes.size()).isEqualTo(1) + val finalVote = finalVotes.get(Vote.PRIORITY_REJECTED_MODES) + assertThat(finalVote).isInstanceOf(RejectedModesVote::class.java) + val newRejectedModesVote = finalVote as RejectedModesVote + assertThat(newRejectedModesVote.mModeIds.size).isEqualTo(1) + assertThat(newRejectedModesVote.mModeIds).contains(MODE_ID_2) + } + + @Test + fun testForModesNotRejectedAfterDisplayChanged() { + // Mock Display 1 is added + modeChangeObserver.mDisplayListener.onDisplayAdded(LOGICAL_DISPLAY_ID) + val votes = votesStorage.getVotes(LOGICAL_DISPLAY_ID) + assertThat(votes.size()).isEqualTo(0) + + // Mode Rejected received for Display 1, votes added for rejected mode + modeChangeObserver.mModeChangeListener.onModeRejected(PHYSICAL_DISPLAY_ID_1, MODE_ID_1) + val newVotes = votesStorage.getVotes(LOGICAL_DISPLAY_ID) + assertThat(newVotes.size()).isEqualTo(1) + val vote = newVotes.get(Vote.PRIORITY_REJECTED_MODES) + assertThat(vote).isInstanceOf(RejectedModesVote::class.java) + val rejectedModesVote = vote as RejectedModesVote + assertThat(rejectedModesVote.mModeIds.size).isEqualTo(1) + assertThat(rejectedModesVote.mModeIds).contains(MODE_ID_1) + + // Display Changed such that logical Id corresponds to PhysicalDisplayId2 + // Rejected Modes Vote is removed + whenever(mockInjector.getDisplay(LOGICAL_DISPLAY_ID)).thenReturn(mockDisplay2) + whenever(mockDisplay2.getAddress()).thenReturn(physicalAddress2) + modeChangeObserver.mDisplayListener.onDisplayChanged(LOGICAL_DISPLAY_ID) + val finalVotes = votesStorage.getVotes(LOGICAL_DISPLAY_ID) + assertThat(finalVotes.size()).isEqualTo(0) + } +}
\ No newline at end of file diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationStartInfoTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationStartInfoTest.java index 987b9c6427d8..3289d70b89ac 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/ApplicationStartInfoTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/ApplicationStartInfoTest.java @@ -680,6 +680,67 @@ public class ApplicationStartInfoTest { ApplicationStartInfo.START_TIMESTAMP_FORK)); } + /** + * Test that cleanup old records works as expected, removing records that are older than the max + * retention length. + */ + @Test + @EnableFlags(android.app.Flags.FLAG_APP_START_INFO_CLEANUP_OLD_RECORDS) + public void testOldRecordsCleanup() throws Exception { + // Use a different start timestamp for each record so we can identify which was removed. + // This timestamp is not used for ordering and has no impact on removal. + final long startTimeRecord1 = 123L; + final long startTimeRecord2 = 456L; + final long startTimeRecord3 = 789L; + + // Create a process record to use with all starts. + ProcessRecord app = makeProcessRecord( + APP_1_PID_1, // pid + APP_1_UID, // uid + APP_1_UID, // packageUid + null, // definingUid + APP_1_PROCESS_NAME, // processName + APP_1_PACKAGE_NAME); // packageName + + // Set monotonic time to 1, and then trigger a start info record. + doReturn(1L).when(mAppStartInfoTracker).getMonotonicTimeMs(); + mAppStartInfoTracker.handleProcessBroadcastStart(startTimeRecord1, app, + buildIntent(COMPONENT), false /* isAlarm */); + + // Set monotonic time to 2, and then trigger another start info record. + doReturn(2L).when(mAppStartInfoTracker).getMonotonicTimeMs(); + mAppStartInfoTracker.handleProcessBroadcastStart(startTimeRecord2, app, + buildIntent(COMPONENT), false /* isAlarm */); + + // Set monotonic time to 3, then trigger another start info record. + doReturn(3L).when(mAppStartInfoTracker).getMonotonicTimeMs(); + mAppStartInfoTracker.handleProcessBroadcastStart(startTimeRecord3, app, + buildIntent(COMPONENT), false /* isAlarm */); + + // Verify that all 3 records were added successfully. + ArrayList<ApplicationStartInfo> list = new ArrayList<ApplicationStartInfo>(); + mAppStartInfoTracker.getStartInfo(null, APP_1_UID, 0, 0, list); + assertEquals(3, list.size()); + assertEquals(startTimeRecord3, list.get(0).getStartupTimestamps().get(0).longValue()); + assertEquals(startTimeRecord2, list.get(1).getStartupTimestamps().get(0).longValue()); + assertEquals(startTimeRecord1, list.get(2).getStartupTimestamps().get(0).longValue()); + + // Set monotonic time to max history length + 3 so that the older 2 records will be removed + // but that newest 1 will remain. + doReturn(AppStartInfoTracker.APP_START_INFO_HISTORY_LENGTH_MS + 3L) + .when(mAppStartInfoTracker).getMonotonicTimeMs(); + + // Trigger a persist which will trigger the cleanup of old records. + mAppStartInfoTracker.persistProcessStartInfo(); + + // Now verify that the records older than max were removed, and that the records not older + // remain. + list.clear(); + mAppStartInfoTracker.getStartInfo(null, APP_1_UID, 0, 0, list); + assertEquals(1, list.size()); + assertEquals(startTimeRecord3, list.get(0).getStartupTimestamps().get(0).longValue()); + } + private static <T> void setFieldValue(Class clazz, Object obj, String fieldName, T val) { try { Field field = clazz.getDeclaredField(fieldName); diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ProcessObserverTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ProcessObserverTest.java index 43becc59c3cb..558ea29d85fc 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/ProcessObserverTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/ProcessObserverTest.java @@ -42,6 +42,8 @@ import android.os.Binder; import android.os.Handler; import android.os.HandlerThread; import android.os.IBinder; +import android.platform.test.annotations.EnableFlags; +import android.platform.test.flag.junit.SetFlagsRule; import android.util.Log; import androidx.test.filters.MediumTest; @@ -79,6 +81,8 @@ public class ProcessObserverTest { @Rule public final ApplicationExitInfoTest.ServiceThreadRule mServiceThreadRule = new ApplicationExitInfoTest.ServiceThreadRule(); + @Rule + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); private Context mContext; private HandlerThread mHandlerThread; @@ -239,8 +243,8 @@ public class ProcessObserverTest { /** * Verify that a process start event is dispatched to process observers. */ - @Ignore("b/323959187") @Test + @EnableFlags(android.app.Flags.FLAG_ENABLE_PROCESS_OBSERVER_BROADCAST_ON_PROCESS_STARTED) public void testNormal() throws Exception { ProcessRecord app = startProcess(); verify(mProcessObserver).onProcessStarted( diff --git a/services/tests/servicestests/Android.bp b/services/tests/servicestests/Android.bp index d702cae248a9..067fba9893e5 100644 --- a/services/tests/servicestests/Android.bp +++ b/services/tests/servicestests/Android.bp @@ -31,6 +31,7 @@ android_test { "test-apps/SuspendTestApp/src/**/*.java", "test-apps/DisplayManagerTestApp/src/**/*.java", + "test-apps/TopologyTestApp/src/**/*.java", ], static_libs: [ @@ -141,6 +142,7 @@ android_test { data: [ ":DisplayManagerTestApp", + ":TopologyTestApp", ":SimpleServiceTestApp1", ":SimpleServiceTestApp2", ":SimpleServiceTestApp3", diff --git a/services/tests/servicestests/AndroidTest.xml b/services/tests/servicestests/AndroidTest.xml index 5298251b79f7..9a4983482522 100644 --- a/services/tests/servicestests/AndroidTest.xml +++ b/services/tests/servicestests/AndroidTest.xml @@ -36,6 +36,7 @@ <option name="cleanup-apks" value="true" /> <option name="install-arg" value="-t" /> <option name="test-file-name" value="DisplayManagerTestApp.apk" /> + <option name="test-file-name" value="TopologyTestApp.apk" /> <option name="test-file-name" value="FrameworksServicesTests.apk" /> <option name="test-file-name" value="SuspendTestApp.apk" /> <option name="test-file-name" value="SimpleServiceTestApp1.apk" /> diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java index 2ccd33648c3e..e0bc3e76f31d 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/AccessibilityManagerServiceTest.java @@ -82,6 +82,7 @@ import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; import android.content.pm.ResolveInfo; import android.content.pm.ServiceInfo; +import android.content.pm.UserInfo; import android.content.res.XmlResourceParser; import android.graphics.drawable.Icon; import android.hardware.display.DisplayManager; @@ -131,6 +132,7 @@ import com.android.internal.accessibility.util.ShortcutUtils; import com.android.internal.compat.IPlatformCompat; import com.android.internal.content.PackageMonitor; import com.android.server.LocalServices; +import com.android.server.SystemService; import com.android.server.accessibility.AccessibilityManagerService.AccessibilityDisplayListener; import com.android.server.accessibility.magnification.FullScreenMagnificationController; import com.android.server.accessibility.magnification.MagnificationConnectionManager; @@ -2122,6 +2124,36 @@ public class AccessibilityManagerServiceTest { } @Test + @EnableFlags(Flags.FLAG_MANAGER_LIFECYCLE_USER_CHANGE) + public void lifecycle_onUserSwitching_switchesUser() throws RemoteException { + mA11yms.mUserInitializationCompleteCallbacks.add(mUserInitializationCompleteCallback); + AccessibilityManagerService.Lifecycle lifecycle = + new AccessibilityManagerService.Lifecycle(mTestableContext, mA11yms); + int newUserId = mA11yms.getCurrentUserIdLocked() + 1; + + lifecycle.onUserSwitching( + new SystemService.TargetUser(new UserInfo(0, "USER", 0)), + new SystemService.TargetUser(new UserInfo(newUserId, "USER", 0))); + mTestableLooper.processAllMessages(); + + verify(mUserInitializationCompleteCallback).onUserInitializationComplete(newUserId); + } + + @Test + @DisableFlags(Flags.FLAG_MANAGER_LIFECYCLE_USER_CHANGE) + public void intent_user_switched_switchesUser() throws RemoteException { + mA11yms.mUserInitializationCompleteCallbacks.add(mUserInitializationCompleteCallback); + int newUserId = mA11yms.getCurrentUserIdLocked() + 1; + final Intent intent = new Intent(Intent.ACTION_USER_SWITCHED); + intent.putExtra(Intent.EXTRA_USER_HANDLE, newUserId); + + sendBroadcastToAccessibilityManagerService(intent, mA11yms.getCurrentUserIdLocked()); + mTestableLooper.processAllMessages(); + + verify(mUserInitializationCompleteCallback).onUserInitializationComplete(newUserId); + } + + @Test @DisableFlags(android.provider.Flags.FLAG_A11Y_STANDALONE_GESTURE_ENABLED) public void getShortcutTypeForGenericShortcutCalls_softwareType() { final AccessibilityUserState userState = new AccessibilityUserState( diff --git a/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickControllerTest.java index 99c922ca30c4..df77866b5e7f 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickControllerTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/autoclick/AutoclickControllerTest.java @@ -25,6 +25,7 @@ import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -866,6 +867,23 @@ public class AutoclickControllerTest { @Test @EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR) + public void scrollPanelController_directionalButtonsHideIndicator() { + injectFakeMouseActionHoverMoveEvent(); + + // Create a spy on the real object to verify method calls. + AutoclickIndicatorView spyIndicatorView = spy(mController.mAutoclickIndicatorView); + mController.mAutoclickIndicatorView = spyIndicatorView; + + // Simulate hover on direction button. + mController.mScrollPanelController.onHoverButtonChange( + AutoclickScrollPanel.DIRECTION_UP, true); + + // Verify clearIndicator was called. + verify(spyIndicatorView).clearIndicator(); + } + + @Test + @EnableFlags(com.android.server.accessibility.Flags.FLAG_ENABLE_AUTOCLICK_INDICATOR) public void hoverOnAutoclickPanel_rightClickType_forceTriggerLeftClick() { MotionEventCaptor motionEventCaptor = new MotionEventCaptor(); mController.setNext(motionEventCaptor); diff --git a/services/tests/servicestests/src/com/android/server/location/contexthub/ContextHubEndpointTest.java b/services/tests/servicestests/src/com/android/server/location/contexthub/ContextHubEndpointTest.java index 43b1ec393bf6..87cd1560509c 100644 --- a/services/tests/servicestests/src/com/android/server/location/contexthub/ContextHubEndpointTest.java +++ b/services/tests/servicestests/src/com/android/server/location/contexthub/ContextHubEndpointTest.java @@ -19,7 +19,9 @@ package com.android.server.location.contexthub; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.never; import static org.mockito.Mockito.timeout; import static org.mockito.Mockito.times; @@ -29,6 +31,7 @@ import static org.mockito.Mockito.when; import android.content.Context; import android.hardware.contexthub.EndpointInfo; import android.hardware.contexthub.ErrorCode; +import android.hardware.contexthub.HubEndpoint; import android.hardware.contexthub.HubEndpointInfo; import android.hardware.contexthub.HubEndpointInfo.HubEndpointIdentifier; import android.hardware.contexthub.HubMessage; @@ -385,6 +388,49 @@ public class ContextHubEndpointTest { unregisterExampleEndpoint(endpoint); } + @Test + public void testUnreliableMessageFailureClosesSession() throws RemoteException { + IContextHubEndpoint endpoint = registerExampleEndpoint(); + int sessionId = openTestSession(endpoint); + + doThrow(new RemoteException("Intended exception in test")) + .when(mMockCallback) + .onMessageReceived(anyInt(), any(HubMessage.class)); + mEndpointManager.onMessageReceived(sessionId, SAMPLE_UNRELIABLE_MESSAGE); + ArgumentCaptor<HubMessage> messageCaptor = ArgumentCaptor.forClass(HubMessage.class); + verify(mMockCallback).onMessageReceived(eq(sessionId), messageCaptor.capture()); + assertThat(messageCaptor.getValue()).isEqualTo(SAMPLE_UNRELIABLE_MESSAGE); + + verify(mMockEndpointCommunications).closeEndpointSession(sessionId, Reason.UNSPECIFIED); + verify(mMockCallback).onSessionClosed(sessionId, HubEndpoint.REASON_FAILURE); + assertThat(mEndpointManager.getNumAvailableSessions()).isEqualTo(SESSION_ID_RANGE); + + unregisterExampleEndpoint(endpoint); + } + + @Test + public void testSendUnreliableMessageFailureClosesSession() throws RemoteException { + IContextHubEndpoint endpoint = registerExampleEndpoint(); + int sessionId = openTestSession(endpoint); + + doThrow(new RemoteException("Intended exception in test")) + .when(mMockEndpointCommunications) + .sendMessageToEndpoint(anyInt(), any(Message.class)); + endpoint.sendMessage(sessionId, SAMPLE_UNRELIABLE_MESSAGE, /* callback= */ null); + ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(Message.class); + verify(mMockEndpointCommunications) + .sendMessageToEndpoint(eq(sessionId), messageCaptor.capture()); + Message message = messageCaptor.getValue(); + assertThat(message.type).isEqualTo(SAMPLE_UNRELIABLE_MESSAGE.getMessageType()); + assertThat(message.content).isEqualTo(SAMPLE_UNRELIABLE_MESSAGE.getMessageBody()); + + verify(mMockEndpointCommunications).closeEndpointSession(sessionId, Reason.UNSPECIFIED); + verify(mMockCallback).onSessionClosed(sessionId, HubEndpoint.REASON_FAILURE); + assertThat(mEndpointManager.getNumAvailableSessions()).isEqualTo(SESSION_ID_RANGE); + + unregisterExampleEndpoint(endpoint); + } + /** A helper method to create a session and validates reliable message sending. */ private void testMessageTransactionInternal( IContextHubEndpoint endpoint, boolean deliverMessageStatus) throws RemoteException { diff --git a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java index 125791acc61a..f312a1bdb985 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/LockSettingsServiceTests.java @@ -17,7 +17,7 @@ package com.android.server.locksettings; import static android.Manifest.permission.CONFIGURE_FACTORY_RESET_PROTECTION; -import static android.security.Flags.FLAG_CLEAR_STRONG_AUTH_ON_ADD_PRIMARY_CREDENTIAL; +import static android.security.Flags.FLAG_CLEAR_STRONG_AUTH_ON_ADDING_PRIMARY_CREDENTIAL; import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_NONE; import static com.android.internal.widget.LockPatternUtils.CREDENTIAL_TYPE_PASSWORD; @@ -264,7 +264,7 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests { } @Test - @RequiresFlagsEnabled(FLAG_CLEAR_STRONG_AUTH_ON_ADD_PRIMARY_CREDENTIAL) + @RequiresFlagsEnabled(FLAG_CLEAR_STRONG_AUTH_ON_ADDING_PRIMARY_CREDENTIAL) public void setLockCredential_forPrimaryUser_clearsStrongAuthWhenFlagIsOn() throws Exception { setCredential(PRIMARY_USER_ID, newPassword("password")); @@ -273,7 +273,7 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests { } @Test - @RequiresFlagsDisabled(FLAG_CLEAR_STRONG_AUTH_ON_ADD_PRIMARY_CREDENTIAL) + @RequiresFlagsDisabled(FLAG_CLEAR_STRONG_AUTH_ON_ADDING_PRIMARY_CREDENTIAL) public void setLockCredential_forPrimaryUser_leavesStrongAuthWhenFlagIsOff() throws Exception { setCredential(PRIMARY_USER_ID, newPassword("password")); @@ -312,7 +312,7 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests { } @Test - @RequiresFlagsEnabled(FLAG_CLEAR_STRONG_AUTH_ON_ADD_PRIMARY_CREDENTIAL) + @RequiresFlagsEnabled(FLAG_CLEAR_STRONG_AUTH_ON_ADDING_PRIMARY_CREDENTIAL) public void setLockCredential_profileWithNewSeparateChallenge_clearsStrongAuthWhenFlagIsOn() throws Exception { mService.setSeparateProfileChallengeEnabled(MANAGED_PROFILE_USER_ID, true, null); @@ -323,7 +323,7 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests { } @Test - @RequiresFlagsDisabled(FLAG_CLEAR_STRONG_AUTH_ON_ADD_PRIMARY_CREDENTIAL) + @RequiresFlagsDisabled(FLAG_CLEAR_STRONG_AUTH_ON_ADDING_PRIMARY_CREDENTIAL) public void setLockCredential_profileWithNewSeparateChallenge_leavesStrongAuthWhenFlagIsOff() throws Exception { mService.setSeparateProfileChallengeEnabled(MANAGED_PROFILE_USER_ID, true, null); @@ -377,7 +377,7 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests { } @Test - @RequiresFlagsEnabled(FLAG_CLEAR_STRONG_AUTH_ON_ADD_PRIMARY_CREDENTIAL) + @RequiresFlagsEnabled(FLAG_CLEAR_STRONG_AUTH_ON_ADDING_PRIMARY_CREDENTIAL) public void setLockCredential_primaryWithUnifiedProfile_clearsStrongAuthForBothWhenFlagIsOn() throws Exception { final LockscreenCredential credential = newPassword("oldPassword"); @@ -393,7 +393,7 @@ public class LockSettingsServiceTests extends BaseLockSettingsServiceTests { } @Test - @RequiresFlagsDisabled(FLAG_CLEAR_STRONG_AUTH_ON_ADD_PRIMARY_CREDENTIAL) + @RequiresFlagsDisabled(FLAG_CLEAR_STRONG_AUTH_ON_ADDING_PRIMARY_CREDENTIAL) public void setLockCredential_primaryWithUnifiedProfile_leavesStrongAuthForBothWhenFlagIsOff() throws Exception { final LockscreenCredential credential = newPassword("oldPassword"); diff --git a/services/tests/servicestests/src/com/android/server/statusbar/StatusBarManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/statusbar/StatusBarManagerServiceTest.java index 148c96850d34..6d682ccef98d 100644 --- a/services/tests/servicestests/src/com/android/server/statusbar/StatusBarManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/statusbar/StatusBarManagerServiceTest.java @@ -36,6 +36,7 @@ import static android.app.StatusBarManager.DISABLE_SYSTEM_INFO; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.nullable; @@ -69,16 +70,20 @@ import android.os.Binder; import android.os.Looper; import android.os.RemoteException; import android.os.UserHandle; +import android.platform.test.annotations.EnableFlags; import android.service.quicksettings.TileService; import android.testing.TestableContext; +import android.util.Pair; import androidx.test.InstrumentationRegistry; +import com.android.internal.statusbar.DisableStates; import com.android.internal.statusbar.IAddTileResultCallback; import com.android.internal.statusbar.IStatusBar; import com.android.server.LocalServices; import com.android.server.policy.GlobalActionsProvider; import com.android.server.wm.ActivityTaskManagerInternal; +import com.android.systemui.shared.Flags; import libcore.junit.util.compat.CoreCompatChangeRule; @@ -105,6 +110,7 @@ public class StatusBarManagerServiceTest { TEST_SERVICE); private static final CharSequence APP_NAME = "AppName"; private static final CharSequence TILE_LABEL = "Tile label"; + private static final int SECONDARY_DISPLAY_ID = 2; @Rule public final TestableContext mContext = @@ -749,6 +755,40 @@ public class StatusBarManagerServiceTest { } @Test + @EnableFlags(Flags.FLAG_STATUS_BAR_CONNECTED_DISPLAYS) + public void testDisableForAllDisplays() throws Exception { + int user1Id = 0; + mockUidCheck(); + mockCurrentUserCheck(user1Id); + + mStatusBarManagerService.onDisplayAdded(SECONDARY_DISPLAY_ID); + + int expectedFlags = DISABLE_MASK & DISABLE_BACK; + String pkg = mContext.getPackageName(); + + // before disabling + assertEquals(DISABLE_NONE, + mStatusBarManagerService.getDisableFlags(mMockStatusBar, user1Id)[0]); + + // disable + mStatusBarManagerService.disable(expectedFlags, mMockStatusBar, pkg); + + ArgumentCaptor<DisableStates> disableStatesCaptor = ArgumentCaptor.forClass( + DisableStates.class); + verify(mMockStatusBar).disableForAllDisplays(disableStatesCaptor.capture()); + DisableStates capturedDisableStates = disableStatesCaptor.getValue(); + assertTrue(capturedDisableStates.animate); + assertEquals(capturedDisableStates.displaysWithStates.size(), 2); + Pair<Integer, Integer> display0States = capturedDisableStates.displaysWithStates.get(0); + assertEquals((int) display0States.first, expectedFlags); + assertEquals((int) display0States.second, 0); + Pair<Integer, Integer> display2States = capturedDisableStates.displaysWithStates.get( + SECONDARY_DISPLAY_ID); + assertEquals((int) display2States.first, expectedFlags); + assertEquals((int) display2States.second, 0); + } + + @Test public void testSetHomeDisabled() throws Exception { int expectedFlags = DISABLE_MASK & DISABLE_HOME; String pkg = mContext.getPackageName(); @@ -851,6 +891,40 @@ public class StatusBarManagerServiceTest { } @Test + @EnableFlags(Flags.FLAG_STATUS_BAR_CONNECTED_DISPLAYS) + public void testDisable2ForAllDisplays() throws Exception { + int user1Id = 0; + mockUidCheck(); + mockCurrentUserCheck(user1Id); + + mStatusBarManagerService.onDisplayAdded(SECONDARY_DISPLAY_ID); + + int expectedFlags = DISABLE2_MASK & DISABLE2_NOTIFICATION_SHADE; + String pkg = mContext.getPackageName(); + + // before disabling + assertEquals(DISABLE_NONE, + mStatusBarManagerService.getDisableFlags(mMockStatusBar, user1Id)[0]); + + // disable + mStatusBarManagerService.disable2(expectedFlags, mMockStatusBar, pkg); + + ArgumentCaptor<DisableStates> disableStatesCaptor = ArgumentCaptor.forClass( + DisableStates.class); + verify(mMockStatusBar).disableForAllDisplays(disableStatesCaptor.capture()); + DisableStates capturedDisableStates = disableStatesCaptor.getValue(); + assertTrue(capturedDisableStates.animate); + assertEquals(capturedDisableStates.displaysWithStates.size(), 2); + Pair<Integer, Integer> display0States = capturedDisableStates.displaysWithStates.get(0); + assertEquals((int) display0States.first, 0); + assertEquals((int) display0States.second, expectedFlags); + Pair<Integer, Integer> display2States = capturedDisableStates.displaysWithStates.get( + SECONDARY_DISPLAY_ID); + assertEquals((int) display2States.first, 0); + assertEquals((int) display2States.second, expectedFlags); + } + + @Test public void testSetQuickSettingsDisabled2() throws Exception { int expectedFlags = DISABLE2_MASK & DISABLE2_QUICK_SETTINGS; String pkg = mContext.getPackageName(); diff --git a/services/tests/servicestests/test-apps/TopologyTestApp/Android.bp b/services/tests/servicestests/test-apps/TopologyTestApp/Android.bp new file mode 100644 index 000000000000..dcf9cc216687 --- /dev/null +++ b/services/tests/servicestests/test-apps/TopologyTestApp/Android.bp @@ -0,0 +1,38 @@ +// Copyright (C) 2025 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_applicable_licenses: ["frameworks_base_license"], +} + +android_test_helper_app { + name: "TopologyTestApp", + + srcs: ["**/*.java"], + + dex_preopt: { + enabled: false, + }, + optimize: { + enabled: false, + }, + + platform_apis: true, + certificate: "platform", +} diff --git a/services/tests/servicestests/test-apps/TopologyTestApp/AndroidManifest.xml b/services/tests/servicestests/test-apps/TopologyTestApp/AndroidManifest.xml new file mode 100644 index 000000000000..dad2315148df --- /dev/null +++ b/services/tests/servicestests/test-apps/TopologyTestApp/AndroidManifest.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2025 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.servicestests.apps.topologytestapp"> + + <uses-permission android:name="android.permission.MANAGE_DISPLAYS" /> + + <application android:label="TopologyUpdateTestApp"> + <activity android:name="com.android.servicestests.apps.topologytestapp.TopologyUpdateActivity" + android:exported="true" /> + </application> + +</manifest> diff --git a/services/tests/servicestests/test-apps/TopologyTestApp/OWNERS b/services/tests/servicestests/test-apps/TopologyTestApp/OWNERS new file mode 100644 index 000000000000..e9557f84f8fb --- /dev/null +++ b/services/tests/servicestests/test-apps/TopologyTestApp/OWNERS @@ -0,0 +1,3 @@ +# Bug component: 345010 + +include /services/core/java/com/android/server/display/OWNERS diff --git a/services/tests/servicestests/test-apps/TopologyTestApp/src/com/android/servicestests/apps/topologytestapp/TopologyUpdateActivity.java b/services/tests/servicestests/test-apps/TopologyTestApp/src/com/android/servicestests/apps/topologytestapp/TopologyUpdateActivity.java new file mode 100644 index 000000000000..b35ba3c2c60c --- /dev/null +++ b/services/tests/servicestests/test-apps/TopologyTestApp/src/com/android/servicestests/apps/topologytestapp/TopologyUpdateActivity.java @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2025 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.servicestests.apps.topologytestapp; + +import android.app.Activity; +import android.content.Intent; +import android.hardware.display.DisplayManager; +import android.hardware.display.DisplayTopology; +import android.os.Bundle; +import android.os.Message; +import android.os.Messenger; +import android.os.Process; +import android.os.RemoteException; +import android.util.Log; + +import java.util.function.Consumer; + +/** + * A simple activity listening to topology updates + */ +public class TopologyUpdateActivity extends Activity { + public static final int MESSAGE_LAUNCHED = 1; + public static final int MESSAGE_CALLBACK = 2; + + private static final String TAG = TopologyUpdateActivity.class.getSimpleName(); + + private static final String TEST_MESSENGER = "MESSENGER"; + + private Messenger mMessenger; + private DisplayManager mDisplayManager; + private final Consumer<DisplayTopology> mTopologyListener = this::callback; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + Intent intent = getIntent(); + mMessenger = intent.getParcelableExtra(TEST_MESSENGER, Messenger.class); + mDisplayManager = getApplicationContext().getSystemService(DisplayManager.class); + mDisplayManager.registerTopologyListener(getMainExecutor(), mTopologyListener); + launched(); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + mDisplayManager.unregisterTopologyListener(mTopologyListener); + } + + private void launched() { + try { + Message msg = Message.obtain(); + msg.what = MESSAGE_LAUNCHED; + msg.arg1 = android.os.Process.myPid(); + msg.arg2 = Process.myUid(); + Log.d(TAG, "Launched"); + mMessenger.send(msg); + } catch (RemoteException e) { + e.rethrowAsRuntimeException(); + } + } + + private void callback(DisplayTopology topology) { + try { + Message msg = Message.obtain(); + msg.what = MESSAGE_CALLBACK; + msg.obj = topology; + Log.d(TAG, "Msg " + topology); + mMessenger.send(msg); + } catch (RemoteException e) { + e.rethrowAsRuntimeException(); + } + } +} diff --git a/services/tests/wmtests/src/com/android/server/policy/CombinationKeyTests.java b/services/tests/wmtests/src/com/android/server/policy/CombinationKeyTests.java index 036e03c60091..e473a06b25e7 100644 --- a/services/tests/wmtests/src/com/android/server/policy/CombinationKeyTests.java +++ b/services/tests/wmtests/src/com/android/server/policy/CombinationKeyTests.java @@ -49,6 +49,7 @@ public class CombinationKeyTests extends ShortcutKeyTestBase { @Before public void setUp() { setUpPhoneWindowManager(); + mPhoneWindowManager.overrideStatusBarManagerInternal(); } /** diff --git a/services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java b/services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java index fcdf88f16550..0495e967c0e3 100644 --- a/services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java +++ b/services/tests/wmtests/src/com/android/server/policy/KeyGestureEventTests.java @@ -39,8 +39,6 @@ import androidx.test.filters.MediumTest; import com.android.hardware.input.Flags; import com.android.internal.annotations.Keep; -import junit.framework.Assert; - import junitparams.JUnitParamsRunner; import junitparams.Parameters; @@ -433,112 +431,94 @@ public class KeyGestureEventTests extends ShortcutKeyTestBase { @Test public void testKeyGestureRecentApps() { - Assert.assertTrue( - sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS)); + sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS); mPhoneWindowManager.assertShowRecentApps(); } @Test public void testKeyGestureAppSwitch() { - Assert.assertTrue( - sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_APP_SWITCH)); + sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_APP_SWITCH); mPhoneWindowManager.assertToggleRecentApps(); } @Test public void testKeyGestureLaunchAssistant() { - Assert.assertTrue( - sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_ASSISTANT)); + sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_ASSISTANT); mPhoneWindowManager.assertSearchManagerLaunchAssist(); } @Test public void testKeyGestureLaunchVoiceAssistant() { - Assert.assertTrue( - sendKeyGestureEventComplete( - KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_VOICE_ASSISTANT)); + sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_VOICE_ASSISTANT); mPhoneWindowManager.assertSearchManagerLaunchAssist(); } @Test public void testKeyGestureGoHome() { - Assert.assertTrue( - sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_HOME)); + sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_HOME); mPhoneWindowManager.assertGoToHomescreen(); } @Test public void testKeyGestureLaunchSystemSettings() { - Assert.assertTrue( - sendKeyGestureEventComplete( - KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS)); + sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SYSTEM_SETTINGS); mPhoneWindowManager.assertLaunchSystemSettings(); } @Test public void testKeyGestureLock() { - Assert.assertTrue( - sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_LOCK_SCREEN)); + sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_LOCK_SCREEN); mPhoneWindowManager.assertLockedAfterAppTransitionFinished(); } @Test public void testKeyGestureToggleNotificationPanel() throws RemoteException { - Assert.assertTrue( - sendKeyGestureEventComplete( - KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL)); + sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL); mPhoneWindowManager.assertTogglePanel(); } @Test public void testKeyGestureScreenshot() { - Assert.assertTrue( - sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_TAKE_SCREENSHOT)); + sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_TAKE_SCREENSHOT); mPhoneWindowManager.assertTakeScreenshotCalled(); } @Test public void testKeyGestureTriggerBugReport() throws RemoteException { - Assert.assertTrue( - sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_TRIGGER_BUG_REPORT)); + sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_TRIGGER_BUG_REPORT); mPhoneWindowManager.assertTakeBugreport(true); } @Test public void testKeyGestureBack() { - Assert.assertTrue(sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_BACK)); + sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_BACK); mPhoneWindowManager.assertBackEventInjected(); } @Test public void testKeyGestureMultiWindowNavigation() { - Assert.assertTrue(sendKeyGestureEventComplete( - KeyGestureEvent.KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION)); + sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_MULTI_WINDOW_NAVIGATION); mPhoneWindowManager.assertMoveFocusedTaskToFullscreen(); } @Test public void testKeyGestureDesktopMode() { - Assert.assertTrue( - sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_DESKTOP_MODE)); + sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_DESKTOP_MODE); mPhoneWindowManager.assertMoveFocusedTaskToDesktop(); } @Test public void testKeyGestureSplitscreenNavigation() { - Assert.assertTrue(sendKeyGestureEventComplete( - KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_LEFT)); + sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_LEFT); mPhoneWindowManager.assertMoveFocusedTaskToStageSplit(true); - Assert.assertTrue(sendKeyGestureEventComplete( - KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_RIGHT)); + sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_SPLIT_SCREEN_NAVIGATION_RIGHT); mPhoneWindowManager.assertMoveFocusedTaskToStageSplit(false); } @Test public void testKeyGestureShortcutHelper() { - Assert.assertTrue(sendKeyGestureEventComplete( - KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER)); + sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_OPEN_SHORTCUT_HELPER); mPhoneWindowManager.assertToggleShortcutsMenu(); } @@ -549,173 +529,139 @@ public class KeyGestureEventTests extends ShortcutKeyTestBase { for (int i = 0; i < currentBrightness.length; i++) { mPhoneWindowManager.prepareBrightnessDecrease(currentBrightness[i]); - Assert.assertTrue( - sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_BRIGHTNESS_DOWN)); + sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_BRIGHTNESS_DOWN); mPhoneWindowManager.verifyNewBrightness(newBrightness[i]); } } @Test public void testKeyGestureRecentAppSwitcher() { - Assert.assertTrue(sendKeyGestureEventStart( - KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER)); + sendKeyGestureEventStart(KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER); mPhoneWindowManager.assertShowRecentApps(); - - Assert.assertTrue(sendKeyGestureEventComplete( - KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER)); + sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS_SWITCHER); mPhoneWindowManager.assertHideRecentApps(); } @Test public void testKeyGestureLanguageSwitch() { - Assert.assertTrue( - sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_LANGUAGE_SWITCH)); + sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_LANGUAGE_SWITCH); mPhoneWindowManager.assertSwitchKeyboardLayout(1, DEFAULT_DISPLAY); - Assert.assertTrue( - sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_LANGUAGE_SWITCH, - KeyEvent.META_SHIFT_ON)); + sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_LANGUAGE_SWITCH, + KeyEvent.META_SHIFT_ON); mPhoneWindowManager.assertSwitchKeyboardLayout(-1, DEFAULT_DISPLAY); } @Test public void testKeyGestureLaunchSearch() { - Assert.assertTrue( - sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SEARCH)); + sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SEARCH); mPhoneWindowManager.assertLaunchSearch(); } @Test public void testKeyGestureScreenshotChord() { - Assert.assertTrue( - sendKeyGestureEventStart(KeyGestureEvent.KEY_GESTURE_TYPE_SCREENSHOT_CHORD)); + sendKeyGestureEventStart(KeyGestureEvent.KEY_GESTURE_TYPE_SCREENSHOT_CHORD); mPhoneWindowManager.moveTimeForward(500); - Assert.assertTrue( - sendKeyGestureEventCancel(KeyGestureEvent.KEY_GESTURE_TYPE_SCREENSHOT_CHORD)); + sendKeyGestureEventCancel(KeyGestureEvent.KEY_GESTURE_TYPE_SCREENSHOT_CHORD); mPhoneWindowManager.assertTakeScreenshotCalled(); } @Test public void testKeyGestureScreenshotChordCancelled() { - Assert.assertTrue( - sendKeyGestureEventStart(KeyGestureEvent.KEY_GESTURE_TYPE_SCREENSHOT_CHORD)); - Assert.assertTrue( - sendKeyGestureEventCancel(KeyGestureEvent.KEY_GESTURE_TYPE_SCREENSHOT_CHORD)); + sendKeyGestureEventStart(KeyGestureEvent.KEY_GESTURE_TYPE_SCREENSHOT_CHORD); + sendKeyGestureEventCancel(KeyGestureEvent.KEY_GESTURE_TYPE_SCREENSHOT_CHORD); mPhoneWindowManager.assertTakeScreenshotNotCalled(); } @Test public void testKeyGestureRingerToggleChord() { mPhoneWindowManager.overridePowerVolumeUp(POWER_VOLUME_UP_BEHAVIOR_MUTE); - Assert.assertTrue( - sendKeyGestureEventStart(KeyGestureEvent.KEY_GESTURE_TYPE_RINGER_TOGGLE_CHORD)); + sendKeyGestureEventStart(KeyGestureEvent.KEY_GESTURE_TYPE_RINGER_TOGGLE_CHORD); mPhoneWindowManager.moveTimeForward(500); - Assert.assertTrue( - sendKeyGestureEventCancel(KeyGestureEvent.KEY_GESTURE_TYPE_RINGER_TOGGLE_CHORD)); + sendKeyGestureEventCancel(KeyGestureEvent.KEY_GESTURE_TYPE_RINGER_TOGGLE_CHORD); mPhoneWindowManager.assertVolumeMute(); } @Test public void testKeyGestureRingerToggleChordCancelled() { mPhoneWindowManager.overridePowerVolumeUp(POWER_VOLUME_UP_BEHAVIOR_MUTE); - Assert.assertTrue( - sendKeyGestureEventStart(KeyGestureEvent.KEY_GESTURE_TYPE_RINGER_TOGGLE_CHORD)); - Assert.assertTrue( - sendKeyGestureEventCancel(KeyGestureEvent.KEY_GESTURE_TYPE_RINGER_TOGGLE_CHORD)); + sendKeyGestureEventStart(KeyGestureEvent.KEY_GESTURE_TYPE_RINGER_TOGGLE_CHORD); + sendKeyGestureEventCancel(KeyGestureEvent.KEY_GESTURE_TYPE_RINGER_TOGGLE_CHORD); mPhoneWindowManager.assertVolumeNotMuted(); } @Test public void testKeyGestureGlobalAction() { mPhoneWindowManager.overridePowerVolumeUp(POWER_VOLUME_UP_BEHAVIOR_GLOBAL_ACTIONS); - Assert.assertTrue( - sendKeyGestureEventStart(KeyGestureEvent.KEY_GESTURE_TYPE_GLOBAL_ACTIONS)); + sendKeyGestureEventStart(KeyGestureEvent.KEY_GESTURE_TYPE_GLOBAL_ACTIONS); mPhoneWindowManager.moveTimeForward(500); - Assert.assertTrue( - sendKeyGestureEventCancel(KeyGestureEvent.KEY_GESTURE_TYPE_GLOBAL_ACTIONS)); + sendKeyGestureEventCancel(KeyGestureEvent.KEY_GESTURE_TYPE_GLOBAL_ACTIONS); mPhoneWindowManager.assertShowGlobalActionsCalled(); } @Test public void testKeyGestureGlobalActionCancelled() { mPhoneWindowManager.overridePowerVolumeUp(POWER_VOLUME_UP_BEHAVIOR_GLOBAL_ACTIONS); - Assert.assertTrue( - sendKeyGestureEventStart(KeyGestureEvent.KEY_GESTURE_TYPE_GLOBAL_ACTIONS)); - Assert.assertTrue( - sendKeyGestureEventCancel(KeyGestureEvent.KEY_GESTURE_TYPE_GLOBAL_ACTIONS)); + sendKeyGestureEventStart(KeyGestureEvent.KEY_GESTURE_TYPE_GLOBAL_ACTIONS); + sendKeyGestureEventCancel(KeyGestureEvent.KEY_GESTURE_TYPE_GLOBAL_ACTIONS); mPhoneWindowManager.assertShowGlobalActionsNotCalled(); } @Test public void testKeyGestureTvTriggerBugReport() { - Assert.assertTrue( - sendKeyGestureEventStart(KeyGestureEvent.KEY_GESTURE_TYPE_TV_TRIGGER_BUG_REPORT)); + sendKeyGestureEventStart(KeyGestureEvent.KEY_GESTURE_TYPE_TV_TRIGGER_BUG_REPORT); mPhoneWindowManager.moveTimeForward(1000); - Assert.assertTrue( - sendKeyGestureEventCancel(KeyGestureEvent.KEY_GESTURE_TYPE_TV_TRIGGER_BUG_REPORT)); + sendKeyGestureEventCancel(KeyGestureEvent.KEY_GESTURE_TYPE_TV_TRIGGER_BUG_REPORT); mPhoneWindowManager.assertBugReportTakenForTv(); } @Test public void testKeyGestureTvTriggerBugReportCancelled() { - Assert.assertTrue( - sendKeyGestureEventStart(KeyGestureEvent.KEY_GESTURE_TYPE_TV_TRIGGER_BUG_REPORT)); - Assert.assertTrue( - sendKeyGestureEventCancel(KeyGestureEvent.KEY_GESTURE_TYPE_TV_TRIGGER_BUG_REPORT)); + sendKeyGestureEventStart(KeyGestureEvent.KEY_GESTURE_TYPE_TV_TRIGGER_BUG_REPORT); + sendKeyGestureEventCancel(KeyGestureEvent.KEY_GESTURE_TYPE_TV_TRIGGER_BUG_REPORT); mPhoneWindowManager.assertBugReportNotTakenForTv(); } @Test public void testKeyGestureAccessibilityShortcut() { - Assert.assertTrue( - sendKeyGestureEventComplete( - KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT)); + sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT); mPhoneWindowManager.assertAccessibilityKeychordCalled(); } @Test public void testKeyGestureCloseAllDialogs() { - Assert.assertTrue( - sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_CLOSE_ALL_DIALOGS)); + sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_CLOSE_ALL_DIALOGS); mPhoneWindowManager.assertCloseAllDialogs(); } @Test @EnableFlags(com.android.hardware.input.Flags.FLAG_ENABLE_TALKBACK_AND_MAGNIFIER_KEY_GESTURES) public void testKeyGestureToggleTalkback() { - Assert.assertTrue( - sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_TALKBACK)); + sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_TALKBACK); mPhoneWindowManager.assertTalkBack(true); - Assert.assertTrue( - sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_TALKBACK)); + sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_TALKBACK); mPhoneWindowManager.assertTalkBack(false); } @Test @EnableFlags(com.android.hardware.input.Flags.FLAG_ENABLE_VOICE_ACCESS_KEY_GESTURES) public void testKeyGestureToggleVoiceAccess() { - Assert.assertTrue( - sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_VOICE_ACCESS)); + sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_VOICE_ACCESS); mPhoneWindowManager.assertVoiceAccess(true); - Assert.assertTrue( - sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_VOICE_ACCESS)); + sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_VOICE_ACCESS); mPhoneWindowManager.assertVoiceAccess(false); } @Test public void testKeyGestureToggleDoNotDisturb() { mPhoneWindowManager.overrideZenMode(Settings.Global.ZEN_MODE_OFF); - Assert.assertTrue( - sendKeyGestureEventComplete( - KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_DO_NOT_DISTURB)); + sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_DO_NOT_DISTURB); mPhoneWindowManager.assertZenMode(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS); mPhoneWindowManager.overrideZenMode(Settings.Global.ZEN_MODE_IMPORTANT_INTERRUPTIONS); - Assert.assertTrue( - sendKeyGestureEventComplete( - KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_DO_NOT_DISTURB)); + sendKeyGestureEventComplete(KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_DO_NOT_DISTURB); mPhoneWindowManager.assertZenMode(Settings.Global.ZEN_MODE_OFF); } diff --git a/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTests.java b/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTests.java index 32a3b7f2c9cc..f3d5e39ec127 100644 --- a/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTests.java +++ b/services/tests/wmtests/src/com/android/server/policy/PhoneWindowManagerTests.java @@ -35,6 +35,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; import static com.android.dx.mockito.inline.extended.ExtendedMockito.when; import static com.android.server.policy.PhoneWindowManager.EXTRA_TRIGGER_HUB; +import static com.android.server.policy.PhoneWindowManager.SHORT_PRESS_POWER_DREAM_OR_AWAKE_OR_SLEEP; import static com.android.server.policy.PhoneWindowManager.SHORT_PRESS_POWER_HUB_OR_DREAM_OR_SLEEP; import static com.google.common.truth.Truth.assertThat; @@ -43,6 +44,7 @@ import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyList; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; @@ -63,6 +65,7 @@ import android.view.contentprotection.flags.Flags; import androidx.test.filters.SmallTest; import com.android.internal.util.test.LocalServiceKeeperRule; +import com.android.internal.widget.LockPatternUtils; import com.android.server.input.InputManagerInternal; import com.android.server.pm.UserManagerInternal; import com.android.server.policy.keyguard.KeyguardServiceDelegate; @@ -119,6 +122,8 @@ public class PhoneWindowManagerTests { private DisplayPolicy mDisplayPolicy; @Mock private KeyguardServiceDelegate mKeyguardServiceDelegate; + @Mock + private LockPatternUtils mLockPatternUtils; @Before public void setUp() { @@ -146,7 +151,7 @@ public class PhoneWindowManagerTests { mPhoneWindowManager.mKeyguardDelegate = mKeyguardServiceDelegate; final InputManager im = mock(InputManager.class); - doNothing().when(im).registerKeyGestureEventHandler(any()); + doNothing().when(im).registerKeyGestureEventHandler(anyList(), any()); doReturn(im).when(mContext).getSystemService(eq(Context.INPUT_SERVICE)); } @@ -253,6 +258,7 @@ public class PhoneWindowManagerTests { @Test public void powerPress_hubOrDreamOrSleep_goesToSleepFromDream() { when(mDisplayPolicy.isAwake()).thenReturn(true); + when(mLockPatternUtils.isLockScreenDisabled(anyInt())).thenReturn(false); initPhoneWindowManager(); // Set power button behavior. @@ -274,6 +280,7 @@ public class PhoneWindowManagerTests { @Test public void powerPress_hubOrDreamOrSleep_hubAvailableLocks() { when(mDisplayPolicy.isAwake()).thenReturn(true); + when(mLockPatternUtils.isLockScreenDisabled(anyInt())).thenReturn(false); mContext.getTestablePermissions().setPermission(android.Manifest.permission.DEVICE_POWER, PERMISSION_GRANTED); initPhoneWindowManager(); @@ -302,6 +309,7 @@ public class PhoneWindowManagerTests { @Test public void powerPress_hubOrDreamOrSleep_hubNotAvailableDreams() { when(mDisplayPolicy.isAwake()).thenReturn(true); + when(mLockPatternUtils.isLockScreenDisabled(anyInt())).thenReturn(false); initPhoneWindowManager(); // Set power button behavior. @@ -322,6 +330,77 @@ public class PhoneWindowManagerTests { verify(mDreamManagerInternal).requestDream(); } + @Test + public void powerPress_dreamOrAwakeOrSleep_awakeFromDream() { + when(mDisplayPolicy.isAwake()).thenReturn(true); + initPhoneWindowManager(); + + // Set power button behavior. + Settings.Global.putInt(mContext.getContentResolver(), + Settings.Global.POWER_BUTTON_SHORT_PRESS, + SHORT_PRESS_POWER_DREAM_OR_AWAKE_OR_SLEEP); + mPhoneWindowManager.updateSettings(null); + + // Can not dream when device is dreaming. + when(mDreamManagerInternal.canStartDreaming(any(Boolean.class))).thenReturn(false); + // Device is dreaming. + when(mDreamManagerInternal.isDreaming()).thenReturn(true); + + // Power button pressed. + int eventTime = 0; + mPhoneWindowManager.powerPress(eventTime, 1, 0); + + // Dream is stopped. + verify(mDreamManagerInternal) + .stopDream(false /*immediate*/, "short press power" /*reason*/); + } + + @Test + public void powerPress_dreamOrAwakeOrSleep_canNotDreamGoToSleep() { + when(mDisplayPolicy.isAwake()).thenReturn(true); + initPhoneWindowManager(); + + // Set power button behavior. + Settings.Global.putInt(mContext.getContentResolver(), + Settings.Global.POWER_BUTTON_SHORT_PRESS, + SHORT_PRESS_POWER_DREAM_OR_AWAKE_OR_SLEEP); + mPhoneWindowManager.updateSettings(null); + + // Can not dream for other reasons. + when(mDreamManagerInternal.canStartDreaming(any(Boolean.class))).thenReturn(false); + // Device is not dreaming. + when(mDreamManagerInternal.isDreaming()).thenReturn(false); + + // Power button pressed. + int eventTime = 0; + mPhoneWindowManager.powerPress(eventTime, 1, 0); + + // Device goes to sleep. + verify(mPowerManager).goToSleep(eventTime, PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON, 0); + } + + @Test + public void powerPress_dreamOrAwakeOrSleep_dreamFromActive() { + when(mDisplayPolicy.isAwake()).thenReturn(true); + initPhoneWindowManager(); + + // Set power button behavior. + Settings.Global.putInt(mContext.getContentResolver(), + Settings.Global.POWER_BUTTON_SHORT_PRESS, + SHORT_PRESS_POWER_DREAM_OR_AWAKE_OR_SLEEP); + mPhoneWindowManager.updateSettings(null); + + // Can dream when active. + when(mDreamManagerInternal.canStartDreaming(any(Boolean.class))).thenReturn(true); + + // Power button pressed. + int eventTime = 0; + mPhoneWindowManager.powerPress(eventTime, 1, 0); + + // Dream is requested. + verify(mDreamManagerInternal).requestDream(); + } + private void initPhoneWindowManager() { mPhoneWindowManager.mDefaultDisplayPolicy = mDisplayPolicy; mPhoneWindowManager.mDefaultDisplayRotation = mock(DisplayRotation.class); @@ -345,6 +424,11 @@ public class PhoneWindowManagerTests { return mKeyguardServiceDelegate; } + @Override + LockPatternUtils getLockPatternUtils() { + return mLockPatternUtils; + } + /** * {@code WindowWakeUpPolicy} registers a local service in its constructor, easier to just * mock it out so we don't have to unregister it after every test. diff --git a/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java b/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java index c57adfd69b06..f89c6f638384 100644 --- a/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java +++ b/services/tests/wmtests/src/com/android/server/policy/ShortcutKeyTestBase.java @@ -238,33 +238,33 @@ class ShortcutKeyTestBase { sendKeyCombination(new int[]{keyCode}, durationMillis, false, DEFAULT_DISPLAY); } - boolean sendKeyGestureEventStart(int gestureType) { - return mPhoneWindowManager.sendKeyGestureEvent( + void sendKeyGestureEventStart(int gestureType) { + mPhoneWindowManager.sendKeyGestureEvent( new KeyGestureEvent.Builder().setKeyGestureType(gestureType).setAction( KeyGestureEvent.ACTION_GESTURE_START).build()); } - boolean sendKeyGestureEventComplete(int gestureType) { - return mPhoneWindowManager.sendKeyGestureEvent( + void sendKeyGestureEventComplete(int gestureType) { + mPhoneWindowManager.sendKeyGestureEvent( new KeyGestureEvent.Builder().setKeyGestureType(gestureType).setAction( KeyGestureEvent.ACTION_GESTURE_COMPLETE).build()); } - boolean sendKeyGestureEventCancel(int gestureType) { - return mPhoneWindowManager.sendKeyGestureEvent( + void sendKeyGestureEventCancel(int gestureType) { + mPhoneWindowManager.sendKeyGestureEvent( new KeyGestureEvent.Builder().setKeyGestureType(gestureType).setAction( KeyGestureEvent.ACTION_GESTURE_COMPLETE).setFlags( KeyGestureEvent.FLAG_CANCELLED).build()); } - boolean sendKeyGestureEventComplete(int gestureType, int modifierState) { - return mPhoneWindowManager.sendKeyGestureEvent( + void sendKeyGestureEventComplete(int gestureType, int modifierState) { + mPhoneWindowManager.sendKeyGestureEvent( new KeyGestureEvent.Builder().setModifierState(modifierState).setKeyGestureType( gestureType).setAction(KeyGestureEvent.ACTION_GESTURE_COMPLETE).build()); } - boolean sendKeyGestureEventComplete(int keycode, int modifierState, int gestureType) { - return mPhoneWindowManager.sendKeyGestureEvent( + void sendKeyGestureEventComplete(int keycode, int modifierState, int gestureType) { + mPhoneWindowManager.sendKeyGestureEvent( new KeyGestureEvent.Builder().setKeycodes(new int[]{keycode}).setModifierState( modifierState).setKeyGestureType(gestureType).setAction( KeyGestureEvent.ACTION_GESTURE_COMPLETE).build()); diff --git a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java index e56fd3c6272d..7b6d361c55d4 100644 --- a/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java +++ b/services/tests/wmtests/src/com/android/server/policy/TestPhoneWindowManager.java @@ -49,6 +49,7 @@ import static com.android.server.policy.PhoneWindowManager.POWER_VOLUME_UP_BEHAV import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyList; import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.CALLS_REAL_METHODS; import static org.mockito.Mockito.after; @@ -353,7 +354,7 @@ class TestPhoneWindowManager { doReturn(mAppOpsManager).when(mContext).getSystemService(eq(AppOpsManager.class)); doReturn(mDisplayManager).when(mContext).getSystemService(eq(DisplayManager.class)); doReturn(mInputManager).when(mContext).getSystemService(eq(InputManager.class)); - doNothing().when(mInputManager).registerKeyGestureEventHandler(any()); + doNothing().when(mInputManager).registerKeyGestureEventHandler(anyList(), any()); doNothing().when(mInputManager).unregisterKeyGestureEventHandler(any()); doReturn(mPackageManager).when(mContext).getPackageManager(); doReturn(mSensorPrivacyManager).when(mContext).getSystemService( @@ -476,8 +477,8 @@ class TestPhoneWindowManager { mPhoneWindowManager.interceptUnhandledKey(event, mInputToken); } - boolean sendKeyGestureEvent(KeyGestureEvent event) { - return mPhoneWindowManager.handleKeyGestureEvent(event, mInputToken); + void sendKeyGestureEvent(KeyGestureEvent event) { + mPhoneWindowManager.handleKeyGestureEvent(event, mInputToken); } /** diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java index 7f242dea9f45..773a566f6315 100644 --- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java @@ -1459,21 +1459,6 @@ public class ActivityRecordTests extends WindowTestsBase { } /** - * Verify that finish bottom activity from a task won't boost it to top. - */ - @Test - public void testFinishBottomActivityIfPossible_noZBoost() { - final ActivityRecord bottomActivity = createActivityWithTask(); - final ActivityRecord topActivity = new ActivityBuilder(mAtm) - .setTask(bottomActivity.getTask()).build(); - topActivity.setVisibleRequested(true); - // simulating bottomActivity as a trampoline activity. - bottomActivity.setState(RESUMED, "test"); - bottomActivity.finishIfPossible("test", false); - assertFalse(bottomActivity.mNeedsZBoost); - } - - /** * Verify that complete finish request for visible activity must be delayed before the next one * becomes visible. */ diff --git a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java b/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java deleted file mode 100644 index e4628c45a15b..000000000000 --- a/services/tests/wmtests/src/com/android/server/wm/AppWindowTokenAnimationTests.java +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server.wm; - -import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; -import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_APP_TRANSITION; - -import static com.google.common.truth.Truth.assertThat; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.ArgumentMatchers.intThat; - -import android.platform.test.annotations.Presubmit; -import android.view.SurfaceControl; - -import androidx.test.filters.SmallTest; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - - -/** - * Animation related tests for the {@link ActivityRecord} class. - * - * Build/Install/Run: - * atest AppWindowTokenAnimationTests - */ -@SmallTest -@Presubmit -@RunWith(WindowTestRunner.class) -public class AppWindowTokenAnimationTests extends WindowTestsBase { - - private ActivityRecord mActivity; - - @Mock - private AnimationAdapter mSpec; - - @Before - public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - - mActivity = createActivityRecord(mDisplayContent); - } - - @Test - public void clipAfterAnim_boundsLayerIsCreated() { - mActivity.mNeedsAnimationBoundsLayer = true; - - mActivity.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */, - ANIMATION_TYPE_APP_TRANSITION); - verify(mTransaction).reparent(eq(mActivity.getSurfaceControl()), - eq(mActivity.mSurfaceAnimator.mLeash)); - verify(mTransaction).reparent(eq(mActivity.mSurfaceAnimator.mLeash), - eq(mActivity.mAnimationBoundsLayer)); - } - - @Test - public void clipAfterAnim_boundsLayerZBoosted() { - final Task task = mActivity.getTask(); - final ActivityRecord topActivity = createActivityRecord(task); - task.assignChildLayers(mTransaction); - - assertThat(topActivity.getLastLayer()).isGreaterThan(mActivity.getLastLayer()); - - mActivity.mNeedsAnimationBoundsLayer = true; - mActivity.mNeedsZBoost = true; - mActivity.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */, - ANIMATION_TYPE_APP_TRANSITION); - - verify(mTransaction).setLayer(eq(mActivity.mAnimationBoundsLayer), - intThat(layer -> layer > topActivity.getLastLayer())); - - // The layer should be restored after the animation leash is removed. - mActivity.onAnimationLeashLost(mTransaction); - assertThat(mActivity.mNeedsZBoost).isFalse(); - assertThat(topActivity.getLastLayer()).isGreaterThan(mActivity.getLastLayer()); - } - - @Test - public void clipAfterAnim_boundsLayerIsDestroyed() { - mActivity.mNeedsAnimationBoundsLayer = true; - mActivity.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */, - ANIMATION_TYPE_APP_TRANSITION); - final SurfaceControl leash = mActivity.mSurfaceAnimator.mLeash; - final SurfaceControl animationBoundsLayer = mActivity.mAnimationBoundsLayer; - final ArgumentCaptor<SurfaceAnimator.OnAnimationFinishedCallback> callbackCaptor = - ArgumentCaptor.forClass( - SurfaceAnimator.OnAnimationFinishedCallback.class); - verify(mSpec).startAnimation(any(), any(), eq(ANIMATION_TYPE_APP_TRANSITION), - callbackCaptor.capture()); - - callbackCaptor.getValue().onAnimationFinished( - ANIMATION_TYPE_APP_TRANSITION, mSpec); - verify(mTransaction).remove(eq(leash)); - verify(mTransaction).remove(eq(animationBoundsLayer)); - assertThat(mActivity.mNeedsAnimationBoundsLayer).isFalse(); - } - - @Test - public void clipAfterAnimCancelled_boundsLayerIsDestroyed() { - mActivity.mNeedsAnimationBoundsLayer = true; - mActivity.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */, - ANIMATION_TYPE_APP_TRANSITION); - final SurfaceControl leash = mActivity.mSurfaceAnimator.mLeash; - final SurfaceControl animationBoundsLayer = mActivity.mAnimationBoundsLayer; - - mActivity.mSurfaceAnimator.cancelAnimation(); - verify(mTransaction).remove(eq(leash)); - verify(mTransaction).remove(eq(animationBoundsLayer)); - assertThat(mActivity.mNeedsAnimationBoundsLayer).isFalse(); - } - - @Test - public void clipNoneAnim_boundsLayerIsNotCreated() { - mActivity.mNeedsAnimationBoundsLayer = false; - - mActivity.mSurfaceAnimator.startAnimation(mTransaction, mSpec, true /* hidden */, - ANIMATION_TYPE_APP_TRANSITION); - verify(mTransaction).reparent(eq(mActivity.getSurfaceControl()), - eq(mActivity.mSurfaceAnimator.mLeash)); - assertThat(mActivity.mAnimationBoundsLayer).isNull(); - } -} diff --git a/services/tests/wmtests/src/com/android/server/wm/BackgroundLaunchProcessControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BackgroundLaunchProcessControllerTests.java index 27e147d98b1f..747b09cb5660 100644 --- a/services/tests/wmtests/src/com/android/server/wm/BackgroundLaunchProcessControllerTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/BackgroundLaunchProcessControllerTests.java @@ -74,14 +74,14 @@ public class BackgroundLaunchProcessControllerTests { BackgroundActivityStartCallback mCallback = new BackgroundActivityStartCallback() { @Override - public boolean isActivityStartAllowed(Collection<IBinder> tokens, int uid, - String packageName) { + public BackgroundActivityStartCallbackResult isActivityStartAllowed( + Collection<IBinder> tokens, int uid, String packageName) { for (IBinder token : tokens) { if (token == null || mActivityStartAllowed.contains(token)) { - return true; + return new BackgroundActivityStartCallbackResult(true, token); } } - return false; + return RESULT_FALSE; } @Override diff --git a/services/tests/wmtests/src/com/android/server/wm/DesktopAppCompatAspectRatioPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DesktopAppCompatAspectRatioPolicyTests.java index fa7dcc8ebbc7..81d753b8e079 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DesktopAppCompatAspectRatioPolicyTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DesktopAppCompatAspectRatioPolicyTests.java @@ -35,6 +35,7 @@ import static com.android.server.wm.AppCompatConfiguration.DEFAULT_LETTERBOX_ASP import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; import android.compat.testing.PlatformCompatChangeRule; import android.content.pm.ActivityInfo; @@ -413,7 +414,7 @@ public class DesktopAppCompatAspectRatioPolicyTests extends WindowTestsBase { void setDesiredAspectRatio(float aspectRatio) { doReturn(aspectRatio).when(getDesktopAppCompatAspectRatioPolicy()) - .getDesiredAspectRatio(any()); + .getDesiredAspectRatio(any(), anyBoolean()); } DesktopAppCompatAspectRatioPolicy getDesktopAppCompatAspectRatioPolicy() { @@ -422,7 +423,7 @@ public class DesktopAppCompatAspectRatioPolicyTests extends WindowTestsBase { float calculateAspectRatio() { return getDesktopAppCompatAspectRatioPolicy().calculateAspectRatio( - getTopActivity().getTask()); + getTopActivity().getTask(), /* hasOrientationMismatch */ true); } ActivityRecord getTopActivity() { diff --git a/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java b/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java index 00b617e91bfd..f587d6e8c346 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DesktopModeLaunchParamsModifierTests.java @@ -52,6 +52,7 @@ import static com.android.server.wm.LaunchParamsController.LaunchParamsModifier. import static org.junit.Assert.assertEquals; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; @@ -437,7 +438,7 @@ public class DesktopModeLaunchParamsModifierTests extends spyOn(activity.mAppCompatController.getDesktopAspectRatioPolicy()); doReturn(LETTERBOX_ASPECT_RATIO).when(activity.mAppCompatController - .getDesktopAspectRatioPolicy()).calculateAspectRatio(any()); + .getDesktopAspectRatioPolicy()).calculateAspectRatio(any(), anyBoolean()); final int desiredWidth = (int) ((LANDSCAPE_DISPLAY_BOUNDS.height() / LETTERBOX_ASPECT_RATIO) + 0.5f); @@ -933,7 +934,7 @@ public class DesktopModeLaunchParamsModifierTests extends spyOn(activity.mAppCompatController.getDesktopAspectRatioPolicy()); doReturn(LETTERBOX_ASPECT_RATIO).when(activity.mAppCompatController - .getDesktopAspectRatioPolicy()).calculateAspectRatio(any()); + .getDesktopAspectRatioPolicy()).calculateAspectRatio(any(), anyBoolean()); final int desiredHeight = (int) (LANDSCAPE_DISPLAY_BOUNDS.height() * DESKTOP_MODE_INITIAL_BOUNDS_SCALE); @@ -1060,7 +1061,7 @@ public class DesktopModeLaunchParamsModifierTests extends spyOn(activity.mAppCompatController.getDesktopAspectRatioPolicy()); doReturn(LETTERBOX_ASPECT_RATIO).when(activity.mAppCompatController - .getDesktopAspectRatioPolicy()).calculateAspectRatio(any()); + .getDesktopAspectRatioPolicy()).calculateAspectRatio(any(), anyBoolean()); final int desiredWidth = PORTRAIT_DISPLAY_BOUNDS.width() - (DESKTOP_MODE_LANDSCAPE_APP_PADDING * 2); @@ -1115,7 +1116,7 @@ public class DesktopModeLaunchParamsModifierTests extends spyOn(activity.mAppCompatController.getDesktopAspectRatioPolicy()); doReturn(LETTERBOX_ASPECT_RATIO).when(activity.mAppCompatController - .getDesktopAspectRatioPolicy()).calculateAspectRatio(any()); + .getDesktopAspectRatioPolicy()).calculateAspectRatio(any(), anyBoolean()); final int desiredWidth = PORTRAIT_DISPLAY_BOUNDS.width() - (DESKTOP_MODE_LANDSCAPE_APP_PADDING * 2); @@ -1569,7 +1570,7 @@ public class DesktopModeLaunchParamsModifierTests extends activity.mAppCompatController.getDesktopAspectRatioPolicy(); spyOn(desktopAppCompatAspectRatioPolicy); doReturn(aspectRatio).when(desktopAppCompatAspectRatioPolicy) - .getDesiredAspectRatio(any()); + .getDesiredAspectRatio(any(), anyBoolean()); } private void applyUserMinAspectRatioOverride(ActivityRecord activity, int overrideCode, @@ -1579,7 +1580,7 @@ public class DesktopModeLaunchParamsModifierTests extends activity.mAppCompatController.getDesktopAspectRatioPolicy(); spyOn(desktopAppCompatAspectRatioPolicy); doReturn(1f).when(desktopAppCompatAspectRatioPolicy) - .getDesiredAspectRatio(any()); + .getDesiredAspectRatio(any(), anyBoolean()); // Enable user aspect ratio settings final AppCompatConfiguration appCompatConfiguration = diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java index ec83c50e95aa..1aa8681c9bfd 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java @@ -49,10 +49,8 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.clearInvocations; -import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import android.app.ActivityOptions; @@ -192,24 +190,6 @@ public class TaskDisplayAreaTests extends WindowTestsBase { } @Test - public void testActivityWithZBoost_taskDisplayAreaDoesNotMoveUp() { - final Task rootTask = createTask(mDisplayContent); - final Task task = createTaskInRootTask(rootTask, 0 /* userId */); - final ActivityRecord activity = createNonAttachedActivityRecord(mDisplayContent); - task.addChild(activity, 0 /* addPos */); - final TaskDisplayArea taskDisplayArea = activity.getDisplayArea(); - activity.mNeedsAnimationBoundsLayer = true; - activity.mNeedsZBoost = true; - spyOn(taskDisplayArea.mSurfaceAnimator); - - mDisplayContent.assignChildLayers(mTransaction); - - assertThat(activity.needsZBoost()).isTrue(); - assertThat(taskDisplayArea.needsZBoost()).isFalse(); - verify(taskDisplayArea.mSurfaceAnimator, never()).setLayer(eq(mTransaction), anyInt()); - } - - @Test public void testRootTaskPositionChildAt() { Task pinnedTask = createTask( mDisplayContent, WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD); diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java index 044aacc4b988..b617f0285606 100644 --- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java @@ -100,8 +100,6 @@ import androidx.test.filters.MediumTest; import com.android.modules.utils.TypedXmlPullParser; import com.android.modules.utils.TypedXmlSerializer; -import libcore.junit.util.compat.CoreCompatChangeRule; - import org.junit.Assert; import org.junit.Before; import org.junit.Rule; @@ -414,79 +412,96 @@ public class TaskTests extends WindowTestsBase { } @Test - @CoreCompatChangeRule.EnableCompatChanges({ActivityInfo.FORCE_RESIZE_APP}) - public void testIsResizeable_nonResizeable_forceResize_overridesEnabled_Resizeable() { + public void testIsResizeable_nonResizeable_forceResize_overridesEnabled_resizeable() { final Task task = new TaskBuilder(mSupervisor) .setCreateActivity(true) - .setComponent( - ComponentName.createRelative(mContext, SizeCompatTests.class.getName())) .build(); task.setResizeMode(RESIZE_MODE_UNRESIZEABLE); + final ActivityRecord activity = task.getRootActivity(); + final AppCompatResizeOverrides resizeOverrides = + activity.mAppCompatController.getResizeOverrides(); + spyOn(activity); + spyOn(resizeOverrides); + doReturn(true).when(resizeOverrides).shouldOverrideForceResizeApp(); + task.intent = null; + task.setIntent(activity); // Override should take effect and task should be resizeable. assertTrue(task.getTaskInfo().isResizeable); } @Test - @CoreCompatChangeRule.EnableCompatChanges({ActivityInfo.FORCE_RESIZE_APP}) - public void testIsResizeable_nonResizeable_forceResize_overridesDisabled_nonResizeable() { + public void testIsResizeable_resizeable_forceNonResize_overridesEnabled_nonResizeable() { final Task task = new TaskBuilder(mSupervisor) .setCreateActivity(true) - .setComponent( - ComponentName.createRelative(mContext, SizeCompatTests.class.getName())) .build(); - task.setResizeMode(RESIZE_MODE_UNRESIZEABLE); - - // Disallow resize overrides. - task.mAllowForceResizeOverride = false; + task.setResizeMode(RESIZE_MODE_RESIZEABLE); + final ActivityRecord activity = task.getRootActivity(); + final AppCompatResizeOverrides resizeOverrides = + activity.mAppCompatController.getResizeOverrides(); + spyOn(activity); + spyOn(resizeOverrides); + doReturn(true).when(resizeOverrides).shouldOverrideForceNonResizeApp(); + task.intent = null; + task.setIntent(activity); - // Override should not take effect and task should be un-resizeable. + // Override should take effect and task should be un-resizeable. assertFalse(task.getTaskInfo().isResizeable); } @Test - @CoreCompatChangeRule.EnableCompatChanges({ActivityInfo.FORCE_NON_RESIZE_APP}) - public void testIsResizeable_resizeable_forceNonResize_overridesEnabled_nonResizeable() { + public void testIsResizeable_resizeableTask_fullscreenOverride_resizeable() { final Task task = new TaskBuilder(mSupervisor) .setCreateActivity(true) - .setComponent( - ComponentName.createRelative(mContext, SizeCompatTests.class.getName())) .build(); - task.setResizeMode(RESIZE_MODE_RESIZEABLE); + task.setResizeMode(RESIZE_MODE_UNRESIZEABLE); + final ActivityRecord activity = task.getRootActivity(); + final AppCompatAspectRatioOverrides aspectRatioOverrides = + activity.mAppCompatController.getAspectRatioOverrides(); + spyOn(aspectRatioOverrides); + doReturn(true).when(aspectRatioOverrides).hasFullscreenOverride(); + task.intent = null; + task.setIntent(activity); - // Override should take effect and task should be un-resizeable. - assertFalse(task.getTaskInfo().isResizeable); + // Override should take effect and task should be resizeable. + assertTrue(task.getTaskInfo().isResizeable); } @Test - @CoreCompatChangeRule.EnableCompatChanges({ActivityInfo.FORCE_NON_RESIZE_APP}) - public void testIsResizeable_resizeable_forceNonResize_overridesDisabled_Resizeable() { + public void testIsResizeable_resizeableTask_universalResizeable_resizeable() { final Task task = new TaskBuilder(mSupervisor) .setCreateActivity(true) - .setComponent( - ComponentName.createRelative(mContext, SizeCompatTests.class.getName())) .build(); - task.setResizeMode(RESIZE_MODE_RESIZEABLE); - - // Disallow resize overrides. - task.mAllowForceResizeOverride = false; + task.setResizeMode(RESIZE_MODE_UNRESIZEABLE); + final ActivityRecord activity = task.getRootActivity(); + spyOn(activity); + doReturn(true).when(activity).isUniversalResizeable(); + task.intent = null; + task.setIntent(activity); - // Override should not take effect and task should be resizeable. + // Override should take effect and task should be resizeable. assertTrue(task.getTaskInfo().isResizeable); } @Test - @CoreCompatChangeRule.EnableCompatChanges({ActivityInfo.FORCE_NON_RESIZE_APP}) - public void testIsResizeable_systemWideForceResize_compatForceNonResize__Resizeable() { + public void testIsResizeable_systemWideForceResize_compatForceNonResize_resizeable() { final Task task = new TaskBuilder(mSupervisor) .setCreateActivity(true) - .setComponent( - ComponentName.createRelative(mContext, SizeCompatTests.class.getName())) + .setComponent(ComponentName.createRelative(mContext, TaskTests.class.getName())) .build(); task.setResizeMode(RESIZE_MODE_RESIZEABLE); // Set system-wide force resizeable override. task.mAtmService.mForceResizableActivities = true; + final ActivityRecord activity = task.getRootActivity(); + final AppCompatResizeOverrides resizeOverrides = + activity.mAppCompatController.getResizeOverrides(); + spyOn(activity); + spyOn(resizeOverrides); + doReturn(true).when(resizeOverrides).shouldOverrideForceNonResizeApp(); + task.intent = null; + task.setIntent(activity); + // System wide override should tak priority over app compat override so the task should // remain resizeable. assertTrue(task.getTaskInfo().isResizeable); diff --git a/telephony/java/android/telephony/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java index 14915109999c..19574fd95ac7 100644 --- a/telephony/java/android/telephony/CarrierConfigManager.java +++ b/telephony/java/android/telephony/CarrierConfigManager.java @@ -11493,17 +11493,17 @@ public class CarrierConfigManager { + "target=GERAN|UTRAN|EUTRAN|NGRAN|IWLAN, type=allowed"}); PersistableBundle auto_data_switch_rat_signal_score_string_bundle = new PersistableBundle(); auto_data_switch_rat_signal_score_string_bundle.putIntArray( - "NR_SA_MMWAVE", new int[]{10000, 13227, 16000, 18488, 20017}); + "NR_SA_MMWAVE", new int[]{6300, 10227, 16000, 18488, 19017}); auto_data_switch_rat_signal_score_string_bundle.putIntArray( - "NR_NSA_MMWAVE", new int[]{8000, 10227, 12488, 15017, 15278}); + "NR_NSA_MMWAVE", new int[]{5700, 9227, 12488, 13517, 15978}); auto_data_switch_rat_signal_score_string_bundle.putIntArray( "LTE", new int[]{3731, 5965, 8618, 11179, 13384}); auto_data_switch_rat_signal_score_string_bundle.putIntArray( - "LTE_CA", new int[]{3831, 6065, 8718, 11379, 13484}); + "LTE_CA", new int[]{3831, 6065, 8718, 11379, 14484}); auto_data_switch_rat_signal_score_string_bundle.putIntArray( - "NR_SA", new int[]{5288, 6795, 6955, 7562, 9713}); + "NR_SA", new int[]{2288, 6795, 6955, 7562, 15484}); auto_data_switch_rat_signal_score_string_bundle.putIntArray( - "NR_NSA", new int[]{5463, 6827, 8029, 9007, 9428}); + "NR_NSA", new int[]{2463, 6827, 8029, 9007, 15884}); auto_data_switch_rat_signal_score_string_bundle.putIntArray( "UMTS", new int[]{100, 169, 183, 192, 300}); auto_data_switch_rat_signal_score_string_bundle.putIntArray( diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl index 0dd0a42d44b4..b8aa9e8646bd 100644 --- a/telephony/java/com/android/internal/telephony/ITelephony.aidl +++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl @@ -3238,6 +3238,15 @@ interface ITelephony { boolean setOemEnabledSatelliteProvisionStatus(in boolean reset, in boolean isProvisioned); /** + * This API is used by CTS to override the version of the config data + * + * @param reset Whether to restore the original version + * @param version The overriding version + * @return {@code true} if successful, {@code false} otherwise + */ + boolean overrideConfigDataVersion(in boolean reset, in int version); + + /** * Test method to confirm the file contents are not altered. */ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(" diff --git a/tests/FsVerityTest/FsVerityTestApp/src/com/android/fsverity/Helper.java b/tests/FsVerityTest/FsVerityTestApp/src/com/android/fsverity/Helper.java index c52be7c2b0c6..8e012598c9eb 100644 --- a/tests/FsVerityTest/FsVerityTestApp/src/com/android/fsverity/Helper.java +++ b/tests/FsVerityTest/FsVerityTestApp/src/com/android/fsverity/Helper.java @@ -21,7 +21,10 @@ import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertThrows; import android.content.Context; +import android.os.Bundle; import android.security.FileIntegrityManager; +import android.system.Os; +import android.system.OsConstants; import android.util.Log; import androidx.test.core.app.ApplicationProvider; @@ -35,8 +38,9 @@ import org.junit.Test; import java.io.FileOutputStream; import java.io.IOException; import java.io.RandomAccessFile; -import java.util.ArrayList; import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; /** * Test helper that works with the host-side test to set up a test file, and to verify fs-verity @@ -47,8 +51,6 @@ public class Helper { private static final String FILENAME = "test.file"; - private static final long BLOCK_SIZE = 4096; - @Rule public final AdoptShellPermissionsRule mAdoptShellPermissionsRule = new AdoptShellPermissionsRule( @@ -58,7 +60,7 @@ public class Helper { @Test public void prepareTest() throws Exception { Context context = ApplicationProvider.getApplicationContext(); - android.os.Bundle testArgs = InstrumentationRegistry.getArguments(); + Bundle testArgs = InstrumentationRegistry.getArguments(); String basename = testArgs.getString("basename"); context.deleteFile(basename); @@ -84,31 +86,52 @@ public class Helper { fim.setupFsVerity(context.getFileStreamPath(basename)); } + private static long getPageSize() { + String arch = System.getProperty("os.arch"); + Log.d(TAG, "os.arch=" + arch); + if ("x86_64".equals(arch)) { + // Override the fake 16K page size from cf_x86_64_pgagnostic. The real page size on + // x86_64 is always 4K. This test needs the real page size because it is testing I/O + // error reporting behavior that is dependent on the real page size. + return 4096; + } + return Os.sysconf(OsConstants._SC_PAGE_SIZE); + } + @Test public void verifyFileRead() throws Exception { Context context = ApplicationProvider.getApplicationContext(); - // Collect indices that the backing blocks are supposed to be corrupted. - android.os.Bundle testArgs = InstrumentationRegistry.getArguments(); + Bundle testArgs = InstrumentationRegistry.getArguments(); assertThat(testArgs).isNotNull(); String filePath = testArgs.getString("filePath"); - String csv = testArgs.getString("brokenBlockIndicesCsv"); - Log.d(TAG, "brokenBlockIndicesCsv: " + csv); - String[] strings = csv.split(","); - var corrupted = new ArrayList(strings.length); - for (int i = 0; i < strings.length; i++) { - corrupted.add(Integer.parseInt(strings[i])); + String csv = testArgs.getString("brokenByteIndicesCsv"); + Log.d(TAG, "brokenByteIndicesCsv: " + csv); + + // Build the set of pages that contain a corrupted byte. + final long pageSize = getPageSize(); + Set<Long> corruptedPageIndices = new HashSet(); + for (String s : csv.split(",")) { + long byteIndex = Long.parseLong(s); + long pageIndex = byteIndex / pageSize; + corruptedPageIndices.add(pageIndex); } - - // Expect the read to succeed or fail per the prior. - try (var file = new RandomAccessFile(filePath, "r")) { - long total_blocks = (file.length() + BLOCK_SIZE - 1) / BLOCK_SIZE; - for (int i = 0; i < (int) total_blocks; i++) { - file.seek(i * BLOCK_SIZE); - if (corrupted.contains(i)) { - Log.d(TAG, "Expecting read at block #" + i + " to fail"); + Log.d(TAG, "corruptedPageIndices=" + corruptedPageIndices); + + // Read bytes from the file and verify the expected result based on the containing page. + // (The kernel reports fs-verity errors at page granularity.) + final long stride = 1024; + // Using a stride that is a divisor of the page size ensures that the last page is tested. + assertThat(pageSize % stride).isEqualTo(0); + try (RandomAccessFile file = new RandomAccessFile(filePath, "r")) { + for (long byteIndex = 0; byteIndex < file.length(); byteIndex += stride) { + file.seek(byteIndex); + long pageIndex = byteIndex / pageSize; + if (corruptedPageIndices.contains(pageIndex)) { + Log.d(TAG, "Expecting read at byte #" + byteIndex + " to fail"); assertThrows(IOException.class, () -> file.read()); } else { + Log.d(TAG, "Expecting read at byte #" + byteIndex + " to succeed"); assertThat(file.readByte()).isEqualTo('1'); } } diff --git a/tests/FsVerityTest/src/com/android/fsverity/FsVerityHostTest.java b/tests/FsVerityTest/src/com/android/fsverity/FsVerityHostTest.java index b1d6e96dca9b..88d9e9e08dce 100644 --- a/tests/FsVerityTest/src/com/android/fsverity/FsVerityHostTest.java +++ b/tests/FsVerityTest/src/com/android/fsverity/FsVerityHostTest.java @@ -33,13 +33,12 @@ import org.junit.runner.RunWith; /** * This test verifies fs-verity works end-to-end. There is a corresponding helper app. * - * <p>The helper app uses a FileIntegrityManager API to enable fs-verity to a file. The host test - * here * tampers with the file's backing storage, then tells the helper app to read and expect + * <p>The helper app uses a FileIntegrityManager API to enable fs-verity on a file. The host test + * here tampers with the file's backing storage, then tells the helper app to read and expect * success/failure on read. * - * <p>In order to make sure a block of the file is readable only if the underlying block on disk - * stay intact, the test needs to bypass the filesystem and tampers with the corresponding physical - * address against the block device. + * <p>Since the filesystem by design provides no way to corrupt fs-verity files itself, the test + * needs to bypass the filesystem and write directly to the block device to corrupt the files. */ @RootPermissionTest @RunWith(DeviceJUnit4ClassRunner.class) @@ -57,7 +56,7 @@ public class FsVerityHostTest extends BaseHostJUnit4Test { BlockDeviceWriter.damageFileAgainstBlockDevice(device, getTargetFilePath(), 8192); BlockDeviceWriter.dropCaches(device); - verifyRead(getTargetFilePath(), "0,2"); + verifyRead(getTargetFilePath(), "0,8192"); } @Test @@ -70,7 +69,7 @@ public class FsVerityHostTest extends BaseHostJUnit4Test { BlockDeviceWriter.damageFileAgainstBlockDevice(device, getTargetFilePath(), 128 * 4096 + 1); BlockDeviceWriter.dropCaches(device); - verifyRead(getTargetFilePath(), "1,100,128"); + verifyRead(getTargetFilePath(), "4096,409600,524289"); } private String getTargetFilePath() throws DeviceNotAvailableException { @@ -87,11 +86,17 @@ public class FsVerityHostTest extends BaseHostJUnit4Test { assertThat(runDeviceTests(options)).isTrue(); } + /** + * Verifies the read success/failure expectation given the corrupted byte indices in the file. + * + * @param path the remote file path to read. + * @param indicesCsv a comma-separated list of indices of bytes that were corrupted. + */ private void verifyRead(String path, String indicesCsv) throws Exception { DeviceTestRunOptions options = new DeviceTestRunOptions(TARGET_PACKAGE); options.setTestClassName(TARGET_PACKAGE + ".Helper"); options.setTestMethodName("verifyFileRead"); - options.addInstrumentationArg("brokenBlockIndicesCsv", indicesCsv); + options.addInstrumentationArg("brokenByteIndicesCsv", indicesCsv); options.addInstrumentationArg("filePath", getTargetFilePath()); assertThat(runDeviceTests(options)).isTrue(); } diff --git a/tests/Input/src/android/hardware/input/KeyGestureEventHandlerTest.kt b/tests/Input/src/android/hardware/input/KeyGestureEventHandlerTest.kt index 794fd0255726..c62bd0b72584 100644 --- a/tests/Input/src/android/hardware/input/KeyGestureEventHandlerTest.kt +++ b/tests/Input/src/android/hardware/input/KeyGestureEventHandlerTest.kt @@ -18,12 +18,10 @@ package android.hardware.input import android.content.Context import android.content.ContextWrapper -import android.os.IBinder import android.platform.test.annotations.Presubmit import android.platform.test.flag.junit.SetFlagsRule import android.view.KeyEvent import androidx.test.core.app.ApplicationProvider -import com.android.server.testutils.any import com.android.test.input.MockInputManagerRule import org.junit.Before import org.junit.Rule @@ -37,6 +35,7 @@ import kotlin.test.assertEquals import kotlin.test.assertNotNull import kotlin.test.assertNull import kotlin.test.fail +import org.junit.Assert.assertThrows /** * Tests for [InputManager.KeyGestureEventHandler]. @@ -82,7 +81,7 @@ class KeyGestureEventHandlerTest { // Handle key gesture handler registration. doAnswer { - val listener = it.getArgument(0) as IKeyGestureHandler + val listener = it.getArgument(1) as IKeyGestureHandler if (registeredListener != null && registeredListener!!.asBinder() != listener.asBinder()) { // There can only be one registered key gesture handler per process. @@ -90,7 +89,7 @@ class KeyGestureEventHandlerTest { } registeredListener = listener null - }.`when`(inputManagerRule.mock).registerKeyGestureHandler(any()) + }.`when`(inputManagerRule.mock).registerKeyGestureHandler(Mockito.any(), Mockito.any()) // Handle key gesture handler being unregistered. doAnswer { @@ -101,7 +100,7 @@ class KeyGestureEventHandlerTest { } registeredListener = null null - }.`when`(inputManagerRule.mock).unregisterKeyGestureHandler(any()) + }.`when`(inputManagerRule.mock).unregisterKeyGestureHandler(Mockito.any()) } private fun handleKeyGestureEvent(event: KeyGestureEvent) { @@ -121,11 +120,12 @@ class KeyGestureEventHandlerTest { var callbackCount = 0 // Add a key gesture event listener - inputManager.registerKeyGestureEventHandler(KeyGestureHandler { event, _ -> + inputManager.registerKeyGestureEventHandler( + listOf(KeyGestureEvent.KEY_GESTURE_TYPE_HOME) + ) { event, _ -> assertEquals(HOME_GESTURE_EVENT, event) callbackCount++ - true - }) + } // Request handling for key gesture event will notify the handler. handleKeyGestureEvent(HOME_GESTURE_EVENT) @@ -135,29 +135,41 @@ class KeyGestureEventHandlerTest { @Test fun testAddingHandlersRegistersInternalCallbackHandler() { // Set up two callbacks. - val callback1 = KeyGestureHandler { _, _ -> false } - val callback2 = KeyGestureHandler { _, _ -> false } + val callback1 = InputManager.KeyGestureEventHandler { _, _ -> } + val callback2 = InputManager.KeyGestureEventHandler { _, _ -> } assertNull(registeredListener) // Adding the handler should register the callback with InputManagerService. - inputManager.registerKeyGestureEventHandler(callback1) + inputManager.registerKeyGestureEventHandler( + listOf(KeyGestureEvent.KEY_GESTURE_TYPE_HOME), + callback1 + ) assertNotNull(registeredListener) // Adding another handler should not register new internal listener. val currListener = registeredListener - inputManager.registerKeyGestureEventHandler(callback2) + inputManager.registerKeyGestureEventHandler( + listOf(KeyGestureEvent.KEY_GESTURE_TYPE_BACK), + callback2 + ) assertEquals(currListener, registeredListener) } @Test fun testRemovingHandlersUnregistersInternalCallbackHandler() { // Set up two callbacks. - val callback1 = KeyGestureHandler { _, _ -> false } - val callback2 = KeyGestureHandler { _, _ -> false } + val callback1 = InputManager.KeyGestureEventHandler { _, _ -> } + val callback2 = InputManager.KeyGestureEventHandler { _, _ -> } - inputManager.registerKeyGestureEventHandler(callback1) - inputManager.registerKeyGestureEventHandler(callback2) + inputManager.registerKeyGestureEventHandler( + listOf(KeyGestureEvent.KEY_GESTURE_TYPE_HOME), + callback1 + ) + inputManager.registerKeyGestureEventHandler( + listOf(KeyGestureEvent.KEY_GESTURE_TYPE_BACK), + callback2 + ) // Only removing all handlers should remove the internal callback inputManager.unregisterKeyGestureEventHandler(callback1) @@ -172,47 +184,74 @@ class KeyGestureEventHandlerTest { var callbackCount1 = 0 var callbackCount2 = 0 // Handler 1 captures all home gestures - val callback1 = KeyGestureHandler { event, _ -> + val callback1 = InputManager.KeyGestureEventHandler { event, _ -> callbackCount1++ - event.keyGestureType == KeyGestureEvent.KEY_GESTURE_TYPE_HOME + assertEquals(KeyGestureEvent.KEY_GESTURE_TYPE_HOME, event.keyGestureType) } - // Handler 2 captures all gestures - val callback2 = KeyGestureHandler { _, _ -> + // Handler 2 captures all back gestures + val callback2 = InputManager.KeyGestureEventHandler { event, _ -> callbackCount2++ - true + assertEquals(KeyGestureEvent.KEY_GESTURE_TYPE_BACK, event.keyGestureType) } // Add both key gesture event handlers - inputManager.registerKeyGestureEventHandler(callback1) - inputManager.registerKeyGestureEventHandler(callback2) + inputManager.registerKeyGestureEventHandler( + listOf(KeyGestureEvent.KEY_GESTURE_TYPE_HOME), + callback1 + ) + inputManager.registerKeyGestureEventHandler( + listOf(KeyGestureEvent.KEY_GESTURE_TYPE_BACK), + callback2 + ) - // Request handling for key gesture event, should notify callbacks in order. So, only the - // first handler should receive a callback since it captures the event. + // Request handling for home key gesture event, should notify only callback1 handleKeyGestureEvent(HOME_GESTURE_EVENT) assertEquals(1, callbackCount1) assertEquals(0, callbackCount2) - // Second handler should receive the event since the first handler doesn't capture the event + // Request handling for back key gesture event, should notify only callback2 handleKeyGestureEvent(BACK_GESTURE_EVENT) - assertEquals(2, callbackCount1) + assertEquals(1, callbackCount1) assertEquals(1, callbackCount2) inputManager.unregisterKeyGestureEventHandler(callback1) - // Request handling for key gesture event, should still trigger callback2 but not callback1. + + // Request handling for home key gesture event, should not trigger callback2 handleKeyGestureEvent(HOME_GESTURE_EVENT) - assertEquals(2, callbackCount1) - assertEquals(2, callbackCount2) + assertEquals(1, callbackCount1) + assertEquals(1, callbackCount2) + } + + @Test + fun testUnableToRegisterSameHandlerTwice() { + val handler = InputManager.KeyGestureEventHandler { _, _ -> } + + inputManager.registerKeyGestureEventHandler( + listOf(KeyGestureEvent.KEY_GESTURE_TYPE_HOME), + handler + ) + + assertThrows(IllegalArgumentException::class.java) { + inputManager.registerKeyGestureEventHandler( + listOf(KeyGestureEvent.KEY_GESTURE_TYPE_BACK), handler + ) + } } - inner class KeyGestureHandler( - private var handler: (event: KeyGestureEvent, token: IBinder?) -> Boolean - ) : InputManager.KeyGestureEventHandler { + @Test + fun testUnableToRegisterSameGestureTwice() { + val handler1 = InputManager.KeyGestureEventHandler { _, _ -> } + val handler2 = InputManager.KeyGestureEventHandler { _, _ -> } + + inputManager.registerKeyGestureEventHandler( + listOf(KeyGestureEvent.KEY_GESTURE_TYPE_HOME), + handler1 + ) - override fun handleKeyGestureEvent( - event: KeyGestureEvent, - focusedToken: IBinder? - ): Boolean { - return handler(event, focusedToken) + assertThrows(IllegalArgumentException::class.java) { + inputManager.registerKeyGestureEventHandler( + listOf(KeyGestureEvent.KEY_GESTURE_TYPE_HOME), handler2 + ) } } } diff --git a/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt index 4f1fb6487b19..163dda84a71c 100644 --- a/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt +++ b/tests/Input/src/com/android/server/input/KeyGestureControllerTests.kt @@ -63,6 +63,7 @@ import org.junit.After import org.junit.Assert.assertArrayEquals import org.junit.Assert.assertEquals import org.junit.Assert.assertNull +import org.junit.Assert.assertThrows import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Rule @@ -107,7 +108,10 @@ class KeyGestureControllerTests { const val SETTINGS_KEY_BEHAVIOR_SETTINGS_ACTIVITY = 0 const val SETTINGS_KEY_BEHAVIOR_NOTIFICATION_PANEL = 1 const val SETTINGS_KEY_BEHAVIOR_NOTHING = 2 + const val SYSTEM_PID = 0 const val TEST_PID = 10 + const val RANDOM_PID1 = 11 + const val RANDOM_PID2 = 12 } @JvmField @@ -170,6 +174,7 @@ class KeyGestureControllerTests { return atomicFile } }) + startNewInputGlobalTestSession() } @After @@ -199,17 +204,22 @@ class KeyGestureControllerTests { val correctIm = context.getSystemService(InputManager::class.java)!! val virtualDevice = correctIm.getInputDevice(KeyCharacterMap.VIRTUAL_KEYBOARD)!! val kcm = virtualDevice.keyCharacterMap!! - inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManager) - val inputManager = InputManager(context) - Mockito.`when`(context.getSystemService(Mockito.eq(Context.INPUT_SERVICE))) - .thenReturn(inputManager) - val keyboardDevice = InputDevice.Builder().setId(DEVICE_ID).build() Mockito.`when`(iInputManager.inputDeviceIds).thenReturn(intArrayOf(DEVICE_ID)) Mockito.`when`(iInputManager.getInputDevice(DEVICE_ID)).thenReturn(keyboardDevice) ExtendedMockito.`when`(KeyCharacterMap.load(Mockito.anyInt())).thenReturn(kcm) } + private fun startNewInputGlobalTestSession() { + if (this::inputManagerGlobalSession.isInitialized) { + inputManagerGlobalSession.close() + } + inputManagerGlobalSession = InputManagerGlobal.createTestSession(iInputManager) + val inputManager = InputManager(context) + Mockito.`when`(context.getSystemService(Mockito.eq(Context.INPUT_SERVICE))) + .thenReturn(inputManager) + } + private fun setupKeyGestureController() { keyGestureController = KeyGestureController( @@ -225,13 +235,14 @@ class KeyGestureControllerTests { return accessibilityShortcutController } }) - Mockito.`when`(iInputManager.registerKeyGestureHandler(Mockito.any())) + Mockito.`when`(iInputManager.registerKeyGestureHandler(Mockito.any(), Mockito.any())) .thenAnswer { val args = it.arguments if (args[0] != null) { keyGestureController.registerKeyGestureHandler( - args[0] as IKeyGestureHandler, - TEST_PID + args[0] as IntArray, + args[1] as IKeyGestureHandler, + SYSTEM_PID ) } } @@ -285,59 +296,6 @@ class KeyGestureControllerTests { ) } - @Test - fun testKeyGestureEvent_multipleGestureHandlers() { - setupKeyGestureController() - - // Set up two callbacks. - var callbackCount1 = 0 - var callbackCount2 = 0 - var selfCallback = 0 - val externalHandler1 = KeyGestureHandler { _, _ -> - callbackCount1++ - true - } - val externalHandler2 = KeyGestureHandler { _, _ -> - callbackCount2++ - true - } - val selfHandler = KeyGestureHandler { _, _ -> - selfCallback++ - false - } - - // Register key gesture handler: External process (last in priority) - keyGestureController.registerKeyGestureHandler(externalHandler1, currentPid + 1) - - // Register key gesture handler: External process (second in priority) - keyGestureController.registerKeyGestureHandler(externalHandler2, currentPid - 1) - - // Register key gesture handler: Self process (first in priority) - keyGestureController.registerKeyGestureHandler(selfHandler, currentPid) - - keyGestureController.handleKeyGesture(/* deviceId = */ 0, intArrayOf(KeyEvent.KEYCODE_HOME), - /* modifierState = */ 0, KeyGestureEvent.KEY_GESTURE_TYPE_HOME, - KeyGestureEvent.ACTION_GESTURE_COMPLETE, /* displayId */ 0, - /* focusedToken = */ null, /* flags = */ 0, /* appLaunchData = */null - ) - - assertEquals( - "Self handler should get callbacks first", - 1, - selfCallback - ) - assertEquals( - "Higher priority handler should get callbacks first", - 1, - callbackCount2 - ) - assertEquals( - "Lower priority handler should not get callbacks if already handled", - 0, - callbackCount1 - ) - } - class TestData( val name: String, val keys: IntArray, @@ -789,10 +747,6 @@ class KeyGestureControllerTests { ) fun testCustomKeyGesturesNotAllowedForSystemGestures(test: TestData) { setupKeyGestureController() - // Need to re-init so that bookmarks are correctly blocklisted - Mockito.`when`(iInputManager.getAppLaunchBookmarks()) - .thenReturn(keyGestureController.appLaunchBookmarks) - keyGestureController.systemRunning() val builder = InputGestureData.Builder() .setKeyGestureType(test.expectedKeyGestureType) @@ -1163,9 +1117,6 @@ class KeyGestureControllerTests { KeyEvent.KEYCODE_FULLSCREEN ) - val handler = KeyGestureHandler { _, _ -> false } - keyGestureController.registerKeyGestureHandler(handler, 0) - for (key in testKeys) { sendKeys(intArrayOf(key), assertNotSentToApps = true) } @@ -1179,6 +1130,7 @@ class KeyGestureControllerTests { testKeyGestureNotProduced( "SEARCH -> Default Search", intArrayOf(KeyEvent.KEYCODE_SEARCH), + intArrayOf(KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SEARCH) ) } @@ -1207,6 +1159,10 @@ class KeyGestureControllerTests { testKeyGestureNotProduced( "SETTINGS -> Do Nothing", intArrayOf(KeyEvent.KEYCODE_SETTINGS), + intArrayOf( + KeyGestureEvent.KEY_GESTURE_TYPE_LAUNCH_SEARCH, + KeyGestureEvent.KEY_GESTURE_TYPE_TOGGLE_NOTIFICATION_PANEL + ) ) } @@ -1290,28 +1246,6 @@ class KeyGestureControllerTests { ) ), TestData( - "VOLUME_DOWN + VOLUME_UP -> Accessibility Chord", - intArrayOf(KeyEvent.KEYCODE_VOLUME_DOWN, KeyEvent.KEYCODE_VOLUME_UP), - KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT_CHORD, - intArrayOf(KeyEvent.KEYCODE_VOLUME_DOWN, KeyEvent.KEYCODE_VOLUME_UP), - 0, - intArrayOf( - KeyGestureEvent.ACTION_GESTURE_START, - KeyGestureEvent.ACTION_GESTURE_COMPLETE - ) - ), - TestData( - "BACK + DPAD_DOWN -> Accessibility Chord(for TV)", - intArrayOf(KeyEvent.KEYCODE_BACK, KeyEvent.KEYCODE_DPAD_DOWN), - KeyGestureEvent.KEY_GESTURE_TYPE_ACCESSIBILITY_SHORTCUT_CHORD, - intArrayOf(KeyEvent.KEYCODE_BACK, KeyEvent.KEYCODE_DPAD_DOWN), - 0, - intArrayOf( - KeyGestureEvent.ACTION_GESTURE_START, - KeyGestureEvent.ACTION_GESTURE_COMPLETE - ) - ), - TestData( "BACK + DPAD_CENTER -> TV Trigger Bug Report", intArrayOf(KeyEvent.KEYCODE_BACK, KeyEvent.KEYCODE_DPAD_CENTER), KeyGestureEvent.KEY_GESTURE_TYPE_TV_TRIGGER_BUG_REPORT, @@ -1428,9 +1362,11 @@ class KeyGestureControllerTests { testLooper.dispatchAll() // Reinitialize the gesture controller simulating a login/logout for the user. + startNewInputGlobalTestSession() setupKeyGestureController() keyGestureController.setCurrentUserId(userId) testLooper.dispatchAll() + val savedInputGestures = keyGestureController.getCustomInputGestures(userId, null) assertEquals( "Test: $test doesn't produce correct number of saved input gestures", @@ -1469,6 +1405,7 @@ class KeyGestureControllerTests { // Delete the old data and reinitialize the controller simulating a "fresh" install. tempFile.delete() + startNewInputGlobalTestSession() setupKeyGestureController() keyGestureController.setCurrentUserId(userId) testLooper.dispatchAll() @@ -1541,9 +1478,12 @@ class KeyGestureControllerTests { val handledEvents = mutableListOf<KeyGestureEvent>() val handler = KeyGestureHandler { event, _ -> handledEvents.add(KeyGestureEvent(event)) - true } - keyGestureController.registerKeyGestureHandler(handler, 0) + keyGestureController.registerKeyGestureHandler( + intArrayOf(test.expectedKeyGestureType), + handler, + TEST_PID + ) handledEvents.clear() keyGestureController.handleTouchpadGesture(test.touchpadGestureType) @@ -1570,7 +1510,7 @@ class KeyGestureControllerTests { event.appLaunchData ) - keyGestureController.unregisterKeyGestureHandler(handler, 0) + keyGestureController.unregisterKeyGestureHandler(handler, TEST_PID) } @Test @@ -1591,9 +1531,11 @@ class KeyGestureControllerTests { testLooper.dispatchAll() // Reinitialize the gesture controller simulating a login/logout for the user. + startNewInputGlobalTestSession() setupKeyGestureController() keyGestureController.setCurrentUserId(userId) testLooper.dispatchAll() + val savedInputGestures = keyGestureController.getCustomInputGestures(userId, null) assertEquals( "Test: $test doesn't produce correct number of saved input gestures", @@ -1627,6 +1569,7 @@ class KeyGestureControllerTests { // Delete the old data and reinitialize the controller simulating a "fresh" install. tempFile.delete() + startNewInputGlobalTestSession() setupKeyGestureController() keyGestureController.setCurrentUserId(userId) testLooper.dispatchAll() @@ -1699,13 +1642,97 @@ class KeyGestureControllerTests { Mockito.verify(accessibilityShortcutController, never()).performAccessibilityShortcut() } + @Test + fun testUnableToRegisterFromSamePidTwice() { + setupKeyGestureController() + + val handler1 = KeyGestureHandler { _, _ -> } + val handler2 = KeyGestureHandler { _, _ -> } + keyGestureController.registerKeyGestureHandler( + intArrayOf(KeyGestureEvent.KEY_GESTURE_TYPE_HOME), + handler1, + RANDOM_PID1 + ) + + assertThrows(IllegalStateException::class.java) { + keyGestureController.registerKeyGestureHandler( + intArrayOf(KeyGestureEvent.KEY_GESTURE_TYPE_BACK), + handler2, + RANDOM_PID1 + ) + } + } + + @Test + fun testUnableToRegisterSameGestureTwice() { + setupKeyGestureController() + + val handler1 = KeyGestureHandler { _, _ -> } + val handler2 = KeyGestureHandler { _, _ -> } + keyGestureController.registerKeyGestureHandler( + intArrayOf(KeyGestureEvent.KEY_GESTURE_TYPE_HOME), + handler1, + RANDOM_PID1 + ) + + assertThrows(IllegalArgumentException::class.java) { + keyGestureController.registerKeyGestureHandler( + intArrayOf(KeyGestureEvent.KEY_GESTURE_TYPE_HOME), + handler2, + RANDOM_PID2 + ) + } + } + + @Test + fun testUnableToRegisterEmptyListOfGestures() { + setupKeyGestureController() + + val handler = KeyGestureHandler { _, _ -> } + + assertThrows(IllegalArgumentException::class.java) { + keyGestureController.registerKeyGestureHandler( + intArrayOf(), + handler, + RANDOM_PID1 + ) + } + } + + @Test + fun testGestureHandlerNotCalledOnceUnregistered() { + setupKeyGestureController() + + var callbackCount = 0 + val handler1 = KeyGestureHandler { _, _ -> callbackCount++ } + keyGestureController.registerKeyGestureHandler( + intArrayOf(KeyGestureEvent.KEY_GESTURE_TYPE_RECENT_APPS), + handler1, + TEST_PID + ) + sendKeys(intArrayOf(KeyEvent.KEYCODE_RECENT_APPS)) + assertEquals(1, callbackCount) + + keyGestureController.unregisterKeyGestureHandler( + handler1, + TEST_PID + ) + + // Callback should not be sent after unregister + sendKeys(intArrayOf(KeyEvent.KEYCODE_RECENT_APPS)) + assertEquals(1, callbackCount) + } + private fun testKeyGestureInternal(test: TestData) { val handledEvents = mutableListOf<KeyGestureEvent>() val handler = KeyGestureHandler { event, _ -> handledEvents.add(KeyGestureEvent(event)) - true } - keyGestureController.registerKeyGestureHandler(handler, 0) + keyGestureController.registerKeyGestureHandler( + intArrayOf(test.expectedKeyGestureType), + handler, + TEST_PID + ) handledEvents.clear() sendKeys(test.keys) @@ -1744,16 +1771,19 @@ class KeyGestureControllerTests { ) } - keyGestureController.unregisterKeyGestureHandler(handler, 0) + keyGestureController.unregisterKeyGestureHandler(handler, TEST_PID) } - private fun testKeyGestureNotProduced(testName: String, testKeys: IntArray) { + private fun testKeyGestureNotProduced( + testName: String, + testKeys: IntArray, + possibleGestures: IntArray + ) { var handledEvents = mutableListOf<KeyGestureEvent>() val handler = KeyGestureHandler { event, _ -> handledEvents.add(KeyGestureEvent(event)) - true } - keyGestureController.registerKeyGestureHandler(handler, 0) + keyGestureController.registerKeyGestureHandler(possibleGestures, handler, TEST_PID) handledEvents.clear() sendKeys(testKeys) @@ -1823,10 +1853,10 @@ class KeyGestureControllerTests { } inner class KeyGestureHandler( - private var handler: (event: AidlKeyGestureEvent, token: IBinder?) -> Boolean + private var handler: (event: AidlKeyGestureEvent, token: IBinder?) -> Unit ) : IKeyGestureHandler.Stub() { - override fun handleKeyGesture(event: AidlKeyGestureEvent, token: IBinder?): Boolean { - return handler(event, token) + override fun handleKeyGesture(event: AidlKeyGestureEvent, token: IBinder?) { + handler(event, token) } } } diff --git a/tests/Input/src/com/android/test/input/AnrTest.kt b/tests/Input/src/com/android/test/input/AnrTest.kt index f8cb86b7b1fe..3ad3763a5d20 100644 --- a/tests/Input/src/com/android/test/input/AnrTest.kt +++ b/tests/Input/src/com/android/test/input/AnrTest.kt @@ -16,6 +16,7 @@ package com.android.test.input import android.app.ActivityManager +import android.app.ActivityTaskManager import android.app.ApplicationExitInfo import android.app.Instrumentation import android.content.Intent @@ -28,6 +29,7 @@ import android.os.SystemClock import android.server.wm.CtsWindowInfoUtils.getWindowCenter import android.server.wm.CtsWindowInfoUtils.waitForWindowOnTop import android.testing.PollingCheck +import android.util.Log import android.view.InputEvent import android.view.MotionEvent import android.view.MotionEvent.ACTION_DOWN @@ -46,21 +48,19 @@ import com.android.cts.input.inputeventmatchers.withMotionAction import java.time.Duration import java.util.concurrent.LinkedBlockingQueue import java.util.function.Supplier -import org.junit.After import org.junit.Assert.assertEquals import org.junit.Assert.assertTrue import org.junit.Assert.fail -import org.junit.Before +import org.junit.Assume.assumeTrue import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith /** - * Click on the center of the window identified by the provided window token. - * The click is performed using "UinputTouchScreen" device. - * If the touchscreen device is closed too soon, it may cause the click to be dropped. Therefore, - * the provided runnable can ensure that the click is delivered before the device is closed, thus - * avoiding this race. + * Click on the center of the window identified by the provided window token. The click is performed + * using "UinputTouchScreen" device. If the touchscreen device is closed too soon, it may cause the + * click to be dropped. Therefore, the provided runnable can ensure that the click is delivered + * before the device is closed, thus avoiding this race. */ private fun clickOnWindow( token: IBinder, @@ -104,6 +104,10 @@ class AnrTest { private val remoteInputEvents = LinkedBlockingQueue<InputEvent>() private val verifier = BlockingQueueEventVerifier(remoteInputEvents) + // Some devices don't support ANR error dialogs, such as cars, TVs, etc. + private val anrDialogsAreSupported = + ActivityTaskManager.currentUiModeSupportsErrorDialogs(instrumentation.targetContext) + val binder = object : IAnrTestService.Stub() { override fun provideActivityInfo(token: IBinder, displayId: Int, pid: Int) { @@ -121,34 +125,37 @@ class AnrTest { @get:Rule val debugInputRule = DebugInputRule() - @Before - fun setUp() { - startUnresponsiveActivity() - PACKAGE_NAME = UnresponsiveGestureMonitorActivity::class.java.getPackage()!!.getName() - } - - @After fun tearDown() {} - @Test @DebugInputRule.DebugInput(bug = 339924248) fun testGestureMonitorAnr_Close() { + startUnresponsiveActivity() + val timestamp = System.currentTimeMillis() triggerAnr() - clickCloseAppOnAnrDialog() + if (anrDialogsAreSupported) { + clickCloseAppOnAnrDialog() + } else { + Log.i(TAG, "The device does not support ANR dialogs, skipping check for ANR window") + // We still want to wait for the app to get killed by the ActivityManager + } + waitForNewExitReasonAfter(timestamp) } @Test @DebugInputRule.DebugInput(bug = 339924248) fun testGestureMonitorAnr_Wait() { + assumeTrue(anrDialogsAreSupported) + startUnresponsiveActivity() triggerAnr() clickWaitOnAnrDialog() SystemClock.sleep(500) // Wait at least 500ms after tapping on wait // ANR dialog should reappear after a delay - find the close button on it to verify + val timestamp = System.currentTimeMillis() clickCloseAppOnAnrDialog() + waitForNewExitReasonAfter(timestamp) } private fun clickCloseAppOnAnrDialog() { // Find anr dialog and kill app - val timestamp = System.currentTimeMillis() val uiDevice: UiDevice = UiDevice.getInstance(instrumentation) val closeAppButton: UiObject2? = uiDevice.wait(Until.findObject(By.res("android:id/aerr_close")), 20000) @@ -157,14 +164,6 @@ class AnrTest { return } closeAppButton.click() - /** - * We must wait for the app to be fully closed before exiting this test. This is because - * another test may again invoke 'am start' for the same activity. If the 1st process that - * got ANRd isn't killed by the time second 'am start' runs, the killing logic will apply to - * the newly launched 'am start' instance, and the second test will fail because the - * unresponsive activity will never be launched. - */ - waitForNewExitReasonAfter(timestamp) } private fun clickWaitOnAnrDialog() { @@ -180,16 +179,27 @@ class AnrTest { } private fun getExitReasons(): List<ApplicationExitInfo> { + val packageName = UnresponsiveGestureMonitorActivity::class.java.getPackage()!!.name lateinit var infos: List<ApplicationExitInfo> instrumentation.runOnMainSync { val am = instrumentation.getContext().getSystemService(ActivityManager::class.java)!! - infos = am.getHistoricalProcessExitReasons(PACKAGE_NAME, remotePid!!, NO_MAX) + infos = am.getHistoricalProcessExitReasons(packageName, remotePid!!, NO_MAX) } return infos } + /** + * We must wait for the app to be fully closed before exiting this test. This is because another + * test may again invoke 'am start' for the same activity. If the 1st process that got ANRd + * isn't killed by the time second 'am start' runs, the killing logic will apply to the newly + * launched 'am start' instance, and the second test will fail because the unresponsive activity + * will never be launched. + * + * Also, we must ensure that we wait until it's killed, so that the next test can launch this + * activity again. + */ private fun waitForNewExitReasonAfter(timestamp: Long) { - PollingCheck.waitFor { + PollingCheck.waitFor(Duration.ofSeconds(20).toMillis() * Build.HW_TIMEOUT_MULTIPLIER) { val reasons = getExitReasons() !reasons.isEmpty() && reasons[0].timestamp >= timestamp } @@ -199,16 +209,15 @@ class AnrTest { } private fun triggerAnr() { - clickOnWindow( - remoteWindowToken!!, - remoteDisplayId!!, - instrumentation, - ) { verifier.assertReceivedMotion(withMotionAction(ACTION_DOWN)) } + clickOnWindow(remoteWindowToken!!, remoteDisplayId!!, instrumentation) { + verifier.assertReceivedMotion(withMotionAction(ACTION_DOWN)) + } SystemClock.sleep(DISPATCHING_TIMEOUT.toLong()) // default ANR timeout for gesture monitors } private fun startUnresponsiveActivity() { + remoteWindowToken = null val intent = Intent(instrumentation.targetContext, UnresponsiveGestureMonitorActivity::class.java) intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_NEW_DOCUMENT @@ -218,12 +227,17 @@ class AnrTest { instrumentation.targetContext.startActivity(intent) // first, wait for the token to become valid PollingCheck.check( - "UnresponsiveGestureMonitorActivity failed to call 'provideActivityInfo'", - Duration.ofSeconds(5).toMillis()) { remoteWindowToken != null } + "UnresponsiveGestureMonitorActivity failed to call 'provideActivityInfo'", + Duration.ofSeconds(10).toMillis() * Build.HW_TIMEOUT_MULTIPLIER, + ) { + remoteWindowToken != null + } // next, wait for the window of the activity to get on top // we could combine the two checks above, but the current setup makes it easier to detect // errors - assertTrue("Remote activity window did not become visible", - waitForWindowOnTop(Duration.ofSeconds(5), Supplier { remoteWindowToken })) + assertTrue( + "Remote activity window did not become visible", + waitForWindowOnTop(Duration.ofSeconds(5), Supplier { remoteWindowToken }), + ) } } diff --git a/tests/vcn/Android.bp b/tests/vcn/Android.bp index 5ad1d1dce324..205fdb119a78 100644 --- a/tests/vcn/Android.bp +++ b/tests/vcn/Android.bp @@ -17,9 +17,7 @@ android_test { // For access hidden connectivity methods in tests defaults: ["framework-connectivity-test-defaults"], - // Tethering module is released in R so this test needs to be installable - // on R - min_sdk_version: "30", + min_sdk_version: "36", srcs: [ "java/**/*.java", diff --git a/tests/vcn/AndroidManifest.xml b/tests/vcn/AndroidManifest.xml index 6e8b4ac48816..c940eeff8948 100644 --- a/tests/vcn/AndroidManifest.xml +++ b/tests/vcn/AndroidManifest.xml @@ -16,8 +16,8 @@ <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.frameworks.tests.vcn"> - <!-- TODO: b/374174952 Use 36 after Android B finalization --> - <uses-sdk android:minSdkVersion="30" android:targetSdkVersion="35" /> + + <uses-sdk android:minSdkVersion="36" android:targetSdkVersion="36" /> <application> <uses-library android:name="android.test.runner" /> diff --git a/tests/vcn/AndroidTest.xml b/tests/vcn/AndroidTest.xml index 9c8362f36cb2..ffb79adf906b 100644 --- a/tests/vcn/AndroidTest.xml +++ b/tests/vcn/AndroidTest.xml @@ -27,6 +27,8 @@ class="com.android.tradefed.testtype.suite.module.MainlineTestModuleController"> <option name="mainline-module-package-name" value="com.google.android.tethering" /> </object> + <object type="module_controller" + class="com.android.tradefed.testtype.suite.module.Sdk36ModuleController" /> <test class="com.android.tradefed.testtype.AndroidJUnitTest" > <option name="package" value="com.android.frameworks.tests.vcn" /> diff --git a/tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkTemplateTest.java b/tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkTemplateTest.java index 0fa11ae1fe7d..95205f4fd790 100644 --- a/tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkTemplateTest.java +++ b/tests/vcn/java/android/net/vcn/VcnCellUnderlyingNetworkTemplateTest.java @@ -23,12 +23,8 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.fail; -import android.os.Build; - import androidx.test.filters.SmallTest; - -import com.android.testutils.DevSdkIgnoreRule; -import com.android.testutils.DevSdkIgnoreRunner; +import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; @@ -36,10 +32,7 @@ import org.junit.runner.RunWith; import java.util.HashSet; import java.util.Set; -// TODO: b/374174952 After B finalization, use Sdk36ModuleController to ensure VCN tests only run on -// Android B/B+ -@RunWith(DevSdkIgnoreRunner.class) -@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.VANILLA_ICE_CREAM) +@RunWith(AndroidJUnit4.class) @SmallTest public class VcnCellUnderlyingNetworkTemplateTest extends VcnUnderlyingNetworkTemplateTestBase { private static final Set<String> ALLOWED_PLMN_IDS = new HashSet<>(); diff --git a/tests/vcn/java/android/net/vcn/VcnConfigTest.java b/tests/vcn/java/android/net/vcn/VcnConfigTest.java index fa97de0aff45..73a0a6183cb6 100644 --- a/tests/vcn/java/android/net/vcn/VcnConfigTest.java +++ b/tests/vcn/java/android/net/vcn/VcnConfigTest.java @@ -29,14 +29,11 @@ import static org.mockito.Mockito.mock; import android.annotation.NonNull; import android.content.Context; -import android.os.Build; import android.os.Parcel; import android.util.ArraySet; import androidx.test.filters.SmallTest; - -import com.android.testutils.DevSdkIgnoreRule; -import com.android.testutils.DevSdkIgnoreRunner; +import androidx.test.runner.AndroidJUnit4; import org.junit.Before; import org.junit.Test; @@ -45,10 +42,7 @@ import org.junit.runner.RunWith; import java.util.Collections; import java.util.Set; -// TODO: b/374174952 After B finalization, use Sdk36ModuleController to ensure VCN tests only run on -// Android B/B+ -@RunWith(DevSdkIgnoreRunner.class) -@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.VANILLA_ICE_CREAM) +@RunWith(AndroidJUnit4.class) @SmallTest public class VcnConfigTest { private static final String TEST_PACKAGE_NAME = VcnConfigTest.class.getPackage().getName(); diff --git a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java index 990cc74caf6c..59dc68900100 100644 --- a/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java +++ b/tests/vcn/java/android/net/vcn/VcnGatewayConnectionConfigTest.java @@ -34,13 +34,10 @@ import android.net.ipsec.ike.IkeSessionParams; import android.net.ipsec.ike.IkeTunnelConnectionParams; import android.net.vcn.persistablebundleutils.IkeSessionParamsUtilsTest; import android.net.vcn.persistablebundleutils.TunnelConnectionParamsUtilsTest; -import android.os.Build; import android.os.PersistableBundle; import androidx.test.filters.SmallTest; - -import com.android.testutils.DevSdkIgnoreRule; -import com.android.testutils.DevSdkIgnoreRunner; +import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; @@ -52,10 +49,7 @@ import java.util.List; import java.util.Set; import java.util.concurrent.TimeUnit; -// TODO: b/374174952 After B finalization, use Sdk36ModuleController to ensure VCN tests only run on -// Android B/B+ -@RunWith(DevSdkIgnoreRunner.class) -@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.VANILLA_ICE_CREAM) +@RunWith(AndroidJUnit4.class) @SmallTest public class VcnGatewayConnectionConfigTest { // Public for use in VcnGatewayConnectionTest diff --git a/tests/vcn/java/android/net/vcn/VcnManagerTest.java b/tests/vcn/java/android/net/vcn/VcnManagerTest.java index 1739fbc0fa6d..ecb177e81ef3 100644 --- a/tests/vcn/java/android/net/vcn/VcnManagerTest.java +++ b/tests/vcn/java/android/net/vcn/VcnManagerTest.java @@ -38,13 +38,10 @@ import android.net.NetworkCapabilities; import android.net.vcn.VcnManager.VcnStatusCallback; import android.net.vcn.VcnManager.VcnStatusCallbackBinder; import android.net.vcn.VcnManager.VcnUnderlyingNetworkPolicyListener; -import android.os.Build; import android.os.ParcelUuid; import androidx.test.filters.SmallTest; - -import com.android.testutils.DevSdkIgnoreRule; -import com.android.testutils.DevSdkIgnoreRunner; +import androidx.test.runner.AndroidJUnit4; import org.junit.Before; import org.junit.Test; @@ -55,10 +52,7 @@ import java.net.UnknownHostException; import java.util.UUID; import java.util.concurrent.Executor; -// TODO: b/374174952 After B finalization, use Sdk36ModuleController to ensure VCN tests only run on -// Android B/B+ -@RunWith(DevSdkIgnoreRunner.class) -@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.VANILLA_ICE_CREAM) +@RunWith(AndroidJUnit4.class) @SmallTest public class VcnManagerTest { private static final ParcelUuid SUB_GROUP = new ParcelUuid(new UUID(0, 0)); diff --git a/tests/vcn/java/android/net/vcn/VcnTransportInfoTest.java b/tests/vcn/java/android/net/vcn/VcnTransportInfoTest.java index 52952eb3f2cc..1d57cf2bfbd5 100644 --- a/tests/vcn/java/android/net/vcn/VcnTransportInfoTest.java +++ b/tests/vcn/java/android/net/vcn/VcnTransportInfoTest.java @@ -30,23 +30,17 @@ import static org.junit.Assert.fail; import android.net.NetworkCapabilities; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiInfo; -import android.os.Build; import android.os.Parcel; import androidx.test.filters.SmallTest; - -import com.android.testutils.DevSdkIgnoreRule; -import com.android.testutils.DevSdkIgnoreRunner; +import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; import java.util.Arrays; -// TODO: b/374174952 After B finalization, use Sdk36ModuleController to ensure VCN tests only run on -// Android B/B+ -@RunWith(DevSdkIgnoreRunner.class) -@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.VANILLA_ICE_CREAM) +@RunWith(AndroidJUnit4.class) @SmallTest public class VcnTransportInfoTest { private static final int SUB_ID = 1; diff --git a/tests/vcn/java/android/net/vcn/VcnUnderlyingNetworkPolicyTest.java b/tests/vcn/java/android/net/vcn/VcnUnderlyingNetworkPolicyTest.java index c82d2003dbf6..891298a32e7d 100644 --- a/tests/vcn/java/android/net/vcn/VcnUnderlyingNetworkPolicyTest.java +++ b/tests/vcn/java/android/net/vcn/VcnUnderlyingNetworkPolicyTest.java @@ -22,20 +22,14 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; import android.net.NetworkCapabilities; -import android.os.Build; import androidx.test.filters.SmallTest; - -import com.android.testutils.DevSdkIgnoreRule; -import com.android.testutils.DevSdkIgnoreRunner; +import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; -// TODO: b/374174952 After B finalization, use Sdk36ModuleController to ensure VCN tests only run on -// Android B/B+ -@RunWith(DevSdkIgnoreRunner.class) -@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.VANILLA_ICE_CREAM) +@RunWith(AndroidJUnit4.class) @SmallTest public class VcnUnderlyingNetworkPolicyTest { private static final VcnUnderlyingNetworkPolicy DEFAULT_NETWORK_POLICY = diff --git a/tests/vcn/java/android/net/vcn/VcnUnderlyingNetworkSpecifierTest.java b/tests/vcn/java/android/net/vcn/VcnUnderlyingNetworkSpecifierTest.java index 22361cc71f12..2110d6ee7c86 100644 --- a/tests/vcn/java/android/net/vcn/VcnUnderlyingNetworkSpecifierTest.java +++ b/tests/vcn/java/android/net/vcn/VcnUnderlyingNetworkSpecifierTest.java @@ -22,20 +22,14 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import android.net.TelephonyNetworkSpecifier; -import android.os.Build; import androidx.test.filters.SmallTest; - -import com.android.testutils.DevSdkIgnoreRule; -import com.android.testutils.DevSdkIgnoreRunner; +import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; -// TODO: b/374174952 After B finalization, use Sdk36ModuleController to ensure VCN tests only run on -// Android B/B+ -@RunWith(DevSdkIgnoreRunner.class) -@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.VANILLA_ICE_CREAM) +@RunWith(AndroidJUnit4.class) @SmallTest public class VcnUnderlyingNetworkSpecifierTest { private static final int[] TEST_SUB_IDS = new int[] {1, 2, 3, 5}; diff --git a/tests/vcn/java/android/net/vcn/VcnUtilsTest.java b/tests/vcn/java/android/net/vcn/VcnUtilsTest.java index fb040d8f9b91..0c3f9fedac05 100644 --- a/tests/vcn/java/android/net/vcn/VcnUtilsTest.java +++ b/tests/vcn/java/android/net/vcn/VcnUtilsTest.java @@ -30,12 +30,9 @@ import android.net.Network; import android.net.NetworkCapabilities; import android.net.TelephonyNetworkSpecifier; import android.net.wifi.WifiInfo; -import android.os.Build; import androidx.test.filters.SmallTest; - -import com.android.testutils.DevSdkIgnoreRule; -import com.android.testutils.DevSdkIgnoreRunner; +import androidx.test.runner.AndroidJUnit4; import org.junit.Before; import org.junit.Test; @@ -44,10 +41,7 @@ import org.junit.runner.RunWith; import java.util.Arrays; import java.util.Collections; -// TODO: b/374174952 After B finalization, use Sdk36ModuleController to ensure VCN tests only run on -// Android B/B+ -@RunWith(DevSdkIgnoreRunner.class) -@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.VANILLA_ICE_CREAM) +@RunWith(AndroidJUnit4.class) @SmallTest public class VcnUtilsTest { private static final int SUB_ID = 1; diff --git a/tests/vcn/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplateTest.java b/tests/vcn/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplateTest.java index 2c072e1cbc88..dbbfd83eab59 100644 --- a/tests/vcn/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplateTest.java +++ b/tests/vcn/java/android/net/vcn/VcnWifiUnderlyingNetworkTemplateTest.java @@ -22,22 +22,15 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; -import android.os.Build; - import androidx.test.filters.SmallTest; - -import com.android.testutils.DevSdkIgnoreRule; -import com.android.testutils.DevSdkIgnoreRunner; +import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; import java.util.Set; -// TODO: b/374174952 After B finalization, use Sdk36ModuleController to ensure VCN tests only run on -// Android B/B+ -@RunWith(DevSdkIgnoreRunner.class) -@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.VANILLA_ICE_CREAM) +@RunWith(AndroidJUnit4.class) @SmallTest public class VcnWifiUnderlyingNetworkTemplateTest extends VcnUnderlyingNetworkTemplateTestBase { private static final String SSID = "TestWifi"; diff --git a/tests/vcn/java/android/net/vcn/persistablebundleutils/EapSessionConfigUtilsTest.java b/tests/vcn/java/android/net/vcn/persistablebundleutils/EapSessionConfigUtilsTest.java index 01e9ac2ac3cf..bc8e9d3200b6 100644 --- a/tests/vcn/java/android/net/vcn/persistablebundleutils/EapSessionConfigUtilsTest.java +++ b/tests/vcn/java/android/net/vcn/persistablebundleutils/EapSessionConfigUtilsTest.java @@ -21,14 +21,11 @@ import static android.telephony.TelephonyManager.APPTYPE_USIM; import static org.junit.Assert.assertEquals; import android.net.eap.EapSessionConfig; -import android.os.Build; import android.os.PersistableBundle; import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; - -import com.android.testutils.DevSdkIgnoreRule; -import com.android.testutils.DevSdkIgnoreRunner; +import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; @@ -38,10 +35,7 @@ import java.nio.charset.StandardCharsets; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; -// TODO: b/374174952 After B finalization, use Sdk36ModuleController to ensure VCN tests only run on -// Android B/B+ -@RunWith(DevSdkIgnoreRunner.class) -@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.VANILLA_ICE_CREAM) +@RunWith(AndroidJUnit4.class) @SmallTest public class EapSessionConfigUtilsTest { private static final byte[] EAP_ID = "test@android.net".getBytes(StandardCharsets.US_ASCII); diff --git a/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeIdentificationUtilsTest.java b/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeIdentificationUtilsTest.java index 821e5a6c94cb..4f3930f9b5af 100644 --- a/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeIdentificationUtilsTest.java +++ b/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeIdentificationUtilsTest.java @@ -25,13 +25,10 @@ import android.net.ipsec.ike.IkeIpv4AddrIdentification; import android.net.ipsec.ike.IkeIpv6AddrIdentification; import android.net.ipsec.ike.IkeKeyIdIdentification; import android.net.ipsec.ike.IkeRfc822AddrIdentification; -import android.os.Build; import android.os.PersistableBundle; import androidx.test.filters.SmallTest; - -import com.android.testutils.DevSdkIgnoreRule; -import com.android.testutils.DevSdkIgnoreRunner; +import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; @@ -42,10 +39,7 @@ import java.net.InetAddress; import javax.security.auth.x500.X500Principal; -// TODO: b/374174952 After B finalization, use Sdk36ModuleController to ensure VCN tests only run on -// Android B/B+ -@RunWith(DevSdkIgnoreRunner.class) -@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.VANILLA_ICE_CREAM) +@RunWith(AndroidJUnit4.class) @SmallTest public class IkeIdentificationUtilsTest { private static void verifyPersistableBundleEncodeDecodeIsLossless(IkeIdentification id) { diff --git a/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtilsTest.java b/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtilsTest.java index 7200aee1c012..9f7d2390938f 100644 --- a/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtilsTest.java +++ b/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeSessionParamsUtilsTest.java @@ -29,16 +29,14 @@ import android.net.InetAddresses; import android.net.eap.EapSessionConfig; import android.net.ipsec.ike.IkeFqdnIdentification; import android.net.ipsec.ike.IkeSessionParams; -import android.os.Build; import android.os.PersistableBundle; import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import com.android.internal.org.bouncycastle.util.io.pem.PemObject; import com.android.internal.org.bouncycastle.util.io.pem.PemReader; -import com.android.testutils.DevSdkIgnoreRule; -import com.android.testutils.DevSdkIgnoreRunner; import org.junit.Test; import org.junit.runner.RunWith; @@ -54,10 +52,7 @@ import java.security.cert.X509Certificate; import java.security.interfaces.RSAPrivateKey; import java.util.concurrent.TimeUnit; -// TODO: b/374174952 After B finalization, use Sdk36ModuleController to ensure VCN tests only run on -// Android B/B+ -@RunWith(DevSdkIgnoreRunner.class) -@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.VANILLA_ICE_CREAM) +@RunWith(AndroidJUnit4.class) @SmallTest public class IkeSessionParamsUtilsTest { // Public for use in VcnGatewayConnectionConfigTest, EncryptedTunnelParamsUtilsTest diff --git a/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeTrafficSelectorUtilsTest.java b/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeTrafficSelectorUtilsTest.java index 957e785d70c0..28cf38a2a583 100644 --- a/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeTrafficSelectorUtilsTest.java +++ b/tests/vcn/java/android/net/vcn/persistablebundleutils/IkeTrafficSelectorUtilsTest.java @@ -20,23 +20,17 @@ import static org.junit.Assert.assertEquals; import android.net.InetAddresses; import android.net.ipsec.ike.IkeTrafficSelector; -import android.os.Build; import android.os.PersistableBundle; import androidx.test.filters.SmallTest; - -import com.android.testutils.DevSdkIgnoreRule; -import com.android.testutils.DevSdkIgnoreRunner; +import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; import java.net.InetAddress; -// TODO: b/374174952 After B finalization, use Sdk36ModuleController to ensure VCN tests only run on -// Android B/B+ -@RunWith(DevSdkIgnoreRunner.class) -@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.VANILLA_ICE_CREAM) +@RunWith(AndroidJUnit4.class) @SmallTest public class IkeTrafficSelectorUtilsTest { private static final int START_PORT = 16; diff --git a/tests/vcn/java/android/net/vcn/persistablebundleutils/SaProposalUtilsTest.java b/tests/vcn/java/android/net/vcn/persistablebundleutils/SaProposalUtilsTest.java index 1e8f5ff2dc07..664044a9e7d4 100644 --- a/tests/vcn/java/android/net/vcn/persistablebundleutils/SaProposalUtilsTest.java +++ b/tests/vcn/java/android/net/vcn/persistablebundleutils/SaProposalUtilsTest.java @@ -21,21 +21,15 @@ import static org.junit.Assert.assertEquals; import android.net.ipsec.ike.ChildSaProposal; import android.net.ipsec.ike.IkeSaProposal; import android.net.ipsec.ike.SaProposal; -import android.os.Build; import android.os.PersistableBundle; import androidx.test.filters.SmallTest; - -import com.android.testutils.DevSdkIgnoreRule; -import com.android.testutils.DevSdkIgnoreRunner; +import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; -// TODO: b/374174952 After B finalization, use Sdk36ModuleController to ensure VCN tests only run on -// Android B/B+ -@RunWith(DevSdkIgnoreRunner.class) -@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.VANILLA_ICE_CREAM) +@RunWith(AndroidJUnit4.class) @SmallTest public class SaProposalUtilsTest { /** Package private so that IkeSessionParamsUtilsTest can use it */ diff --git a/tests/vcn/java/android/net/vcn/persistablebundleutils/TunnelConnectionParamsUtilsTest.java b/tests/vcn/java/android/net/vcn/persistablebundleutils/TunnelConnectionParamsUtilsTest.java index 7d17724112ec..f9dc9eb4d5ae 100644 --- a/tests/vcn/java/android/net/vcn/persistablebundleutils/TunnelConnectionParamsUtilsTest.java +++ b/tests/vcn/java/android/net/vcn/persistablebundleutils/TunnelConnectionParamsUtilsTest.java @@ -20,20 +20,14 @@ import static org.junit.Assert.assertEquals; import android.net.ipsec.ike.IkeSessionParams; import android.net.ipsec.ike.IkeTunnelConnectionParams; -import android.os.Build; import androidx.test.filters.SmallTest; - -import com.android.testutils.DevSdkIgnoreRule; -import com.android.testutils.DevSdkIgnoreRunner; +import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; -// TODO: b/374174952 After B finalization, use Sdk36ModuleController to ensure VCN tests only run on -// Android B/B+ -@RunWith(DevSdkIgnoreRunner.class) -@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.VANILLA_ICE_CREAM) +@RunWith(AndroidJUnit4.class) @SmallTest public class TunnelConnectionParamsUtilsTest { // Public for use in VcnGatewayConnectionConfigTest diff --git a/tests/vcn/java/android/net/vcn/persistablebundleutils/TunnelModeChildSessionParamsUtilsTest.java b/tests/vcn/java/android/net/vcn/persistablebundleutils/TunnelModeChildSessionParamsUtilsTest.java index 3d7348a79b8c..e0b5f0ef0381 100644 --- a/tests/vcn/java/android/net/vcn/persistablebundleutils/TunnelModeChildSessionParamsUtilsTest.java +++ b/tests/vcn/java/android/net/vcn/persistablebundleutils/TunnelModeChildSessionParamsUtilsTest.java @@ -25,13 +25,10 @@ import android.net.InetAddresses; import android.net.ipsec.ike.ChildSaProposal; import android.net.ipsec.ike.IkeTrafficSelector; import android.net.ipsec.ike.TunnelModeChildSessionParams; -import android.os.Build; import android.os.PersistableBundle; import androidx.test.filters.SmallTest; - -import com.android.testutils.DevSdkIgnoreRule; -import com.android.testutils.DevSdkIgnoreRunner; +import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; @@ -40,10 +37,7 @@ import java.net.Inet4Address; import java.net.Inet6Address; import java.util.concurrent.TimeUnit; -// TODO: b/374174952 After B finalization, use Sdk36ModuleController to ensure VCN tests only run on -// Android B/B+ -@RunWith(DevSdkIgnoreRunner.class) -@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.VANILLA_ICE_CREAM) +@RunWith(AndroidJUnit4.class) @SmallTest public class TunnelModeChildSessionParamsUtilsTest { // Package private for use in EncryptedTunnelParamsUtilsTest diff --git a/tests/vcn/java/android/net/vcn/util/MtuUtilsTest.java b/tests/vcn/java/android/net/vcn/util/MtuUtilsTest.java index 99c7aa72146b..47638b002f37 100644 --- a/tests/vcn/java/android/net/vcn/util/MtuUtilsTest.java +++ b/tests/vcn/java/android/net/vcn/util/MtuUtilsTest.java @@ -33,12 +33,9 @@ import static org.junit.Assert.assertTrue; import static java.util.Collections.emptyList; import android.net.ipsec.ike.ChildSaProposal; -import android.os.Build; import androidx.test.filters.SmallTest; - -import com.android.testutils.DevSdkIgnoreRule; -import com.android.testutils.DevSdkIgnoreRunner; +import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; @@ -46,10 +43,7 @@ import org.junit.runner.RunWith; import java.util.Arrays; import java.util.List; -// TODO: b/374174952 After B finalization, use Sdk36ModuleController to ensure VCN tests only run on -// Android B/B+ -@RunWith(DevSdkIgnoreRunner.class) -@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.VANILLA_ICE_CREAM) +@RunWith(AndroidJUnit4.class) @SmallTest public class MtuUtilsTest { private void verifyUnderlyingMtuZero(boolean isIpv4) { diff --git a/tests/vcn/java/android/net/vcn/util/PersistableBundleUtilsTest.java b/tests/vcn/java/android/net/vcn/util/PersistableBundleUtilsTest.java index f7786af840ee..c84e60086b37 100644 --- a/tests/vcn/java/android/net/vcn/util/PersistableBundleUtilsTest.java +++ b/tests/vcn/java/android/net/vcn/util/PersistableBundleUtilsTest.java @@ -21,13 +21,10 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import android.os.Build; import android.os.PersistableBundle; import androidx.test.filters.SmallTest; - -import com.android.testutils.DevSdkIgnoreRule; -import com.android.testutils.DevSdkIgnoreRunner; +import androidx.test.runner.AndroidJUnit4; import org.junit.Test; import org.junit.runner.RunWith; @@ -38,10 +35,7 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Objects; -// TODO: b/374174952 After B finalization, use Sdk36ModuleController to ensure VCN tests only run on -// Android B/B+ -@RunWith(DevSdkIgnoreRunner.class) -@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.VANILLA_ICE_CREAM) +@RunWith(AndroidJUnit4.class) @SmallTest public class PersistableBundleUtilsTest { private static final String TEST_KEY = "testKey"; diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java index 3cccbc419425..02400ead5146 100644 --- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java +++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java @@ -95,6 +95,7 @@ import android.telephony.TelephonyManager; import android.util.ArraySet; import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import com.android.server.VcnManagementService.VcnCallback; import com.android.server.VcnManagementService.VcnStatusCallbackInfo; @@ -102,8 +103,6 @@ import com.android.server.vcn.TelephonySubscriptionTracker; import com.android.server.vcn.Vcn; import com.android.server.vcn.VcnContext; import com.android.server.vcn.VcnNetworkProvider; -import com.android.testutils.DevSdkIgnoreRule; -import com.android.testutils.DevSdkIgnoreRunner; import org.junit.Before; import org.junit.Rule; @@ -120,10 +119,7 @@ import java.util.Map.Entry; import java.util.Set; import java.util.UUID; -// TODO: b/374174952 After B finalization, use Sdk36ModuleController to ensure VCN tests only run on -// Android B/B+ -@RunWith(DevSdkIgnoreRunner.class) -@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.VANILLA_ICE_CREAM) +@RunWith(AndroidJUnit4.class) @SmallTest public class VcnManagementServiceTest { @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); diff --git a/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java b/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java index 6276be27fbf5..6a4a1bd3aba3 100644 --- a/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java +++ b/tests/vcn/java/com/android/server/vcn/TelephonySubscriptionTrackerTest.java @@ -54,7 +54,6 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.net.vcn.VcnManager; -import android.os.Build; import android.os.Handler; import android.os.ParcelUuid; import android.os.PersistableBundle; @@ -70,10 +69,9 @@ import android.util.ArrayMap; import android.util.ArraySet; import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import com.android.modules.utils.HandlerExecutor; -import com.android.testutils.DevSdkIgnoreRule; -import com.android.testutils.DevSdkIgnoreRunner; import org.junit.Before; import org.junit.Test; @@ -89,10 +87,7 @@ import java.util.Map; import java.util.Set; import java.util.UUID; -// TODO: b/374174952 After B finalization, use Sdk36ModuleController to ensure VCN tests only run on -// Android B/B+ -@RunWith(DevSdkIgnoreRunner.class) -@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.VANILLA_ICE_CREAM) +@RunWith(AndroidJUnit4.class) @SmallTest public class TelephonySubscriptionTrackerTest { private static final String PACKAGE_NAME = diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java index a34908051360..bf0c46c60f68 100644 --- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java +++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectedStateTest.java @@ -70,18 +70,16 @@ import android.net.vcn.VcnGatewayConnectionConfigTest; import android.net.vcn.VcnManager.VcnErrorCode; import android.net.vcn.VcnTransportInfo; import android.net.vcn.util.MtuUtils; -import android.os.Build; import android.os.PersistableBundle; import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import com.android.server.vcn.VcnGatewayConnection.VcnChildSessionCallback; import com.android.server.vcn.VcnGatewayConnection.VcnChildSessionConfiguration; import com.android.server.vcn.VcnGatewayConnection.VcnIkeSession; import com.android.server.vcn.VcnGatewayConnection.VcnNetworkAgent; import com.android.server.vcn.routeselection.UnderlyingNetworkRecord; -import com.android.testutils.DevSdkIgnoreRule; -import com.android.testutils.DevSdkIgnoreRunner; import org.junit.Before; import org.junit.Test; @@ -96,10 +94,7 @@ import java.util.Collections; import java.util.List; import java.util.function.Consumer; -// TODO: b/374174952 After B finalization, use Sdk36ModuleController to ensure VCN tests only run on -// Android B/B+ -@RunWith(DevSdkIgnoreRunner.class) -@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.VANILLA_ICE_CREAM) +@RunWith(AndroidJUnit4.class) @SmallTest public class VcnGatewayConnectionConnectedStateTest extends VcnGatewayConnectionTestBase { private static final int PARALLEL_SA_COUNT = 4; diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java index e1a572e7a481..c946680db73f 100644 --- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java +++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionConnectingStateTest.java @@ -26,22 +26,16 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import android.net.ipsec.ike.IkeSessionParams; -import android.os.Build; import androidx.test.filters.SmallTest; - -import com.android.testutils.DevSdkIgnoreRule; -import com.android.testutils.DevSdkIgnoreRunner; +import androidx.test.runner.AndroidJUnit4; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; -// TODO: b/374174952 After B finalization, use Sdk36ModuleController to ensure VCN tests only run on -// Android B/B+ -@RunWith(DevSdkIgnoreRunner.class) -@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.VANILLA_ICE_CREAM) +@RunWith(AndroidJUnit4.class) @SmallTest public class VcnGatewayConnectionConnectingStateTest extends VcnGatewayConnectionTestBase { private VcnIkeSession mIkeSession; diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java index 7cfaf5be5111..0b470b9a35d0 100644 --- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java +++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectedStateTest.java @@ -30,21 +30,15 @@ import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import android.net.IpSecManager; -import android.os.Build; import androidx.test.filters.SmallTest; - -import com.android.testutils.DevSdkIgnoreRule; -import com.android.testutils.DevSdkIgnoreRunner; +import androidx.test.runner.AndroidJUnit4; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -// TODO: b/374174952 After B finalization, use Sdk36ModuleController to ensure VCN tests only run on -// Android B/B+ -@RunWith(DevSdkIgnoreRunner.class) -@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.VANILLA_ICE_CREAM) +@RunWith(AndroidJUnit4.class) @SmallTest public class VcnGatewayConnectionDisconnectedStateTest extends VcnGatewayConnectionTestBase { @Before diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectingStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectingStateTest.java index 9132d830c54e..bfcc4ca48313 100644 --- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectingStateTest.java +++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionDisconnectingStateTest.java @@ -23,21 +23,14 @@ import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; -import android.os.Build; - import androidx.test.filters.SmallTest; - -import com.android.testutils.DevSdkIgnoreRule; -import com.android.testutils.DevSdkIgnoreRunner; +import androidx.test.runner.AndroidJUnit4; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -// TODO: b/374174952 After B finalization, use Sdk36ModuleController to ensure VCN tests only run on -// Android B/B+ -@RunWith(DevSdkIgnoreRunner.class) -@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.VANILLA_ICE_CREAM) +@RunWith(AndroidJUnit4.class) @SmallTest public class VcnGatewayConnectionDisconnectingStateTest extends VcnGatewayConnectionTestBase { @Before diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionRetryTimeoutStateTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionRetryTimeoutStateTest.java index d5ef4e028709..e6fe509d1665 100644 --- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionRetryTimeoutStateTest.java +++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionRetryTimeoutStateTest.java @@ -27,21 +27,14 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; -import android.os.Build; - import androidx.test.filters.SmallTest; - -import com.android.testutils.DevSdkIgnoreRule; -import com.android.testutils.DevSdkIgnoreRunner; +import androidx.test.runner.AndroidJUnit4; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -// TODO: b/374174952 After B finalization, use Sdk36ModuleController to ensure VCN tests only run on -// Android B/B+ -@RunWith(DevSdkIgnoreRunner.class) -@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.VANILLA_ICE_CREAM) +@RunWith(AndroidJUnit4.class) @SmallTest public class VcnGatewayConnectionRetryTimeoutStateTest extends VcnGatewayConnectionTestBase { private long mFirstRetryInterval; diff --git a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java index 5283322682ee..2a2d5ba2ef4d 100644 --- a/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java +++ b/tests/vcn/java/com/android/server/vcn/VcnGatewayConnectionTest.java @@ -61,17 +61,15 @@ import android.net.vcn.VcnGatewayConnectionConfigTest; import android.net.vcn.VcnManager; import android.net.vcn.VcnTransportInfo; import android.net.wifi.WifiInfo; -import android.os.Build; import android.os.ParcelUuid; import android.os.Process; import android.telephony.SubscriptionInfo; import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot; import com.android.server.vcn.routeselection.UnderlyingNetworkRecord; -import com.android.testutils.DevSdkIgnoreRule; -import com.android.testutils.DevSdkIgnoreRunner; import org.junit.Before; import org.junit.Test; @@ -89,10 +87,7 @@ import java.util.UUID; import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; -// TODO: b/374174952 After B finalization, use Sdk36ModuleController to ensure VCN tests only run on -// Android B/B+ -@RunWith(DevSdkIgnoreRunner.class) -@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.VANILLA_ICE_CREAM) +@RunWith(AndroidJUnit4.class) @SmallTest public class VcnGatewayConnectionTest extends VcnGatewayConnectionTestBase { private static final int TEST_UID = Process.myUid() + 1; diff --git a/tests/vcn/java/com/android/server/vcn/VcnNetworkProviderTest.java b/tests/vcn/java/com/android/server/vcn/VcnNetworkProviderTest.java index 0185931d628f..4f705d62f155 100644 --- a/tests/vcn/java/com/android/server/vcn/VcnNetworkProviderTest.java +++ b/tests/vcn/java/com/android/server/vcn/VcnNetworkProviderTest.java @@ -29,14 +29,12 @@ import android.annotation.NonNull; import android.content.Context; import android.net.ConnectivityManager; import android.net.NetworkRequest; -import android.os.Build; import android.os.test.TestLooper; import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import com.android.server.vcn.VcnNetworkProvider.NetworkRequestListener; -import com.android.testutils.DevSdkIgnoreRule; -import com.android.testutils.DevSdkIgnoreRunner; import org.junit.Before; import org.junit.Test; @@ -46,10 +44,7 @@ import org.mockito.ArgumentCaptor; import java.util.ArrayList; import java.util.List; -// TODO: b/374174952 After B finalization, use Sdk36ModuleController to ensure VCN tests only run on -// Android B/B+ -@RunWith(DevSdkIgnoreRunner.class) -@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.VANILLA_ICE_CREAM) +@RunWith(AndroidJUnit4.class) @SmallTest public class VcnNetworkProviderTest { private static final int TEST_SCORE_UNSATISFIED = 0; diff --git a/tests/vcn/java/com/android/server/vcn/VcnTest.java b/tests/vcn/java/com/android/server/vcn/VcnTest.java index c12adcbab08a..1853b6b59f5d 100644 --- a/tests/vcn/java/com/android/server/vcn/VcnTest.java +++ b/tests/vcn/java/com/android/server/vcn/VcnTest.java @@ -49,7 +49,6 @@ import android.net.Uri; import android.net.vcn.VcnConfig; import android.net.vcn.VcnGatewayConnectionConfig; import android.net.vcn.VcnGatewayConnectionConfigTest; -import android.os.Build; import android.os.ParcelUuid; import android.os.test.TestLooper; import android.provider.Settings; @@ -57,14 +56,13 @@ import android.telephony.TelephonyManager; import android.util.ArraySet; import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import com.android.server.VcnManagementService.VcnCallback; import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot; import com.android.server.vcn.Vcn.VcnGatewayStatusCallback; import com.android.server.vcn.Vcn.VcnUserMobileDataStateListener; import com.android.server.vcn.VcnNetworkProvider.NetworkRequestListener; -import com.android.testutils.DevSdkIgnoreRule; -import com.android.testutils.DevSdkIgnoreRunner; import org.junit.Before; import org.junit.Test; @@ -79,10 +77,7 @@ import java.util.Map.Entry; import java.util.Set; import java.util.UUID; -// TODO: b/374174952 After B finalization, use Sdk36ModuleController to ensure VCN tests only run on -// Android B/B+ -@RunWith(DevSdkIgnoreRunner.class) -@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.VANILLA_ICE_CREAM) +@RunWith(AndroidJUnit4.class) @SmallTest public class VcnTest { private static final String PKG_NAME = VcnTest.class.getPackage().getName(); diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/IpSecPacketLossDetectorTest.java b/tests/vcn/java/com/android/server/vcn/routeselection/IpSecPacketLossDetectorTest.java index 53a36d3e4d6a..224b45ced965 100644 --- a/tests/vcn/java/com/android/server/vcn/routeselection/IpSecPacketLossDetectorTest.java +++ b/tests/vcn/java/com/android/server/vcn/routeselection/IpSecPacketLossDetectorTest.java @@ -44,18 +44,16 @@ import static org.mockito.Mockito.when; import android.content.BroadcastReceiver; import android.content.Intent; import android.net.IpSecTransformState; -import android.os.Build; import android.os.OutcomeReceiver; import android.os.PowerManager; import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import com.android.server.vcn.routeselection.IpSecPacketLossDetector.PacketLossCalculationResult; import com.android.server.vcn.routeselection.IpSecPacketLossDetector.PacketLossCalculator; import com.android.server.vcn.routeselection.NetworkMetricMonitor.IpSecTransformWrapper; import com.android.server.vcn.routeselection.NetworkMetricMonitor.NetworkMetricMonitorCallback; -import com.android.testutils.DevSdkIgnoreRule; -import com.android.testutils.DevSdkIgnoreRunner; import org.junit.Before; import org.junit.Test; @@ -69,10 +67,7 @@ import java.util.Arrays; import java.util.BitSet; import java.util.concurrent.TimeUnit; -// TODO: b/374174952 After B finalization, use Sdk36ModuleController to ensure VCN tests only run on -// Android B/B+ -@RunWith(DevSdkIgnoreRunner.class) -@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.VANILLA_ICE_CREAM) +@RunWith(AndroidJUnit4.class) @SmallTest public class IpSecPacketLossDetectorTest extends NetworkEvaluationTestBase { private static final String TAG = IpSecPacketLossDetectorTest.class.getSimpleName(); diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java index a9c637f7c943..16dbab9d7a61 100644 --- a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java +++ b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkPriorityClassifierTest.java @@ -42,14 +42,11 @@ import android.net.vcn.VcnGatewayConnectionConfig; import android.net.vcn.VcnManager; import android.net.vcn.VcnUnderlyingNetworkTemplate; import android.net.vcn.VcnWifiUnderlyingNetworkTemplate; -import android.os.Build; import android.os.PersistableBundle; import android.util.ArraySet; import androidx.test.filters.SmallTest; - -import com.android.testutils.DevSdkIgnoreRule; -import com.android.testutils.DevSdkIgnoreRunner; +import androidx.test.runner.AndroidJUnit4; import org.junit.Before; import org.junit.Test; @@ -59,10 +56,7 @@ import java.util.Collections; import java.util.List; import java.util.Set; -// TODO: b/374174952 After B finalization, use Sdk36ModuleController to ensure VCN tests only run on -// Android B/B+ -@RunWith(DevSdkIgnoreRunner.class) -@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.VANILLA_ICE_CREAM) +@RunWith(AndroidJUnit4.class) @SmallTest public class NetworkPriorityClassifierTest extends NetworkEvaluationTestBase { private UnderlyingNetworkRecord mWifiNetworkRecord; diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkControllerTest.java b/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkControllerTest.java index 99c508c139ec..b67a2fd21b40 100644 --- a/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkControllerTest.java +++ b/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkControllerTest.java @@ -58,7 +58,6 @@ import android.net.vcn.VcnCellUnderlyingNetworkTemplate; import android.net.vcn.VcnCellUnderlyingNetworkTemplateTest; import android.net.vcn.VcnGatewayConnectionConfigTest; import android.net.vcn.VcnUnderlyingNetworkTemplate; -import android.os.Build; import android.os.ParcelUuid; import android.os.test.TestLooper; import android.telephony.CarrierConfigManager; @@ -67,6 +66,7 @@ import android.telephony.TelephonyManager; import android.util.ArraySet; import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot; import com.android.server.vcn.VcnContext; @@ -76,8 +76,6 @@ import com.android.server.vcn.routeselection.UnderlyingNetworkController.Network import com.android.server.vcn.routeselection.UnderlyingNetworkController.UnderlyingNetworkControllerCallback; import com.android.server.vcn.routeselection.UnderlyingNetworkController.UnderlyingNetworkListener; import com.android.server.vcn.routeselection.UnderlyingNetworkEvaluator.NetworkEvaluatorCallback; -import com.android.testutils.DevSdkIgnoreRule; -import com.android.testutils.DevSdkIgnoreRunner; import org.junit.Before; import org.junit.Test; @@ -95,10 +93,7 @@ import java.util.List; import java.util.Set; import java.util.UUID; -// TODO: b/374174952 After B finalization, use Sdk36ModuleController to ensure VCN tests only run on -// Android B/B+ -@RunWith(DevSdkIgnoreRunner.class) -@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.VANILLA_ICE_CREAM) +@RunWith(AndroidJUnit4.class) @SmallTest public class UnderlyingNetworkControllerTest { private static final ParcelUuid SUB_GROUP = new ParcelUuid(new UUID(0, 0)); diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluatorTest.java b/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluatorTest.java index 3ca84cf05cd1..850f9aa963d0 100644 --- a/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluatorTest.java +++ b/tests/vcn/java/com/android/server/vcn/routeselection/UnderlyingNetworkEvaluatorTest.java @@ -37,15 +37,13 @@ import static org.mockito.Mockito.when; import android.net.IpSecTransform; import android.net.vcn.VcnGatewayConnectionConfig; -import android.os.Build; import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; import com.android.server.vcn.routeselection.NetworkMetricMonitor.NetworkMetricMonitorCallback; import com.android.server.vcn.routeselection.UnderlyingNetworkEvaluator.Dependencies; import com.android.server.vcn.routeselection.UnderlyingNetworkEvaluator.NetworkEvaluatorCallback; -import com.android.testutils.DevSdkIgnoreRule; -import com.android.testutils.DevSdkIgnoreRunner; import org.junit.Before; import org.junit.Test; @@ -56,10 +54,7 @@ import org.mockito.Mock; import java.util.concurrent.TimeUnit; -// TODO: b/374174952 After B finalization, use Sdk36ModuleController to ensure VCN tests only run on -// Android B/B+ -@RunWith(DevSdkIgnoreRunner.class) -@DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.VANILLA_ICE_CREAM) +@RunWith(AndroidJUnit4.class) @SmallTest public class UnderlyingNetworkEvaluatorTest extends NetworkEvaluationTestBase { private static final int PENALTY_TIMEOUT_MIN = 10; diff --git a/tools/aapt2/ResourcesInternal.proto b/tools/aapt2/ResourcesInternal.proto index f4735a2f6ce7..380c5f21103c 100644 --- a/tools/aapt2/ResourcesInternal.proto +++ b/tools/aapt2/ResourcesInternal.proto @@ -50,8 +50,11 @@ message CompiledFile { // Any symbols this file auto-generates/exports (eg. @+id/foo in an XML file). repeated Symbol exported_symbol = 5; - // The status of the flag the file is behind if any + // The status of the read only flag the file is behind if any uint32 flag_status = 6; bool flag_negated = 7; string flag_name = 8; + + // Whether the file uses read/write feature flags + bool uses_readwrite_feature_flags = 9; } diff --git a/tools/aapt2/cmd/Compile.cpp b/tools/aapt2/cmd/Compile.cpp index a5e18d35a256..3b4f5429f254 100644 --- a/tools/aapt2/cmd/Compile.cpp +++ b/tools/aapt2/cmd/Compile.cpp @@ -407,6 +407,45 @@ static bool IsValidFile(IAaptContext* context, const std::string& input_path) { return true; } +class FindReadWriteFlagsVisitor : public xml::Visitor { + public: + FindReadWriteFlagsVisitor(const FeatureFlagValues& feature_flag_values) + : feature_flag_values_(feature_flag_values) { + } + + void Visit(xml::Element* node) override { + if (had_flags_) { + return; + } + auto* attr = node->FindAttribute(xml::kSchemaAndroid, xml::kAttrFeatureFlag); + if (attr != nullptr) { + std::string_view flag_name = util::TrimWhitespace(attr->value); + if (flag_name.starts_with('!')) { + flag_name = flag_name.substr(1); + } + if (auto it = feature_flag_values_.find(flag_name); it != feature_flag_values_.end()) { + if (!it->second.read_only) { + had_flags_ = true; + return; + } + } else { + // Flag not passed to aapt2, must evaluate at runtime + had_flags_ = true; + return; + } + } + VisitChildren(node); + } + + bool HadFlags() const { + return had_flags_; + } + + private: + bool had_flags_ = false; + const FeatureFlagValues& feature_flag_values_; +}; + static bool CompileXml(IAaptContext* context, const CompileOptions& options, const ResourcePathData& path_data, io::IFile* file, IArchiveWriter* writer, const std::string& output_path) { @@ -436,6 +475,10 @@ static bool CompileXml(IAaptContext* context, const CompileOptions& options, xmlres->file.type = ResourceFile::Type::kProtoXml; xmlres->file.flag = ParseFlag(path_data.flag_name); + FindReadWriteFlagsVisitor visitor(options.feature_flag_values); + xmlres->root->Accept(&visitor); + xmlres->file.uses_readwrite_feature_flags = visitor.HadFlags(); + if (xmlres->file.flag) { std::string error; auto flag_status = GetFlagStatus(xmlres->file.flag, options.feature_flag_values, &error); diff --git a/tools/aapt2/cmd/Convert.h b/tools/aapt2/cmd/Convert.h index 9452e588953e..5576ec0b882f 100644 --- a/tools/aapt2/cmd/Convert.h +++ b/tools/aapt2/cmd/Convert.h @@ -38,14 +38,14 @@ class ConvertCommand : public Command { "--enable-sparse-encoding", "Enables encoding sparse entries using a binary search tree.\n" "This decreases APK size at the cost of resource retrieval performance.\n" - "Only applies sparse encoding to Android O+ resources or all resources if minSdk of " - "the APK is O+", + "Only applies sparse encoding if minSdk of the APK is >= 32", &enable_sparse_encoding_); - AddOptionalSwitch("--force-sparse-encoding", - "Enables encoding sparse entries using a binary search tree.\n" - "This decreases APK size at the cost of resource retrieval performance.\n" - "Applies sparse encoding to all resources regardless of minSdk.", - &force_sparse_encoding_); + AddOptionalSwitch( + "--force-sparse-encoding", + "Enables encoding sparse entries using a binary search tree.\n" + "This decreases APK size at the cost of resource retrieval performance.\n" + "Only applies sparse encoding if minSdk of the APK is >= 32 or is not set", + &force_sparse_encoding_); AddOptionalSwitch( "--enable-compact-entries", "This decreases APK size by using compact resource entries for simple data types.", diff --git a/tools/aapt2/cmd/Link.cpp b/tools/aapt2/cmd/Link.cpp index 755dbb6f8e42..0e18ee250993 100644 --- a/tools/aapt2/cmd/Link.cpp +++ b/tools/aapt2/cmd/Link.cpp @@ -615,6 +615,8 @@ bool ResourceFileFlattener::Flatten(ResourceTable* table, IArchiveWriter* archiv file_op.xml_to_flatten->file.source = file_ref->GetSource(); file_op.xml_to_flatten->file.name = ResourceName(pkg->name, type->named_type, entry->name); + file_op.xml_to_flatten->file.uses_readwrite_feature_flags = + config_value->uses_readwrite_feature_flags; } // NOTE(adamlesinski): Explicitly construct a StringPiece here, or @@ -647,6 +649,17 @@ bool ResourceFileFlattener::Flatten(ResourceTable* table, IArchiveWriter* archiv } } + FeatureFlagsFilterOptions flags_filter_options; + // Don't fail on unrecognized flags or flags without values as these flags might be + // defined and have a value by the time they are evaluated at runtime. + flags_filter_options.fail_on_unrecognized_flags = false; + flags_filter_options.flags_must_have_value = false; + flags_filter_options.remove_disabled_elements = true; + FeatureFlagsFilter flags_filter(options_.feature_flag_values, flags_filter_options); + if (!flags_filter.Consume(context_, file_op.xml_to_flatten.get())) { + return 1; + } + std::vector<std::unique_ptr<xml::XmlResource>> versioned_docs = LinkAndVersionXmlFile(table, &file_op); if (versioned_docs.empty()) { @@ -673,6 +686,7 @@ bool ResourceFileFlattener::Flatten(ResourceTable* table, IArchiveWriter* archiv // Update the output format of this XML file. file_ref->type = XmlFileTypeForOutputFormat(options_.output_format); + bool result = table->AddResource( NewResourceBuilder(file.name) .SetValue(std::move(file_ref), file.config) @@ -685,14 +699,6 @@ bool ResourceFileFlattener::Flatten(ResourceTable* table, IArchiveWriter* archiv } } - FeatureFlagsFilterOptions flags_filter_options; - flags_filter_options.fail_on_unrecognized_flags = false; - flags_filter_options.flags_must_have_value = false; - FeatureFlagsFilter flags_filter(options_.feature_flag_values, flags_filter_options); - if (!flags_filter.Consume(context_, doc.get())) { - return 1; - } - error |= !FlattenXml(context_, *doc, dst_path, options_.keep_raw_values, false /*utf16*/, options_.output_format, archive_writer); } diff --git a/tools/aapt2/cmd/Link.h b/tools/aapt2/cmd/Link.h index 977978834fcd..54a8c8625eb5 100644 --- a/tools/aapt2/cmd/Link.h +++ b/tools/aapt2/cmd/Link.h @@ -164,9 +164,12 @@ class LinkCommand : public Command { AddOptionalSwitch("--no-resource-removal", "Disables automatic removal of resources without\n" "defaults. Use this only when building runtime resource overlay packages.", &options_.no_resource_removal); - AddOptionalSwitch("--enable-sparse-encoding", - "This decreases APK size at the cost of resource retrieval performance.", - &options_.use_sparse_encoding); + AddOptionalSwitch( + "--enable-sparse-encoding", + "Enables encoding sparse entries using a binary search tree.\n" + "This decreases APK size at the cost of resource retrieval performance.\n" + "Only applies sparse encoding if minSdk of the APK is >= 32", + &options_.use_sparse_encoding); AddOptionalSwitch("--enable-compact-entries", "This decreases APK size by using compact resource entries for simple data types.", &options_.table_flattener_options.use_compact_entries); diff --git a/tools/aapt2/cmd/Optimize.h b/tools/aapt2/cmd/Optimize.h index 012b0f230ca2..a8f547e3d96c 100644 --- a/tools/aapt2/cmd/Optimize.h +++ b/tools/aapt2/cmd/Optimize.h @@ -108,14 +108,14 @@ class OptimizeCommand : public Command { "--enable-sparse-encoding", "Enables encoding sparse entries using a binary search tree.\n" "This decreases APK size at the cost of resource retrieval performance.\n" - "Only applies sparse encoding to Android O+ resources or all resources if minSdk of " - "the APK is O+", + "Only applies sparse encoding if minSdk of the APK is >= 32", &options_.enable_sparse_encoding); - AddOptionalSwitch("--force-sparse-encoding", - "Enables encoding sparse entries using a binary search tree.\n" - "This decreases APK size at the cost of resource retrieval performance.\n" - "Applies sparse encoding to all resources regardless of minSdk.", - &options_.force_sparse_encoding); + AddOptionalSwitch( + "--force-sparse-encoding", + "Enables encoding sparse entries using a binary search tree.\n" + "This decreases APK size at the cost of resource retrieval performance.\n" + "Only applies sparse encoding if minSdk of the APK is >= 32 or is not set", + &options_.force_sparse_encoding); AddOptionalSwitch( "--enable-compact-entries", "This decreases APK size by using compact resource entries for simple data types.", diff --git a/tools/aapt2/format/binary/TableFlattener.cpp b/tools/aapt2/format/binary/TableFlattener.cpp index 50144ae816b6..d19c9f2d5d75 100644 --- a/tools/aapt2/format/binary/TableFlattener.cpp +++ b/tools/aapt2/format/binary/TableFlattener.cpp @@ -197,13 +197,16 @@ class PackageFlattener { bool sparse_encode = sparse_entries_ == SparseEntriesMode::Enabled || sparse_entries_ == SparseEntriesMode::Forced; - if (sparse_entries_ == SparseEntriesMode::Forced || - (context_->GetMinSdkVersion() == 0 && config.sdkVersion == 0)) { - // Sparse encode if forced or sdk version is not set in context and config. - } else { - // Otherwise, only sparse encode if the entries will be read on platforms S_V2+. - sparse_encode = sparse_encode && (context_->GetMinSdkVersion() >= SDK_S_V2); - } + // Only sparse encode if the entries will be read on platforms S_V2+. Sparse encoding + // is not supported on older platforms (b/197642721, b/197976367). + // + // We also allow sparse encoding for minSdk is 0 (not set) if sparse encoding is forced, + // in order to support Bundletool's usage of aapt2 where minSdk is not set in splits. + bool meets_min_sdk_requirement_for_sparse_encoding = + (context_->GetMinSdkVersion() >= SDK_S_V2) || + (context_->GetMinSdkVersion() == 0 && sparse_entries_ == SparseEntriesMode::Forced); + + sparse_encode = sparse_encode && meets_min_sdk_requirement_for_sparse_encoding; // Only sparse encode if the offsets are representable in 2 bytes. sparse_encode = sparse_encode && short_offsets; diff --git a/tools/aapt2/format/binary/TableFlattener_test.cpp b/tools/aapt2/format/binary/TableFlattener_test.cpp index 9156b96b67ec..0e8aae14a350 100644 --- a/tools/aapt2/format/binary/TableFlattener_test.cpp +++ b/tools/aapt2/format/binary/TableFlattener_test.cpp @@ -15,6 +15,7 @@ */ #include "format/binary/TableFlattener.h" +#include <string> #include "android-base/stringprintf.h" #include "androidfw/TypeWrappers.h" @@ -326,6 +327,28 @@ static std::unique_ptr<ResourceTable> BuildTableWithSparseEntries( return table; } +static void CheckSparseEntries(IAaptContext* context, const ConfigDescription& sparse_config, + const std::string& sparse_contents) { + ResourceTable sparse_table; + BinaryResourceParser parser(context->GetDiagnostics(), &sparse_table, Source("test.arsc"), + sparse_contents.data(), sparse_contents.size()); + ASSERT_TRUE(parser.Parse()); + + auto value = test::GetValueForConfig<BinaryPrimitive>(&sparse_table, "android:string/foo_0", + sparse_config); + ASSERT_THAT(value, NotNull()); + EXPECT_EQ(0u, value->value.data); + + ASSERT_THAT(test::GetValueForConfig<BinaryPrimitive>(&sparse_table, "android:string/foo_1", + sparse_config), + IsNull()); + + value = test::GetValueForConfig<BinaryPrimitive>(&sparse_table, "android:string/foo_4", + sparse_config); + ASSERT_THAT(value, NotNull()); + EXPECT_EQ(4u, value->value.data); +} + TEST_F(TableFlattenerTest, FlattenSparseEntryWithMinSdkSV2) { std::unique_ptr<IAaptContext> context = test::ContextBuilder() .SetCompilationPackage("android") @@ -347,29 +370,56 @@ TEST_F(TableFlattenerTest, FlattenSparseEntryWithMinSdkSV2) { EXPECT_GT(no_sparse_contents.size(), sparse_contents.size()); - // Attempt to parse the sparse contents. + CheckSparseEntries(context.get(), sparse_config, sparse_contents); +} - ResourceTable sparse_table; - BinaryResourceParser parser(context->GetDiagnostics(), &sparse_table, Source("test.arsc"), - sparse_contents.data(), sparse_contents.size()); - ASSERT_TRUE(parser.Parse()); +TEST_F(TableFlattenerTest, FlattenSparseEntryWithMinSdkSV2AndForced) { + std::unique_ptr<IAaptContext> context = test::ContextBuilder() + .SetCompilationPackage("android") + .SetPackageId(0x01) + .SetMinSdkVersion(SDK_S_V2) + .Build(); - auto value = test::GetValueForConfig<BinaryPrimitive>(&sparse_table, "android:string/foo_0", - sparse_config); - ASSERT_THAT(value, NotNull()); - EXPECT_EQ(0u, value->value.data); + const ConfigDescription sparse_config = test::ParseConfigOrDie("en-rGB"); + auto table_in = BuildTableWithSparseEntries(context.get(), sparse_config, 0.25f); - ASSERT_THAT(test::GetValueForConfig<BinaryPrimitive>(&sparse_table, "android:string/foo_1", - sparse_config), - IsNull()); + TableFlattenerOptions options; + options.sparse_entries = SparseEntriesMode::Forced; - value = test::GetValueForConfig<BinaryPrimitive>(&sparse_table, "android:string/foo_4", - sparse_config); - ASSERT_THAT(value, NotNull()); - EXPECT_EQ(4u, value->value.data); + std::string no_sparse_contents; + ASSERT_TRUE(Flatten(context.get(), {}, table_in.get(), &no_sparse_contents)); + + std::string sparse_contents; + ASSERT_TRUE(Flatten(context.get(), options, table_in.get(), &sparse_contents)); + + EXPECT_GT(no_sparse_contents.size(), sparse_contents.size()); + + CheckSparseEntries(context.get(), sparse_config, sparse_contents); } -TEST_F(TableFlattenerTest, FlattenSparseEntryWithConfigSdkVersionSV2) { +TEST_F(TableFlattenerTest, FlattenSparseEntryWithMinSdkBeforeSV2) { + std::unique_ptr<IAaptContext> context = test::ContextBuilder() + .SetCompilationPackage("android") + .SetPackageId(0x01) + .SetMinSdkVersion(SDK_LOLLIPOP) + .Build(); + + const ConfigDescription sparse_config = test::ParseConfigOrDie("en-rGB"); + auto table_in = BuildTableWithSparseEntries(context.get(), sparse_config, 0.25f); + + TableFlattenerOptions options; + options.sparse_entries = SparseEntriesMode::Enabled; + + std::string no_sparse_contents; + ASSERT_TRUE(Flatten(context.get(), {}, table_in.get(), &no_sparse_contents)); + + std::string sparse_contents; + ASSERT_TRUE(Flatten(context.get(), options, table_in.get(), &sparse_contents)); + + EXPECT_EQ(no_sparse_contents.size(), sparse_contents.size()); +} + +TEST_F(TableFlattenerTest, FlattenSparseEntryWithMinSdkBeforeSV2AndConfigSdkVersionSV2) { std::unique_ptr<IAaptContext> context = test::ContextBuilder() .SetCompilationPackage("android") .SetPackageId(0x01) @@ -391,7 +441,7 @@ TEST_F(TableFlattenerTest, FlattenSparseEntryWithConfigSdkVersionSV2) { EXPECT_EQ(no_sparse_contents.size(), sparse_contents.size()); } -TEST_F(TableFlattenerTest, FlattenSparseEntryRegardlessOfMinSdkWhenForced) { +TEST_F(TableFlattenerTest, FlattenSparseEntryWithMinSdkBeforeSV2AndForced) { std::unique_ptr<IAaptContext> context = test::ContextBuilder() .SetCompilationPackage("android") .SetPackageId(0x01) @@ -410,7 +460,7 @@ TEST_F(TableFlattenerTest, FlattenSparseEntryRegardlessOfMinSdkWhenForced) { std::string sparse_contents; ASSERT_TRUE(Flatten(context.get(), options, table_in.get(), &sparse_contents)); - EXPECT_GT(no_sparse_contents.size(), sparse_contents.size()); + EXPECT_EQ(no_sparse_contents.size(), sparse_contents.size()); } TEST_F(TableFlattenerTest, FlattenSparseEntryWithSdkVersionNotSet) { @@ -429,28 +479,28 @@ TEST_F(TableFlattenerTest, FlattenSparseEntryWithSdkVersionNotSet) { std::string sparse_contents; ASSERT_TRUE(Flatten(context.get(), options, table_in.get(), &sparse_contents)); - EXPECT_GT(no_sparse_contents.size(), sparse_contents.size()); + EXPECT_EQ(no_sparse_contents.size(), sparse_contents.size()); +} - // Attempt to parse the sparse contents. +TEST_F(TableFlattenerTest, FlattenSparseEntryWithSdkVersionNotSetAndForced) { + std::unique_ptr<IAaptContext> context = + test::ContextBuilder().SetCompilationPackage("android").SetPackageId(0x01).Build(); - ResourceTable sparse_table; - BinaryResourceParser parser(context->GetDiagnostics(), &sparse_table, Source("test.arsc"), - sparse_contents.data(), sparse_contents.size()); - ASSERT_TRUE(parser.Parse()); + const ConfigDescription sparse_config = test::ParseConfigOrDie("en-rGB"); + auto table_in = BuildTableWithSparseEntries(context.get(), sparse_config, 0.25f); - auto value = test::GetValueForConfig<BinaryPrimitive>(&sparse_table, "android:string/foo_0", - sparse_config); - ASSERT_THAT(value, NotNull()); - EXPECT_EQ(0u, value->value.data); + TableFlattenerOptions options; + options.sparse_entries = SparseEntriesMode::Forced; - ASSERT_THAT(test::GetValueForConfig<BinaryPrimitive>(&sparse_table, "android:string/foo_1", - sparse_config), - IsNull()); + std::string no_sparse_contents; + ASSERT_TRUE(Flatten(context.get(), {}, table_in.get(), &no_sparse_contents)); - value = test::GetValueForConfig<BinaryPrimitive>(&sparse_table, "android:string/foo_4", - sparse_config); - ASSERT_THAT(value, NotNull()); - EXPECT_EQ(4u, value->value.data); + std::string sparse_contents; + ASSERT_TRUE(Flatten(context.get(), options, table_in.get(), &sparse_contents)); + + EXPECT_GT(no_sparse_contents.size(), sparse_contents.size()); + + CheckSparseEntries(context.get(), sparse_config, sparse_contents); } TEST_F(TableFlattenerTest, DoNotUseSparseEntryForDenseConfig) { diff --git a/tools/aapt2/format/proto/ProtoDeserialize.cpp b/tools/aapt2/format/proto/ProtoDeserialize.cpp index 91ec3485ac3b..b8936553a193 100644 --- a/tools/aapt2/format/proto/ProtoDeserialize.cpp +++ b/tools/aapt2/format/proto/ProtoDeserialize.cpp @@ -640,6 +640,7 @@ bool DeserializeCompiledFileFromPb(const pb::internal::CompiledFile& pb_file, out_file->name = name_ref.ToResourceName(); out_file->source.path = pb_file.source_path(); out_file->type = DeserializeFileReferenceTypeFromPb(pb_file.type()); + out_file->uses_readwrite_feature_flags = pb_file.uses_readwrite_feature_flags(); out_file->flag_status = (FlagStatus)pb_file.flag_status(); if (!pb_file.flag_name().empty()) { diff --git a/tools/aapt2/format/proto/ProtoSerialize.cpp b/tools/aapt2/format/proto/ProtoSerialize.cpp index fcc77d5a9d6d..da99c4f5917c 100644 --- a/tools/aapt2/format/proto/ProtoSerialize.cpp +++ b/tools/aapt2/format/proto/ProtoSerialize.cpp @@ -767,6 +767,7 @@ void SerializeCompiledFileToPb(const ResourceFile& file, pb::internal::CompiledF out_file->set_flag_negated(file.flag->negated); out_file->set_flag_name(file.flag->name); } + out_file->set_uses_readwrite_feature_flags(file.uses_readwrite_feature_flags); for (const SourcedResourceName& exported : file.exported_symbols) { pb::internal::CompiledFile_Symbol* pb_symbol = out_file->add_exported_symbol(); diff --git a/tools/aapt2/link/FlaggedResources_test.cpp b/tools/aapt2/link/FlaggedResources_test.cpp index 47a71fe36e9f..4dcb8507fa45 100644 --- a/tools/aapt2/link/FlaggedResources_test.cpp +++ b/tools/aapt2/link/FlaggedResources_test.cpp @@ -226,9 +226,11 @@ TEST_F(FlaggedResourcesTest, ReadWriteFlagInXmlGetsFlagged) { } } } + ASSERT_TRUE(found) << "No entry for layout1 at v36 with FLAG_USES_FEATURE_FLAGS bit set"; - // There should only be 1 entry that has the FLAG_USES_FEATURE_FLAGS bit of flags set to 1 - ASSERT_EQ(fields_flagged, 1); + // There should only be 2 entry that has the FLAG_USES_FEATURE_FLAGS bit of flags set to 1, the + // three versions of the layout file that has flags + ASSERT_EQ(fields_flagged, 3); } } // namespace aapt diff --git a/tools/aapt2/link/FlaggedXmlVersioner.cpp b/tools/aapt2/link/FlaggedXmlVersioner.cpp index 8a3337c446cb..626cae73bfa2 100644 --- a/tools/aapt2/link/FlaggedXmlVersioner.cpp +++ b/tools/aapt2/link/FlaggedXmlVersioner.cpp @@ -35,10 +35,6 @@ class AllDisabledFlagsVisitor : public xml::Visitor { VisitChildren(node); } - bool HadFlags() const { - return had_flags_; - } - private: bool FixupOrShouldRemove(const std::unique_ptr<xml::Node>& node) { if (auto* el = NodeCast<Element>(node.get())) { @@ -47,7 +43,6 @@ class AllDisabledFlagsVisitor : public xml::Visitor { return false; } - had_flags_ = true; // This class assumes all flags are disabled so we want to remove any elements behind flags // unless the flag specification is negated. In the negated case we remove the featureFlag // attribute because we have already determined whether we are keeping the element or not. @@ -62,56 +57,27 @@ class AllDisabledFlagsVisitor : public xml::Visitor { return false; } - - bool had_flags_ = false; -}; - -// An xml visitor that goes through the a doc and determines if any elements are behind a flag. -class FindFlagsVisitor : public xml::Visitor { - public: - void Visit(xml::Element* node) override { - if (had_flags_) { - return; - } - auto* attr = node->FindAttribute(xml::kSchemaAndroid, xml::kAttrFeatureFlag); - if (attr != nullptr) { - had_flags_ = true; - return; - } - VisitChildren(node); - } - - bool HadFlags() const { - return had_flags_; - } - - bool had_flags_ = false; }; std::vector<std::unique_ptr<xml::XmlResource>> FlaggedXmlVersioner::Process(IAaptContext* context, xml::XmlResource* doc) { std::vector<std::unique_ptr<xml::XmlResource>> docs; - if ((static_cast<ApiVersion>(doc->file.config.sdkVersion) >= SDK_BAKLAVA) || - (static_cast<ApiVersion>(context->GetMinSdkVersion()) >= SDK_BAKLAVA)) { + if (!doc->file.uses_readwrite_feature_flags) { + docs.push_back(doc->Clone()); + } else if ((static_cast<ApiVersion>(doc->file.config.sdkVersion) >= SDK_BAKLAVA) || + (static_cast<ApiVersion>(context->GetMinSdkVersion()) >= SDK_BAKLAVA)) { // Support for read/write flags was added in baklava so if the doc will only get used on // baklava or later we can just return the original doc. docs.push_back(doc->Clone()); - FindFlagsVisitor visitor; - doc->root->Accept(&visitor); - docs.back()->file.uses_readwrite_feature_flags = visitor.HadFlags(); } else { auto preBaklavaVersion = doc->Clone(); AllDisabledFlagsVisitor visitor; preBaklavaVersion->root->Accept(&visitor); - preBaklavaVersion->file.uses_readwrite_feature_flags = false; docs.push_back(std::move(preBaklavaVersion)); - if (visitor.HadFlags()) { - auto baklavaVersion = doc->Clone(); - baklavaVersion->file.config.sdkVersion = SDK_BAKLAVA; - baklavaVersion->file.uses_readwrite_feature_flags = true; - docs.push_back(std::move(baklavaVersion)); - } + auto baklavaVersion = doc->Clone(); + baklavaVersion->file.config.sdkVersion = SDK_BAKLAVA; + docs.push_back(std::move(baklavaVersion)); } return docs; } diff --git a/tools/aapt2/link/FlaggedXmlVersioner_test.cpp b/tools/aapt2/link/FlaggedXmlVersioner_test.cpp index 0c1314f165cc..0dc464253385 100644 --- a/tools/aapt2/link/FlaggedXmlVersioner_test.cpp +++ b/tools/aapt2/link/FlaggedXmlVersioner_test.cpp @@ -101,6 +101,7 @@ TEST_F(FlaggedXmlVersionerTest, PreBaklavaGetsSplit) { <TextView android:featureFlag="package.flag" /><TextView /><TextView /> </LinearLayout>)"); doc->file.config.sdkVersion = SDK_GINGERBREAD; + doc->file.uses_readwrite_feature_flags = true; FlaggedXmlVersioner versioner; auto results = versioner.Process(context_.get(), doc.get()); @@ -131,6 +132,7 @@ TEST_F(FlaggedXmlVersionerTest, NoVersionGetsSplit) { <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"> <TextView android:featureFlag="package.flag" /><TextView /><TextView /> </LinearLayout>)"); + doc->file.uses_readwrite_feature_flags = true; FlaggedXmlVersioner versioner; auto results = versioner.Process(context_.get(), doc.get()); @@ -162,6 +164,7 @@ TEST_F(FlaggedXmlVersionerTest, NegatedFlagAttributeRemoved) { <TextView android:featureFlag="!package.flag" /><TextView /><TextView /> </LinearLayout>)"); doc->file.config.sdkVersion = SDK_GINGERBREAD; + doc->file.uses_readwrite_feature_flags = true; FlaggedXmlVersioner versioner; auto results = versioner.Process(context_.get(), doc.get()); @@ -192,6 +195,7 @@ TEST_F(FlaggedXmlVersionerTest, NegatedFlagAttributeRemovedNoSpecifiedVersion) { <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"> <TextView android:featureFlag="!package.flag" /><TextView /><TextView /> </LinearLayout>)"); + doc->file.uses_readwrite_feature_flags = true; FlaggedXmlVersioner versioner; auto results = versioner.Process(context_.get(), doc.get()); diff --git a/tools/aapt2/link/TableMerger.cpp b/tools/aapt2/link/TableMerger.cpp index 1d4adc4a57d8..17f332397317 100644 --- a/tools/aapt2/link/TableMerger.cpp +++ b/tools/aapt2/link/TableMerger.cpp @@ -295,6 +295,8 @@ bool TableMerger::DoMerge(const android::Source& src, ResourceTablePackage* src_ dst_config_value = dst_entry->FindOrCreateValue(src_config_value->config, src_config_value->product); } + dst_config_value->uses_readwrite_feature_flags |= + src_config_value->uses_readwrite_feature_flags; // Continue if we're taking the new resource. CloningValueTransformer cloner(&main_table_->string_pool); @@ -378,12 +380,13 @@ bool TableMerger::MergeFile(const ResourceFile& file_desc, bool overlay, io::IFi file_ref->file = file; file_ref->SetFlagStatus(file_desc.flag_status); file_ref->SetFlag(file_desc.flag); - ResourceTablePackage* pkg = table.FindOrCreatePackage(file_desc.name.package); - pkg->FindOrCreateType(file_desc.name.type) - ->FindOrCreateEntry(file_desc.name.entry) - ->FindOrCreateValue(file_desc.config, {}) - ->value = std::move(file_ref); + ResourceConfigValue* config_value = pkg->FindOrCreateType(file_desc.name.type) + ->FindOrCreateEntry(file_desc.name.entry) + ->FindOrCreateValue(file_desc.config, {}); + + config_value->value = std::move(file_ref); + config_value->uses_readwrite_feature_flags = file_desc.uses_readwrite_feature_flags; return DoMerge(file->GetSource(), pkg, false /*mangle*/, overlay /*overlay*/, true /*allow_new*/); } diff --git a/tools/aapt2/readme.md b/tools/aapt2/readme.md index 6bdbaaed9858..413f817ea8fd 100644 --- a/tools/aapt2/readme.md +++ b/tools/aapt2/readme.md @@ -5,6 +5,10 @@ 2017. This README will be updated more frequently in the future. - Added a new flag `--no-compress-fonts`. This can significantly speed up loading fonts from APK assets, at the cost of increasing the storage size of the APK. +- Changed the behavior of `--enable-sparse-encoding`. Sparse encoding is only applied if the + minSdkVersion is >= 32. +- Changed the behavior of `--force-sparse-encoding`. Sparse encoding is only applied if the + minSdkVersion is >= 32 or is not set. ## Version 2.19 - Added navigation resource type. |