diff options
481 files changed, 15140 insertions, 4360 deletions
diff --git a/apct-tests/perftests/core/src/android/permission/AppOpsPerfTest.kt b/apct-tests/perftests/core/src/android/permission/AppOpsPerfTest.kt index 2af878e60db7..daf991c313c8 100644 --- a/apct-tests/perftests/core/src/android/permission/AppOpsPerfTest.kt +++ b/apct-tests/perftests/core/src/android/permission/AppOpsPerfTest.kt @@ -17,8 +17,7 @@ package android.permission import android.app.AppOpsManager import android.content.Context -import androidx.benchmark.BenchmarkState -import androidx.benchmark.junit4.BenchmarkRule +import android.perftests.utils.PerfStatusReporter import androidx.test.core.app.ApplicationProvider import androidx.test.filters.LargeTest import org.junit.Before @@ -34,7 +33,7 @@ import org.junit.Test * these APIs should be monitored closely for performance. */ class AppOpsPerfTest { - @get:Rule val mBenchmarkRule: BenchmarkRule = BenchmarkRule() + @get:Rule val perfStatusReporter = PerfStatusReporter() private lateinit var appOpsManager: AppOpsManager private lateinit var opPackageName: String private var opPackageUid: Int = 0 @@ -49,7 +48,7 @@ class AppOpsPerfTest { @Test fun testNoteOp() { - val state: BenchmarkState = mBenchmarkRule.getState() + val state = perfStatusReporter.benchmarkState while (state.keepRunning()) { appOpsManager.noteOp( AppOpsManager.OPSTR_FINE_LOCATION, @@ -63,7 +62,7 @@ class AppOpsPerfTest { @Test fun testUnsafeCheckOp() { - val state: BenchmarkState = mBenchmarkRule.getState() + val state = perfStatusReporter.benchmarkState while (state.keepRunning()) { appOpsManager.unsafeCheckOp( AppOpsManager.OPSTR_FINE_LOCATION, diff --git a/core/api/current.txt b/core/api/current.txt index bbb3932f0b17..9d506b4971e0 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -19910,7 +19910,6 @@ package android.hardware.camera2 { } @FlaggedApi("com.android.internal.camera.flags.concert_mode_api") public final class ExtensionCaptureRequest { - ctor public ExtensionCaptureRequest(); field @FlaggedApi("com.android.internal.camera.flags.concert_mode_api") @NonNull public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Boolean> EFV_AUTO_ZOOM; field @FlaggedApi("com.android.internal.camera.flags.concert_mode_api") @NonNull public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Float> EFV_MAX_PADDING_ZOOM_FACTOR; field @FlaggedApi("com.android.internal.camera.flags.concert_mode_api") @NonNull public static final android.hardware.camera2.CaptureRequest.Key<java.lang.Float> EFV_PADDING_ZOOM_FACTOR; @@ -19923,7 +19922,6 @@ package android.hardware.camera2 { } @FlaggedApi("com.android.internal.camera.flags.concert_mode_api") public final class ExtensionCaptureResult { - ctor public ExtensionCaptureResult(); field @FlaggedApi("com.android.internal.camera.flags.concert_mode_api") @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Boolean> EFV_AUTO_ZOOM; field @FlaggedApi("com.android.internal.camera.flags.concert_mode_api") @NonNull public static final android.hardware.camera2.CaptureResult.Key<int[]> EFV_AUTO_ZOOM_PADDING_REGION; field @FlaggedApi("com.android.internal.camera.flags.concert_mode_api") @NonNull public static final android.hardware.camera2.CaptureResult.Key<java.lang.Float> EFV_MAX_PADDING_ZOOM_FACTOR; diff --git a/core/api/lint-baseline.txt b/core/api/lint-baseline.txt index 1b0da055038d..e6a7ca5bf54c 100644 --- a/core/api/lint-baseline.txt +++ b/core/api/lint-baseline.txt @@ -245,14 +245,6 @@ BroadcastBehavior: android.telephony.euicc.EuiccManager#ACTION_NOTIFY_CARRIER_SE Field 'ACTION_NOTIFY_CARRIER_SETUP_INCOMPLETE' is missing @BroadcastBehavior -CompileTimeConstant: android.hardware.camera2.ExtensionCaptureRequest#EFV_STABILIZATION_MODE_GIMBAL: - All constants must be defined at compile time: android.hardware.camera2.ExtensionCaptureRequest#EFV_STABILIZATION_MODE_GIMBAL -CompileTimeConstant: android.hardware.camera2.ExtensionCaptureRequest#EFV_STABILIZATION_MODE_LOCKED: - All constants must be defined at compile time: android.hardware.camera2.ExtensionCaptureRequest#EFV_STABILIZATION_MODE_LOCKED -CompileTimeConstant: android.hardware.camera2.ExtensionCaptureRequest#EFV_STABILIZATION_MODE_OFF: - All constants must be defined at compile time: android.hardware.camera2.ExtensionCaptureRequest#EFV_STABILIZATION_MODE_OFF - - DeprecationMismatch: android.accounts.AccountManager#newChooseAccountIntent(android.accounts.Account, java.util.ArrayList<android.accounts.Account>, String[], boolean, String, String, String[], android.os.Bundle): Method android.accounts.AccountManager.newChooseAccountIntent(android.accounts.Account, java.util.ArrayList<android.accounts.Account>, String[], boolean, String, String, String[], android.os.Bundle): @Deprecated annotation (present) and @deprecated doc tag (not present) do not match DeprecationMismatch: android.app.Activity#enterPictureInPictureMode(): @@ -1095,14 +1087,6 @@ RequiresPermission: android.webkit.WebSettings#setGeolocationEnabled(boolean): Method 'setGeolocationEnabled' documentation mentions permissions without declaring @RequiresPermission -StaticUtils: ExtensionCaptureRequest: - Fully-static utility classes must not have constructor -StaticUtils: android.hardware.camera2.ExtensionCaptureRequest: - Fully-static utility classes must not have constructor -StaticUtils: android.hardware.camera2.ExtensionCaptureResult: - Fully-static utility classes must not have constructor - - Todo: android.hardware.camera2.params.StreamConfigurationMap: Documentation mentions 'TODO' Todo: android.provider.ContactsContract.RawContacts#newEntityIterator(android.database.Cursor): @@ -1462,14 +1446,6 @@ UnflaggedApi: android.graphics.text.PositionedGlyphs#getItalicOverride(int): UnflaggedApi: android.graphics.text.PositionedGlyphs#getWeightOverride(int): New API must be flagged with @FlaggedApi: method android.graphics.text.PositionedGlyphs.getWeightOverride(int) -UnflaggedApi: android.hardware.camera2.ExtensionCaptureRequest: - New API must be flagged with @FlaggedApi: class android.hardware.camera2.ExtensionCaptureRequest -UnflaggedApi: android.hardware.camera2.ExtensionCaptureRequest#ExtensionCaptureRequest(): - New API must be flagged with @FlaggedApi: constructor android.hardware.camera2.ExtensionCaptureRequest() -UnflaggedApi: android.hardware.camera2.ExtensionCaptureResult: - New API must be flagged with @FlaggedApi: class android.hardware.camera2.ExtensionCaptureResult -UnflaggedApi: android.hardware.camera2.ExtensionCaptureResult#ExtensionCaptureResult(): - New API must be flagged with @FlaggedApi: constructor android.hardware.camera2.ExtensionCaptureResult() UnflaggedApi: android.media.MediaRoute2Info#TYPE_REMOTE_CAR: New API must be flagged with @FlaggedApi: field android.media.MediaRoute2Info.TYPE_REMOTE_CAR UnflaggedApi: android.media.MediaRoute2Info#TYPE_REMOTE_COMPUTER: diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 14ae3f543436..d03dd1623074 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -46,6 +46,7 @@ package android { field public static final String REMAP_MODIFIER_KEYS = "android.permission.REMAP_MODIFIER_KEYS"; field public static final String REMOVE_TASKS = "android.permission.REMOVE_TASKS"; field public static final String REQUEST_UNIQUE_ID_ATTESTATION = "android.permission.REQUEST_UNIQUE_ID_ATTESTATION"; + field public static final String RESERVED_FOR_TESTING_SIGNATURE = "android.permission.RESERVED_FOR_TESTING_SIGNATURE"; field public static final String RESET_APP_ERRORS = "android.permission.RESET_APP_ERRORS"; field public static final String REVOKE_POST_NOTIFICATIONS_WITHOUT_KILL = "android.permission.REVOKE_POST_NOTIFICATIONS_WITHOUT_KILL"; field public static final String SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS = "android.permission.SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS"; diff --git a/core/api/test-lint-baseline.txt b/core/api/test-lint-baseline.txt index 6cc71e5450ae..b4a3abc4ad22 100644 --- a/core/api/test-lint-baseline.txt +++ b/core/api/test-lint-baseline.txt @@ -1973,6 +1973,8 @@ Todo: android.window.WindowContainerTransaction#setActivityWindowingMode(android UnflaggedApi: android.Manifest.permission#MANAGE_REMOTE_AUTH: New API must be flagged with @FlaggedApi: field android.Manifest.permission.MANAGE_REMOTE_AUTH +UnflaggedApi: android.Manifest.permission#RESERVED_FOR_TESTING_SIGNATURE: + New API must be flagged with @FlaggedApi: field android.Manifest.permission.RESERVED_FOR_TESTING_SIGNATURE UnflaggedApi: android.Manifest.permission#START_ACTIVITIES_FROM_SDK_SANDBOX: New API must be flagged with @FlaggedApi: field android.Manifest.permission.START_ACTIVITIES_FROM_SDK_SANDBOX UnflaggedApi: android.Manifest.permission#USE_REMOTE_AUTH: diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index 79e2bd437c3e..1c247a6a8b39 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -9291,11 +9291,11 @@ public class Activity extends ContextThemeWrapper if (DEBUG_LIFECYCLE) Slog.v(TAG, "dispatchMultiWindowModeChanged " + this + ": " + isInMultiWindowMode + " " + newConfig); + mIsInMultiWindowMode = isInMultiWindowMode; mFragments.dispatchMultiWindowModeChanged(isInMultiWindowMode, newConfig); if (mWindow != null) { mWindow.onMultiWindowModeChanged(); } - mIsInMultiWindowMode = isInMultiWindowMode; onMultiWindowModeChanged(isInMultiWindowMode, newConfig); } @@ -9304,11 +9304,11 @@ public class Activity extends ContextThemeWrapper if (DEBUG_LIFECYCLE) Slog.v(TAG, "dispatchPictureInPictureModeChanged " + this + ": " + isInPictureInPictureMode + " " + newConfig); + mIsInPictureInPictureMode = isInPictureInPictureMode; mFragments.dispatchPictureInPictureModeChanged(isInPictureInPictureMode, newConfig); if (mWindow != null) { mWindow.onPictureInPictureModeChanged(isInPictureInPictureMode); } - mIsInPictureInPictureMode = isInPictureInPictureMode; onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig); } diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java index 97852528d014..83c3bf6eda76 100644 --- a/core/java/android/app/ActivityManagerInternal.java +++ b/core/java/android/app/ActivityManagerInternal.java @@ -477,6 +477,11 @@ public abstract class ActivityManagerInternal { */ public static final int OOM_ADJ_REASON_COMPONENT_DISABLED = 22; + /** + * Oom Adj Reason: Follow up update for time sensitive state evaluations. + */ + public static final int OOM_ADJ_REASON_FOLLOW_UP = 23; + @IntDef(prefix = {"OOM_ADJ_REASON_"}, value = { OOM_ADJ_REASON_NONE, OOM_ADJ_REASON_ACTIVITY, @@ -501,6 +506,7 @@ public abstract class ActivityManagerInternal { OOM_ADJ_REASON_EXECUTING_SERVICE, OOM_ADJ_REASON_RESTRICTION_CHANGE, OOM_ADJ_REASON_COMPONENT_DISABLED, + OOM_ADJ_REASON_FOLLOW_UP, }) @Retention(RetentionPolicy.SOURCE) public @interface OomAdjReason {} diff --git a/core/java/android/app/assist/AssistContent.java b/core/java/android/app/assist/AssistContent.java index e5316bc05749..a48868906487 100644 --- a/core/java/android/app/assist/AssistContent.java +++ b/core/java/android/app/assist/AssistContent.java @@ -34,12 +34,22 @@ public class AssistContent implements Parcelable { } /** + * Create an AssistContent with extras initialized. + * * @hide + */ + public AssistContent(@android.annotation.NonNull Bundle extras) { + mExtras = extras; + } + + /** * Called by {@link android.app.ActivityThread} to set the default Intent based on * {@link android.app.Activity#getIntent Activity.getIntent}. * * <p>Automatically populates {@link #mUri} if that Intent is an {@link Intent#ACTION_VIEW} * of a web (http or https scheme) URI.</p> + * + * @hide */ public void setDefaultIntent(Intent intent) { mIntent = intent; diff --git a/core/java/android/app/contextualsearch/flags.aconfig b/core/java/android/app/contextualsearch/flags.aconfig index 3385b2b42074..3b0c86795738 100644 --- a/core/java/android/app/contextualsearch/flags.aconfig +++ b/core/java/android/app/contextualsearch/flags.aconfig @@ -7,3 +7,9 @@ flag { description: "Flag to enable the service" bug: "309689654" } +flag { + name: "enable_token_refresh" + namespace: "machine_learning" + description: "Flag to refresh the token to the callback" + bug: "309689654" +}
\ No newline at end of file diff --git a/core/java/android/companion/ObservingDevicePresenceRequest.java b/core/java/android/companion/ObservingDevicePresenceRequest.java index 11ea735dff4f..3150b8735033 100644 --- a/core/java/android/companion/ObservingDevicePresenceRequest.java +++ b/core/java/android/companion/ObservingDevicePresenceRequest.java @@ -180,6 +180,9 @@ public final class ObservingDevicePresenceRequest implements Parcelable { * <p>Calling apps must use either this API or {@link #setAssociationId(int)}, * but not both.</p> * + * <p>Calling app must hold the + * {@link AssociationRequest#DEVICE_PROFILE_AUTOMOTIVE_PROJECTION} profile.</p> + * * @param uuid The ParcelUuid for observing device presence. */ @NonNull diff --git a/core/java/android/hardware/camera2/ExtensionCaptureRequest.java b/core/java/android/hardware/camera2/ExtensionCaptureRequest.java index c33956b59f2f..b681ce40dfd9 100644 --- a/core/java/android/hardware/camera2/ExtensionCaptureRequest.java +++ b/core/java/android/hardware/camera2/ExtensionCaptureRequest.java @@ -18,7 +18,6 @@ package android.hardware.camera2; import android.annotation.FlaggedApi; import android.annotation.NonNull; -import android.hardware.camera2.CameraCharacteristics; import android.hardware.camera2.CaptureRequest; import android.hardware.camera2.CaptureRequest.Key; import android.hardware.camera2.impl.ExtensionKey; @@ -43,6 +42,9 @@ import com.android.internal.camera.flags.Flags; @FlaggedApi(Flags.FLAG_CONCERT_MODE_API) public final class ExtensionCaptureRequest { + /** To avoid exposing constructor */ + private ExtensionCaptureRequest() {} + /** * <p>Used to apply an additional digital zoom factor for the * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY } diff --git a/core/java/android/hardware/camera2/ExtensionCaptureResult.java b/core/java/android/hardware/camera2/ExtensionCaptureResult.java index 95feb2fd268a..b7ba78cb34b5 100644 --- a/core/java/android/hardware/camera2/ExtensionCaptureResult.java +++ b/core/java/android/hardware/camera2/ExtensionCaptureResult.java @@ -18,7 +18,6 @@ package android.hardware.camera2; import android.annotation.FlaggedApi; import android.annotation.NonNull; -import android.hardware.camera2.CameraCharacteristics; import android.hardware.camera2.CameraExtensionCharacteristics; import android.hardware.camera2.CaptureResult; import android.hardware.camera2.CaptureResult.Key; @@ -45,6 +44,9 @@ import com.android.internal.camera.flags.Flags; @FlaggedApi(Flags.FLAG_CONCERT_MODE_API) public final class ExtensionCaptureResult { + /** To avoid exposing constructor */ + private ExtensionCaptureResult() {} + /** * <p>The padding region for the * {@link android.hardware.camera2.CameraExtensionCharacteristics#EXTENSION_EYES_FREE_VIDEOGRAPHY } diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceSetupImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceSetupImpl.java index 8898a4c37446..df057a1489f0 100644 --- a/core/java/android/hardware/camera2/impl/CameraDeviceSetupImpl.java +++ b/core/java/android/hardware/camera2/impl/CameraDeviceSetupImpl.java @@ -105,7 +105,8 @@ public class CameraDeviceSetupImpl extends CameraDevice.CameraDeviceSetup { try { return cameraService.isSessionConfigurationWithParametersSupported(mCameraId, - mTargetSdkVersion, config, mContext.getDeviceId(), + mTargetSdkVersion, config, + mContext.getDeviceId(), mCameraManager.getDevicePolicyFromContext(mContext)); } catch (ServiceSpecificException e) { throw ExceptionUtils.throwAsPublicException(e); diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java index dda52dd5e8cc..ebcc37113d1a 100644 --- a/core/java/android/hardware/camera2/params/OutputConfiguration.java +++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java @@ -638,13 +638,15 @@ public final class OutputConfiguration implements Parcelable { /** * Create a list of {@link OutputConfiguration} instances for a - * {@link android.hardware.camera2.params.MultiResolutionImageReader}. + * {@link MultiResolutionImageReader}. * * <p>This method can be used to create query OutputConfigurations for a * MultiResolutionImageReader that can be included in a SessionConfiguration passed into - * {@link CameraDeviceSetup#isSessionConfigurationSupported} before opening and setting up - * a camera device in full, at which point {@link #setSurfacesForMultiResolutionOutput} - * can be used to link to the actual MultiResolutionImageReader.</p> + * {@link + * android.hardware.camera2.CameraDevice.CameraDeviceSetup#isSessionConfigurationSupported} + * before opening and setting up a camera device in full, at which point {@link + * #setSurfacesForMultiResolutionOutput} can be used to link to the actual + * MultiResolutionImageReader.</p> * * <p>This constructor takes same arguments used to create a {@link * MultiResolutionImageReader}: a collection of {@link MultiResolutionStreamInfo} @@ -655,12 +657,12 @@ public final class OutputConfiguration implements Parcelable { * @param format The format of the MultiResolutionImageReader. This must be one of the {@link * android.graphics.ImageFormat} or {@link android.graphics.PixelFormat} constants * supported by the camera device. Note that not all formats are supported, like - * {@link ImageFormat.NV21}. The supported multi-resolution reader format can be + * {@link ImageFormat#NV21}. The supported multi-resolution reader format can be * queried by {@link MultiResolutionStreamConfigurationMap#getOutputFormats}. * * @return The list of {@link OutputConfiguration} objects for a MultiResolutionImageReader. * - * @throws IllegaArgumentException If the {@code streams} is null or doesn't contain + * @throws IllegalArgumentException If the {@code streams} is null or doesn't contain * at least 2 items, or if {@code format} isn't a valid camera * format. * @@ -710,7 +712,7 @@ public final class OutputConfiguration implements Parcelable { * instances.</p> * * @param outputConfigurations The OutputConfiguration objects created by {@link - * #createInstancesFromMultiResolutionOutput} + * #createInstancesForMultiResolutionOutput} * @param multiResolutionImageReader The MultiResolutionImageReader object created from the same * MultiResolutionStreamInfo parameters as * {@code outputConfigurations}. @@ -759,31 +761,33 @@ public final class OutputConfiguration implements Parcelable { * the deferred Surface can be obtained: (1) from {@link android.view.SurfaceView} * by calling {@link android.view.SurfaceHolder#getSurface}, (2) from * {@link android.graphics.SurfaceTexture} via - * {@link android.view.Surface#Surface(android.graphics.SurfaceTexture)}, (3) from {@link - * android.media.MediaRecorder} via {@link android.media.MediaRecorder.getSurface} or {@link - * android.media.MediaCodec#createPersistentInputSurface}, or (4) from {@link - * android.media.MediaCodce} via {@link android.media.MediaCodec#createInputSurface} or {@link - * android.media.MediaCodec#createPersistentInputSource}.</p> + * {@link android.view.Surface#Surface(android.graphics.SurfaceTexture)}, (3) from + * {@link android.media.MediaRecorder} via {@link android.media.MediaRecorder#getSurface} or + * {@link android.media.MediaCodec#createPersistentInputSurface}, or (4) from + * {@link android.media.MediaCodec} via {@link android.media.MediaCodec#createInputSurface} or + * {@link android.media.MediaCodec#createPersistentInputSurface}.</p> * * <ul> * <li>Surfaces for {@link android.view.SurfaceView} and {@link android.graphics.SurfaceTexture} * can be deferred until after {@link CameraDevice#createCaptureSession}. In that case, the * output Surface must be set via {@link #addSurface}, and the Surface configuration must be - * finalized via {@link CameraCaptureSession#finalizeOutputConfiguration} before submitting + * finalized via {@link CameraCaptureSession#finalizeOutputConfigurations} before submitting * a request with the Surface target.</li> * <li>For all other target types, the output Surface must be set by {@link #addSurface}, - * and {@link CameraCaptureSession#finalizeOutputConfiguration} is not needed because the + * and {@link CameraCaptureSession#finalizeOutputConfigurations} is not needed because the * OutputConfiguration used to create the session will contain the actual Surface.</li> * </ul> * * <p>Before {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM Android V}, only {@link * android.view.SurfaceView} and {@link android.graphics.SurfaceTexture} are supported. Both * kind of outputs can be deferred until after {@link - * CameraDevice#createCaptureSessionByOutputConfiguration}.</p> + * CameraDevice#createCaptureSessionByOutputConfigurations}.</p> * * <p>An OutputConfiguration object created by this constructor can be used for {@link - * CameraDeviceSetup.isSessionConfigurationSupported} and {@link - * CameraDeviceSetup.getSessionCharacteristics} without having called {@link #addSurface}.</p> + * android.hardware.camera2.CameraDevice.CameraDeviceSetup#isSessionConfigurationSupported} + * and {@link + * android.hardware.camera2.CameraDevice.CameraDeviceSetup#getSessionCharacteristics} without + * having called {@link #addSurface}.</p> * * @param surfaceSize Size for the deferred surface. * @param klass a non-{@code null} {@link Class} object reference that indicates the source of @@ -849,8 +853,10 @@ public final class OutputConfiguration implements Parcelable { * before creating the capture session.</p> * * <p>An OutputConfiguration object created by this constructor can be used for {@link - * CameraDeviceSetup.isSessionConfigurationSupported} and {@link - * CameraDeviceSetup.getSessionCharacteristics} without having called {@link #addSurface}.</p> + * android.hardware.camera2.CameraDevice.CameraDeviceSetup#isSessionConfigurationSupported} + * and {@link + * android.hardware.camera2.CameraDevice.CameraDeviceSetup#getSessionCharacteristics} without + * having called {@link #addSurface}.</p> * * @param format The format of the ImageReader output. This must be one of the * {@link android.graphics.ImageFormat} or {@link android.graphics.PixelFormat} @@ -873,8 +879,10 @@ public final class OutputConfiguration implements Parcelable { * before creating the capture session.</p> * * <p>An OutputConfiguration object created by this constructor can be used for {@link - * CameraDeviceSetup.isSessionConfigurationSupported} and {@link - * CameraDeviceSetup.getSessionCharacteristics} without having called {@link #addSurface}.</p> + * android.hardware.camera2.CameraDevice.CameraDeviceSetup#isSessionConfigurationSupported} + * and {@link + * android.hardware.camera2.CameraDevice.CameraDeviceSetup#getSessionCharacteristics} without + * having called {@link #addSurface}.</p> * * @param surfaceGroupId A group ID for this output, used for sharing memory between multiple * outputs. @@ -899,8 +907,10 @@ public final class OutputConfiguration implements Parcelable { * before creating the capture session.</p> * * <p>An OutputConfiguration object created by this constructor can be used for {@link - * CameraDeviceSetup.isSessionConfigurationSupported} and {@link - * CameraDeviceSetup.getSessionCharacteristics} without having called {@link #addSurface}.</p> + * android.hardware.camera2.CameraDevice.CameraDeviceSetup#isSessionConfigurationSupported} + * and {@link + * android.hardware.camera2.CameraDevice.CameraDeviceSetup#getSessionCharacteristics} without + * having called {@link #addSurface}.</p> * * @param format The format of the ImageReader output. This must be one of the * {@link android.graphics.ImageFormat} or {@link android.graphics.PixelFormat} @@ -923,8 +933,10 @@ public final class OutputConfiguration implements Parcelable { * before creating the capture session.</p> * * <p>An OutputConfiguration object created by this constructor can be used for {@link - * CameraDeviceSetup.isSessionConfigurationSupported} and {@link - * CameraDeviceSetup.getSessionCharacteristics} without having called {@link #addSurface}.</p> + * android.hardware.camera2.CameraDevice.CameraDeviceSetup#isSessionConfigurationSupported} + * and {@link + * android.hardware.camera2.CameraDevice.CameraDeviceSetup#getSessionCharacteristics} without + * having called {@link #addSurface}.</p> * * @param surfaceGroupId A group ID for this output, used for sharing memory between multiple * outputs. @@ -1171,9 +1183,9 @@ public final class OutputConfiguration implements Parcelable { * <li>from {@link android.media.MediaRecorder} by calling * {@link android.media.MediaRecorder#getSurface} or {@link * android.media.MediaCodec#createPersistentInputSurface}</li> - * <li>from {@link android.media.MediaCodce} by calling - * {@link android.media.MediaCodec#createInputSurface} or {@link - * android.media.MediaCodec#createPersistentInputSource}</li> + * <li>from {@link android.media.MediaCodec} by calling + * {@link android.media.MediaCodec#createInputSurface} or + * {@link android.media.MediaCodec#createPersistentInputSurface()}</li> * </ul> * * <p> If the OutputConfiguration was constructed by {@link #OutputConfiguration(int, Size)} diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java index 3d7b714a2f5b..85197221e651 100644 --- a/core/java/android/hardware/display/DisplayManagerGlobal.java +++ b/core/java/android/hardware/display/DisplayManagerGlobal.java @@ -548,6 +548,20 @@ public final class DisplayManagerGlobal { } } + /** + * Request to power a display ON or OFF. + * @hide + */ + @RequiresPermission("android.permission.MANAGE_DISPLAYS") + public boolean requestDisplayPower(int displayId, boolean on) { + try { + return mDm.requestDisplayPower(displayId, on); + } catch (RemoteException ex) { + Log.e(TAG, "Error trying to request display power " + on, ex); + return false; + } + } + public void startWifiDisplayScan() { synchronized (mLock) { if (mWifiDisplayScanNestCount++ == 0) { diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl index 70efc6f2e33f..b7c02b0d0720 100644 --- a/core/java/android/hardware/display/IDisplayManager.aidl +++ b/core/java/android/hardware/display/IDisplayManager.aidl @@ -236,6 +236,10 @@ interface IDisplayManager { @EnforcePermission("MANAGE_DISPLAYS") void disableConnectedDisplay(int displayId); + // Request to power display ON or OFF. + @EnforcePermission("MANAGE_DISPLAYS") + boolean requestDisplayPower(int displayId, boolean on); + // Restricts display modes to specified modeIds. @EnforcePermission("RESTRICT_DISPLAY_MODES") void requestDisplayModes(in IBinder token, int displayId, in @nullable int[] modeIds); diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 05345d88f771..63f0b9ebca36 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -6101,6 +6101,15 @@ public final class Settings { public static final String POINTER_SPEED = "pointer_speed"; /** + * Pointer scale setting. + * + * <p>This float value represents the scale by which the size of the pointer increases. + * @hide + */ + @Readable + public static final String POINTER_SCALE = "pointer_scale"; + + /** * Touchpad pointer speed setting. * This is an integer value in a range between -7 and +7, so there are 15 possible values. * -7 = slowest @@ -6358,6 +6367,7 @@ public final class Settings { PRIVATE_SETTINGS.add(SIP_ASK_ME_EACH_TIME); PRIVATE_SETTINGS.add(POINTER_SPEED); PRIVATE_SETTINGS.add(POINTER_FILL_STYLE); + PRIVATE_SETTINGS.add(POINTER_SCALE); PRIVATE_SETTINGS.add(LOCK_TO_APP_ENABLED); PRIVATE_SETTINGS.add(EGG_MODE); PRIVATE_SETTINGS.add(SHOW_BATTERY_PERCENT); diff --git a/core/java/android/service/contentcapture/ContentCaptureService.java b/core/java/android/service/contentcapture/ContentCaptureService.java index e1965ef25562..405fe26a20b8 100644 --- a/core/java/android/service/contentcapture/ContentCaptureService.java +++ b/core/java/android/service/contentcapture/ContentCaptureService.java @@ -29,6 +29,7 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; import android.app.Service; +import android.app.assist.AssistContent; import android.content.ComponentName; import android.content.ContentCaptureOptions; import android.content.Intent; @@ -133,6 +134,16 @@ public abstract class ContentCaptureService extends Service { */ public static final String SERVICE_META_DATA = "android.content_capture"; + + /** + * Extras key to flag that the passed in {@link AssistContent} is sent only during Activity + * start. + * + * @hide + */ + public static final String ASSIST_CONTENT_ACTIVITY_START_KEY = "activity_start_assist_content"; + + private final LocalDataShareAdapterResourceManager mDataShareAdapterResourceManager = new LocalDataShareAdapterResourceManager(); diff --git a/core/java/android/view/PointerIcon.java b/core/java/android/view/PointerIcon.java index 7c2577fdf8e1..c30212657c57 100644 --- a/core/java/android/view/PointerIcon.java +++ b/core/java/android/view/PointerIcon.java @@ -193,6 +193,9 @@ public final class PointerIcon implements Parcelable { /** @hide */ public static final int POINTER_ICON_VECTOR_STYLE_FILL_END = POINTER_ICON_VECTOR_STYLE_FILL_BLUE; + /** @hide */ public static final float DEFAULT_POINTER_SCALE = 1f; + /** @hide */ public static final float LARGE_POINTER_SCALE = 2.5f; + @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) private final int mType; @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) @@ -253,7 +256,7 @@ public final class PointerIcon implements Parcelable { * @hide */ public static @NonNull PointerIcon getLoadedSystemIcon(@NonNull Context context, int type, - boolean useLargeIcons) { + boolean useLargeIcons, float pointerScale) { if (type == TYPE_NOT_SPECIFIED) { throw new IllegalStateException("Cannot load icon for type TYPE_NOT_SPECIFIED"); } @@ -268,13 +271,18 @@ public final class PointerIcon implements Parcelable { } final int defStyle; - // TODO(b/305193969): Use scaled vectors when large icons are requested. - if (useLargeIcons) { - defStyle = com.android.internal.R.style.LargePointer; - } else if (android.view.flags.Flags.enableVectorCursors()) { + if (android.view.flags.Flags.enableVectorCursorA11ySettings()) { defStyle = com.android.internal.R.style.VectorPointer; } else { - defStyle = com.android.internal.R.style.Pointer; + // TODO(b/346358375): Remove useLargeIcons and the legacy pointer styles when + // enableVectorCursorA11ySetting is rolled out. + if (useLargeIcons) { + defStyle = com.android.internal.R.style.LargePointer; + } else if (android.view.flags.Flags.enableVectorCursors()) { + defStyle = com.android.internal.R.style.VectorPointer; + } else { + defStyle = com.android.internal.R.style.Pointer; + } } TypedArray a = context.obtainStyledAttributes(null, com.android.internal.R.styleable.Pointer, @@ -286,11 +294,11 @@ public final class PointerIcon implements Parcelable { Log.w(TAG, "Missing theme resources for pointer icon type " + type); return type == TYPE_DEFAULT ? getSystemIcon(TYPE_NULL) - : getLoadedSystemIcon(context, TYPE_DEFAULT, useLargeIcons); + : getLoadedSystemIcon(context, TYPE_DEFAULT, useLargeIcons, pointerScale); } final PointerIcon icon = new PointerIcon(type); - icon.loadResource(context.getResources(), resourceId, context.getTheme()); + icon.loadResource(context.getResources(), resourceId, context.getTheme(), pointerScale); return icon; } @@ -353,7 +361,7 @@ public final class PointerIcon implements Parcelable { } PointerIcon icon = new PointerIcon(TYPE_CUSTOM); - icon.loadResource(resources, resourceId, null); + icon.loadResource(resources, resourceId, null, DEFAULT_POINTER_SCALE); return icon; } @@ -460,12 +468,13 @@ public final class PointerIcon implements Parcelable { } private BitmapDrawable getBitmapDrawableFromVectorDrawable(Resources resources, - VectorDrawable vectorDrawable) { + VectorDrawable vectorDrawable, float pointerScale) { // Ensure we pass the display metrics into the Bitmap constructor so that it is initialized // with the correct density. Bitmap bitmap = Bitmap.createBitmap(resources.getDisplayMetrics(), - vectorDrawable.getIntrinsicWidth(), - vectorDrawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888, true /* hasAlpha */); + (int) (vectorDrawable.getIntrinsicWidth() * pointerScale), + (int) (vectorDrawable.getIntrinsicHeight() * pointerScale), + Bitmap.Config.ARGB_8888, true /* hasAlpha */); Canvas canvas = new Canvas(bitmap); vectorDrawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight()); vectorDrawable.draw(canvas); @@ -473,7 +482,7 @@ public final class PointerIcon implements Parcelable { } private void loadResource(@NonNull Resources resources, @XmlRes int resourceId, - @Nullable Resources.Theme theme) { + @Nullable Resources.Theme theme, float pointerScale) { final XmlResourceParser parser = resources.getXml(resourceId); final int bitmapRes; final float hotSpotX; @@ -484,8 +493,10 @@ public final class PointerIcon implements Parcelable { final TypedArray a = resources.obtainAttributes( parser, com.android.internal.R.styleable.PointerIcon); bitmapRes = a.getResourceId(com.android.internal.R.styleable.PointerIcon_bitmap, 0); - hotSpotX = a.getDimension(com.android.internal.R.styleable.PointerIcon_hotSpotX, 0); - hotSpotY = a.getDimension(com.android.internal.R.styleable.PointerIcon_hotSpotY, 0); + hotSpotX = a.getDimension(com.android.internal.R.styleable.PointerIcon_hotSpotX, 0) + * pointerScale; + hotSpotY = a.getDimension(com.android.internal.R.styleable.PointerIcon_hotSpotY, 0) + * pointerScale; a.recycle(); } catch (Exception ex) { throw new IllegalArgumentException("Exception parsing pointer icon resource.", ex); @@ -534,7 +545,7 @@ public final class PointerIcon implements Parcelable { } if (isVectorAnimation) { drawableFrame = getBitmapDrawableFromVectorDrawable(resources, - (VectorDrawable) drawableFrame); + (VectorDrawable) drawableFrame, pointerScale); } mBitmapFrames[i - 1] = getBitmapFromDrawable((BitmapDrawable) drawableFrame); } @@ -542,7 +553,8 @@ public final class PointerIcon implements Parcelable { } if (drawable instanceof VectorDrawable) { mDrawNativeDropShadow = true; - drawable = getBitmapDrawableFromVectorDrawable(resources, (VectorDrawable) drawable); + drawable = getBitmapDrawableFromVectorDrawable(resources, (VectorDrawable) drawable, + pointerScale); } if (!(drawable instanceof BitmapDrawable)) { throw new IllegalArgumentException("<pointer-icon> bitmap attribute must " diff --git a/core/java/android/view/SurfaceControlRegistry.java b/core/java/android/view/SurfaceControlRegistry.java index 127d4a70a564..aa3654dd77ac 100644 --- a/core/java/android/view/SurfaceControlRegistry.java +++ b/core/java/android/view/SurfaceControlRegistry.java @@ -342,12 +342,14 @@ public class SurfaceControlRegistry { return false; } final boolean matchName = !sCallStackDebuggingMatchName.isEmpty(); - if (matchName && (name == null - || !sCallStackDebuggingMatchName.contains(name.toLowerCase()))) { - // Skip if target surface doesn't match requested surface + if (!matchName) { + return true; + } + if (name == null) { return false; } - return true; + return sCallStackDebuggingMatchName.contains(name.toLowerCase()) || + name.toLowerCase().contains(sCallStackDebuggingMatchName); } /** diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java index bcef37f6e0c4..d74867c31bb6 100644 --- a/core/java/android/view/contentcapture/ContentCaptureManager.java +++ b/core/java/android/view/contentcapture/ContentCaptureManager.java @@ -366,6 +366,14 @@ public final class ContentCaptureManager { "enable_content_protection_receiver"; /** + * Whether AssistContent snapshot should be sent on activity start. + * + * @hide + */ + public static final String DEVICE_CONFIG_ENABLE_ACTIVITY_START_ASSIST_CONTENT = + "enable_activity_start_assist_content"; + + /** * Sets the size of the in-memory ring buffer for the content protection flow. * * @hide diff --git a/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java b/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java index 55f2dee95a34..00262be06623 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java +++ b/core/java/com/android/internal/widget/remotecompose/core/CoreDocument.java @@ -30,7 +30,7 @@ public class CoreDocument { ArrayList<Operation> mOperations; RemoteComposeState mRemoteComposeState = new RemoteComposeState(); - + TimeVariables mTimeVariables = new TimeVariables(); // Semantic version of the document Version mVersion = new Version(0, 1, 0); @@ -70,6 +70,7 @@ public class CoreDocument { public void setWidth(int width) { this.mWidth = width; + mRemoteComposeState.setWindowWidth(width); } public int getHeight() { @@ -78,6 +79,8 @@ public class CoreDocument { public void setHeight(int height) { this.mHeight = height; + mRemoteComposeState.setWindowHeight(height); + } public RemoteComposeBuffer getBuffer() { @@ -111,21 +114,21 @@ public class CoreDocument { /** * Sets the way the player handles the content * - * @param scroll set the horizontal behavior (NONE|SCROLL_HORIZONTAL|SCROLL_VERTICAL) + * @param scroll set the horizontal behavior (NONE|SCROLL_HORIZONTAL|SCROLL_VERTICAL) * @param alignment set the alignment of the content (TOP|CENTER|BOTTOM|START|END) - * @param sizing set the type of sizing for the content (NONE|SIZING_LAYOUT|SIZING_SCALE) - * @param mode set the mode of sizing, either LAYOUT modes or SCALE modes - * the LAYOUT modes are: - * - LAYOUT_MATCH_PARENT - * - LAYOUT_WRAP_CONTENT - * or adding an horizontal mode and a vertical mode: - * - LAYOUT_HORIZONTAL_MATCH_PARENT - * - LAYOUT_HORIZONTAL_WRAP_CONTENT - * - LAYOUT_HORIZONTAL_FIXED - * - LAYOUT_VERTICAL_MATCH_PARENT - * - LAYOUT_VERTICAL_WRAP_CONTENT - * - LAYOUT_VERTICAL_FIXED - * The LAYOUT_*_FIXED modes will use the intrinsic document size + * @param sizing set the type of sizing for the content (NONE|SIZING_LAYOUT|SIZING_SCALE) + * @param mode set the mode of sizing, either LAYOUT modes or SCALE modes + * the LAYOUT modes are: + * - LAYOUT_MATCH_PARENT + * - LAYOUT_WRAP_CONTENT + * or adding an horizontal mode and a vertical mode: + * - LAYOUT_HORIZONTAL_MATCH_PARENT + * - LAYOUT_HORIZONTAL_WRAP_CONTENT + * - LAYOUT_HORIZONTAL_FIXED + * - LAYOUT_VERTICAL_MATCH_PARENT + * - LAYOUT_VERTICAL_WRAP_CONTENT + * - LAYOUT_VERTICAL_FIXED + * The LAYOUT_*_FIXED modes will use the intrinsic document size */ public void setRootContentBehavior(int scroll, int alignment, int sizing, int mode) { this.mContentScroll = scroll; @@ -138,8 +141,8 @@ public class CoreDocument { * Given dimensions w x h of where to paint the content, returns the corresponding scale factor * according to the contentSizing information * - * @param w horizontal dimension of the rendering area - * @param h vertical dimension of the rendering area + * @param w horizontal dimension of the rendering area + * @param h vertical dimension of the rendering area * @param scaleOutput will contain the computed scale factor */ public void computeScale(float w, float h, float[] scaleOutput) { @@ -154,37 +157,43 @@ public class CoreDocument { float scale = Math.min(1f, Math.min(scaleX, scaleY)); contentScaleX = scale; contentScaleY = scale; - } break; + } + break; case RootContentBehavior.SCALE_FIT: { float scaleX = w / mWidth; float scaleY = h / mHeight; float scale = Math.min(scaleX, scaleY); contentScaleX = scale; contentScaleY = scale; - } break; + } + break; case RootContentBehavior.SCALE_FILL_WIDTH: { float scale = w / mWidth; contentScaleX = scale; contentScaleY = scale; - } break; + } + break; case RootContentBehavior.SCALE_FILL_HEIGHT: { float scale = h / mHeight; contentScaleX = scale; contentScaleY = scale; - } break; + } + break; case RootContentBehavior.SCALE_CROP: { float scaleX = w / mWidth; float scaleY = h / mHeight; float scale = Math.max(scaleX, scaleY); contentScaleX = scale; contentScaleY = scale; - } break; + } + break; case RootContentBehavior.SCALE_FILL_BOUNDS: { float scaleX = w / mWidth; float scaleY = h / mHeight; contentScaleX = scaleX; contentScaleY = scaleY; - } break; + } + break; default: // nothing } @@ -197,10 +206,10 @@ public class CoreDocument { * Given dimensions w x h of where to paint the content, returns the corresponding translation * according to the contentAlignment information * - * @param w horizontal dimension of the rendering area - * @param h vertical dimension of the rendering area - * @param contentScaleX the horizontal scale we are going to use for the content - * @param contentScaleY the vertical scale we are going to use for the content + * @param w horizontal dimension of the rendering area + * @param h vertical dimension of the rendering area + * @param contentScaleX the horizontal scale we are going to use for the content + * @param contentScaleY the vertical scale we are going to use for the content * @param translateOutput will contain the computed translation */ private void computeTranslate(float w, float h, float contentScaleX, float contentScaleY, @@ -215,26 +224,32 @@ public class CoreDocument { switch (horizontalContentAlignment) { case RootContentBehavior.ALIGNMENT_START: { // nothing - } break; + } + break; case RootContentBehavior.ALIGNMENT_HORIZONTAL_CENTER: { translateX = (w - contentWidth) / 2f; - } break; + } + break; case RootContentBehavior.ALIGNMENT_END: { translateX = w - contentWidth; - } break; + } + break; default: // nothing (same as alignment_start) } switch (verticalContentAlignment) { case RootContentBehavior.ALIGNMENT_TOP: { // nothing - } break; + } + break; case RootContentBehavior.ALIGNMENT_VERTICAL_CENTER: { translateY = (h - contentHeight) / 2f; - } break; + } + break; case RootContentBehavior.ALIGNMENT_BOTTOM: { translateY = h - contentHeight; - } break; + } + break; default: // nothing (same as alignment_top) } @@ -291,7 +306,13 @@ public class CoreDocument { this.mMetadata = metadata; } - public boolean contains(float x, float y) { + /** + * Returns true if x,y coordinate is within bounds + * @param x x-coordinate + * @param y y-coordinate + * @return x,y coordinate is within bounds + */ + public boolean contains(float x, float y) { return x >= mLeft && x < mRight && y >= mTop && y < mBottom; } @@ -341,16 +362,22 @@ public class CoreDocument { public void initializeContext(RemoteContext context) { mRemoteComposeState.reset(); mClickAreas.clear(); - + mRemoteComposeState.setNextId(RemoteComposeState.START_ID); context.mDocument = this; context.mRemoteComposeState = mRemoteComposeState; - // mark context to be in DATA mode, which will skip the painting ops. context.mMode = RemoteContext.ContextMode.DATA; - for (Operation op: mOperations) { + mTimeVariables.updateTime(context); + + for (Operation op : mOperations) { + if (op instanceof VariableSupport) { + ((VariableSupport) op).updateVariables(context); + ((VariableSupport) op).registerListening(context); + } op.apply(context); } context.mMode = RemoteContext.ContextMode.UNSET; + } /////////////////////////////////////////////////////////////////////////////////////////////// @@ -375,7 +402,7 @@ public class CoreDocument { * @param minorVersion minor version number, increased when adding new features * @param patch patch level, increased upon bugfixes */ - void setVersion(int majorVersion, int minorVersion, int patch) { + void setVersion(int majorVersion, int minorVersion, int patch) { mVersion = new Version(majorVersion, minorVersion, patch); } @@ -389,13 +416,13 @@ public class CoreDocument { * the click coordinates will be the one reported; the order of addition of those click areas * is therefore meaningful. * - * @param id the id of the area, which will be reported on click + * @param id the id of the area, which will be reported on click * @param contentDescription the content description (used for accessibility) - * @param left the left coordinate of the click area (in pixels) - * @param top the top coordinate of the click area (in pixels) - * @param right the right coordinate of the click area (in pixels) - * @param bottom the bottom coordinate of the click area (in pixels) - * @param metadata arbitrary metadata associated with the are, also reported on click + * @param left the left coordinate of the click area (in pixels) + * @param top the top coordinate of the click area (in pixels) + * @param right the right coordinate of the click area (in pixels) + * @param bottom the bottom coordinate of the click area (in pixels) + * @param metadata arbitrary metadata associated with the are, also reported on click */ public void addClickArea(int id, String contentDescription, float left, float top, float right, float bottom, String metadata) { @@ -417,7 +444,7 @@ public class CoreDocument { * listeners. */ public void onClick(float x, float y) { - for (ClickAreaRepresentation clickArea: mClickAreas) { + for (ClickAreaRepresentation clickArea : mClickAreas) { if (clickArea.contains(x, y)) { warnClickListeners(clickArea); } @@ -430,7 +457,7 @@ public class CoreDocument { * @param id the click area id */ public void performClick(int id) { - for (ClickAreaRepresentation clickArea: mClickAreas) { + for (ClickAreaRepresentation clickArea : mClickAreas) { if (clickArea.mId == id) { warnClickListeners(clickArea); } @@ -441,17 +468,36 @@ public class CoreDocument { * Warn click listeners when a click area is activated */ private void warnClickListeners(ClickAreaRepresentation clickArea) { - for (ClickCallbacks listener: mClickListeners) { + for (ClickCallbacks listener : mClickListeners) { listener.click(clickArea.mId, clickArea.mMetadata); } } - /////////////////////////////////////////////////////////////////////////////////////////////// + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + for (Operation op : mOperations) { + builder.append(op.toString()); + builder.append("\n"); + } + return builder.toString(); + } + + ////////////////////////////////////////////////////////////////////////// // Painting - /////////////////////////////////////////////////////////////////////////////////////////////// + ////////////////////////////////////////////////////////////////////////// private final float[] mScaleOutput = new float[2]; private final float[] mTranslateOutput = new float[2]; + private int mRepaintNext = -1; // delay to next repaint -1 = don't 1 = asap + + /** + * Returns > 0 if it needs to repaint + * @return + */ + public int needsRepaint() { + return mRepaintNext; + } /** * Paint the document @@ -475,6 +521,11 @@ public class CoreDocument { context.mPaintContext.translate(mTranslateOutput[0], mTranslateOutput[1]); context.mPaintContext.scale(mScaleOutput[0], mScaleOutput[1]); } + mTimeVariables.updateTime(context); + context.loadFloat(RemoteContext.ID_WINDOW_WIDTH, getWidth()); + context.loadFloat(RemoteContext.ID_WINDOW_HEIGHT, getHeight()); + mRepaintNext = context.updateOps(); + for (Operation op : mOperations) { // operations will only be executed if no theme is set (ie UNSPECIFIED) // or the theme is equal as the one passed in argument to paint. diff --git a/core/java/com/android/internal/widget/remotecompose/core/Operations.java b/core/java/com/android/internal/widget/remotecompose/core/Operations.java index 54b277a2ac58..4b45ab691215 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/Operations.java +++ b/core/java/com/android/internal/widget/remotecompose/core/Operations.java @@ -19,6 +19,7 @@ import com.android.internal.widget.remotecompose.core.operations.BitmapData; import com.android.internal.widget.remotecompose.core.operations.ClickArea; import com.android.internal.widget.remotecompose.core.operations.ClipPath; import com.android.internal.widget.remotecompose.core.operations.ClipRect; +import com.android.internal.widget.remotecompose.core.operations.ColorExpression; import com.android.internal.widget.remotecompose.core.operations.DrawArc; import com.android.internal.widget.remotecompose.core.operations.DrawBitmap; import com.android.internal.widget.remotecompose.core.operations.DrawBitmapInt; @@ -28,9 +29,12 @@ import com.android.internal.widget.remotecompose.core.operations.DrawOval; import com.android.internal.widget.remotecompose.core.operations.DrawPath; import com.android.internal.widget.remotecompose.core.operations.DrawRect; import com.android.internal.widget.remotecompose.core.operations.DrawRoundRect; +import com.android.internal.widget.remotecompose.core.operations.DrawText; +import com.android.internal.widget.remotecompose.core.operations.DrawTextAnchored; import com.android.internal.widget.remotecompose.core.operations.DrawTextOnPath; -import com.android.internal.widget.remotecompose.core.operations.DrawTextRun; import com.android.internal.widget.remotecompose.core.operations.DrawTweenPath; +import com.android.internal.widget.remotecompose.core.operations.FloatConstant; +import com.android.internal.widget.remotecompose.core.operations.FloatExpression; import com.android.internal.widget.remotecompose.core.operations.Header; import com.android.internal.widget.remotecompose.core.operations.MatrixRestore; import com.android.internal.widget.remotecompose.core.operations.MatrixRotate; @@ -42,7 +46,10 @@ import com.android.internal.widget.remotecompose.core.operations.PaintData; import com.android.internal.widget.remotecompose.core.operations.PathData; import com.android.internal.widget.remotecompose.core.operations.RootContentBehavior; import com.android.internal.widget.remotecompose.core.operations.RootContentDescription; +import com.android.internal.widget.remotecompose.core.operations.ShaderData; import com.android.internal.widget.remotecompose.core.operations.TextData; +import com.android.internal.widget.remotecompose.core.operations.TextFromFloat; +import com.android.internal.widget.remotecompose.core.operations.TextMerge; import com.android.internal.widget.remotecompose.core.operations.Theme; import com.android.internal.widget.remotecompose.core.operations.utilities.IntMap; @@ -67,9 +74,10 @@ public class Operations { public static final int DRAW_BITMAP = 44; public static final int DRAW_BITMAP_INT = 66; public static final int DATA_BITMAP = 101; + public static final int DATA_SHADER = 45; public static final int DATA_TEXT = 102; -/////////////////////////////===================== + /////////////////////////////===================== public static final int CLIP_PATH = 38; public static final int CLIP_RECT = 39; public static final int PAINT_VALUES = 40; @@ -91,6 +99,12 @@ public class Operations { public static final int MATRIX_SAVE = 130; public static final int MATRIX_RESTORE = 131; public static final int MATRIX_SET = 132; + public static final int DATA_FLOAT = 80; + public static final int ANIMATED_FLOAT = 81; + public static final int DRAW_TEXT_ANCHOR = 133; + public static final int COLOR_EXPRESSIONS = 134; + public static final int TEXT_FROM_FLOAT = 135; + public static final int TEXT_MERGE = 136; /////////////////////////////////////////====================== public static IntMap<CompanionOperation> map = new IntMap<>(); @@ -114,7 +128,7 @@ public class Operations { map.put(DRAW_RECT, DrawRect.COMPANION); map.put(DRAW_ROUND_RECT, DrawRoundRect.COMPANION); map.put(DRAW_TEXT_ON_PATH, DrawTextOnPath.COMPANION); - map.put(DRAW_TEXT_RUN, DrawTextRun.COMPANION); + map.put(DRAW_TEXT_RUN, DrawText.COMPANION); map.put(DRAW_TWEEN_PATH, DrawTweenPath.COMPANION); map.put(DATA_PATH, PathData.COMPANION); map.put(PAINT_VALUES, PaintData.COMPANION); @@ -126,6 +140,13 @@ public class Operations { map.put(MATRIX_TRANSLATE, MatrixTranslate.COMPANION); map.put(CLIP_PATH, ClipPath.COMPANION); map.put(CLIP_RECT, ClipRect.COMPANION); + map.put(DATA_SHADER, ShaderData.COMPANION); + map.put(DATA_FLOAT, FloatConstant.COMPANION); + map.put(ANIMATED_FLOAT, FloatExpression.COMPANION); + map.put(DRAW_TEXT_ANCHOR, DrawTextAnchored.COMPANION); + map.put(COLOR_EXPRESSIONS, ColorExpression.COMPANION); + map.put(TEXT_FROM_FLOAT, TextFromFloat.COMPANION); + map.put(TEXT_MERGE, TextMerge.COMPANION); } diff --git a/core/java/com/android/internal/widget/remotecompose/core/PaintContext.java b/core/java/com/android/internal/widget/remotecompose/core/PaintContext.java index eece8ad52b60..ecd0efceacf3 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/PaintContext.java +++ b/core/java/com/android/internal/widget/remotecompose/core/PaintContext.java @@ -68,7 +68,35 @@ public abstract class PaintContext { public abstract void drawTextOnPath(int textId, int pathId, float hOffset, float vOffset); - public abstract void drawTextRun(int textID, + /** + * Return the dimensions (left, top, right, bottom). + * Relative to a drawTextRun x=0, y=0; + * + * @param textId + * @param start + * @param end if end is -1 it means the whole string + * @param monospace measure with better support for monospace + * @param bounds the bounds (left, top, right, bottom) + */ + public abstract void getTextBounds(int textId, + int start, + int end, + boolean monospace, + float[]bounds); + + /** + * Draw a text starting ast x,y + * + * @param textId reference to the text + * @param start + * @param end + * @param contextStart + * @param contextEnd + * @param x + * @param y + * @param rtl + */ + public abstract void drawTextRun(int textId, int start, int end, int contextStart, @@ -77,6 +105,14 @@ public abstract class PaintContext { float y, boolean rtl); + /** + * Draw an interpolation between two paths + * @param path1Id + * @param path2Id + * @param tween 0.0 = is path1 1.0 is path2 + * @param start + * @param stop + */ public abstract void drawTweenPath(int path1Id, int path2Id, float tween, @@ -85,21 +121,70 @@ public abstract class PaintContext { public abstract void applyPaint(PaintBundle mPaintData); - public abstract void mtrixScale(float scaleX, float scaleY, float centerX, float centerY); - + /** + * Scale the rendering by scaleX and saleY (1.0 = no scale). + * Scaling is done about centerX,centerY. + * + * @param scaleX + * @param scaleY + * @param centerX + * @param centerY + */ + public abstract void matrixScale(float scaleX, float scaleY, float centerX, float centerY); + + /** + * Translate the rendering + * @param translateX + * @param translateY + */ public abstract void matrixTranslate(float translateX, float translateY); + /** + * Skew the rendering + * @param skewX + * @param skewY + */ public abstract void matrixSkew(float skewX, float skewY); + /** + * Rotate the rendering. + * Note rotates are cumulative. + * @param rotate angle to rotate + * @param pivotX x-coordinate about which to rotate + * @param pivotY y-coordinate about which to rotate + */ public abstract void matrixRotate(float rotate, float pivotX, float pivotY); + /** + * Save the current state of the transform + */ public abstract void matrixSave(); + /** + * Restore the previously saved state of the transform + */ public abstract void matrixRestore(); + /** + * Set the clip to a rectangle. + * Drawing outside the current clip region will have no effect + * @param left + * @param top + * @param right + * @param bottom + */ public abstract void clipRect(float left, float top, float right, float bottom); + /** + * Clip based on a path. + * @param pathId + * @param regionOp + */ public abstract void clipPath(int pathId, int regionOp); + /** + * Reset the paint + */ + public abstract void reset(); } diff --git a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java index c2e81318c09a..52fc3143d721 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java +++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeBuffer.java @@ -19,6 +19,7 @@ import com.android.internal.widget.remotecompose.core.operations.BitmapData; import com.android.internal.widget.remotecompose.core.operations.ClickArea; import com.android.internal.widget.remotecompose.core.operations.ClipPath; import com.android.internal.widget.remotecompose.core.operations.ClipRect; +import com.android.internal.widget.remotecompose.core.operations.ColorExpression; import com.android.internal.widget.remotecompose.core.operations.DrawArc; import com.android.internal.widget.remotecompose.core.operations.DrawBitmap; import com.android.internal.widget.remotecompose.core.operations.DrawBitmapInt; @@ -28,9 +29,12 @@ import com.android.internal.widget.remotecompose.core.operations.DrawOval; import com.android.internal.widget.remotecompose.core.operations.DrawPath; import com.android.internal.widget.remotecompose.core.operations.DrawRect; import com.android.internal.widget.remotecompose.core.operations.DrawRoundRect; +import com.android.internal.widget.remotecompose.core.operations.DrawText; +import com.android.internal.widget.remotecompose.core.operations.DrawTextAnchored; import com.android.internal.widget.remotecompose.core.operations.DrawTextOnPath; -import com.android.internal.widget.remotecompose.core.operations.DrawTextRun; import com.android.internal.widget.remotecompose.core.operations.DrawTweenPath; +import com.android.internal.widget.remotecompose.core.operations.FloatConstant; +import com.android.internal.widget.remotecompose.core.operations.FloatExpression; import com.android.internal.widget.remotecompose.core.operations.Header; import com.android.internal.widget.remotecompose.core.operations.MatrixRestore; import com.android.internal.widget.remotecompose.core.operations.MatrixRotate; @@ -43,8 +47,12 @@ import com.android.internal.widget.remotecompose.core.operations.PathData; import com.android.internal.widget.remotecompose.core.operations.RootContentBehavior; import com.android.internal.widget.remotecompose.core.operations.RootContentDescription; import com.android.internal.widget.remotecompose.core.operations.TextData; +import com.android.internal.widget.remotecompose.core.operations.TextFromFloat; +import com.android.internal.widget.remotecompose.core.operations.TextMerge; import com.android.internal.widget.remotecompose.core.operations.Theme; +import com.android.internal.widget.remotecompose.core.operations.Utils; import com.android.internal.widget.remotecompose.core.operations.paint.PaintBundle; +import com.android.internal.widget.remotecompose.core.operations.utilities.easing.FloatAnimation; import java.io.File; import java.io.FileInputStream; @@ -58,9 +66,20 @@ import java.util.Arrays; * Provides an abstract buffer to encode/decode RemoteCompose operations */ public class RemoteComposeBuffer { + public static final int EASING_CUBIC_STANDARD = FloatAnimation.CUBIC_STANDARD; + public static final int EASING_CUBIC_ACCELERATE = FloatAnimation.CUBIC_ACCELERATE; + public static final int EASING_CUBIC_DECELERATE = FloatAnimation.CUBIC_DECELERATE; + public static final int EASING_CUBIC_LINEAR = FloatAnimation.CUBIC_LINEAR; + public static final int EASING_CUBIC_ANTICIPATE = FloatAnimation.CUBIC_ANTICIPATE; + public static final int EASING_CUBIC_OVERSHOOT = FloatAnimation.CUBIC_OVERSHOOT; + public static final int EASING_CUBIC_CUSTOM = FloatAnimation.CUBIC_CUSTOM; + public static final int EASING_SPLINE_CUSTOM = FloatAnimation.SPLINE_CUSTOM; + public static final int EASING_EASE_OUT_BOUNCE = FloatAnimation.EASE_OUT_BOUNCE; + public static final int EASING_EASE_OUT_ELASTIC = FloatAnimation.EASE_OUT_ELASTIC; WireBuffer mBuffer = new WireBuffer(); Platform mPlatform = null; RemoteComposeState mRemoteComposeState; + private static final boolean DEBUG = false; /** * Provides an abstract buffer to encode/decode RemoteCompose operations @@ -171,7 +190,7 @@ public class RemoteComposeBuffer { * * @param text the string to inject in the buffer */ - int addText(String text) { + public int addText(String text) { int id = mRemoteComposeState.dataGetId(text); if (id == -1) { id = mRemoteComposeState.cache(text); @@ -350,7 +369,6 @@ public class RemoteComposeBuffer { addDrawPath(id); } - /** * Draw the specified path * @@ -426,12 +444,160 @@ public class RemoteComposeBuffer { float y, boolean rtl) { int textId = addText(text); - DrawTextRun.COMPANION.apply( + DrawText.COMPANION.apply( mBuffer, textId, start, end, contextStart, contextEnd, x, y, rtl); } /** + * Draw the text, with origin at (x,y). The origin is interpreted + * based on the Align setting in the paint. + * + * @param textId The text to be drawn + * @param start The index of the first character in text to draw + * @param end (end - 1) is the index of the last character in text to draw + * @param contextStart + * @param contextEnd + * @param x The x-coordinate of the origin of the text being drawn + * @param y The y-coordinate of the baseline of the text being drawn + * @param rtl Draw RTTL + */ + public void addDrawTextRun(int textId, + int start, + int end, + int contextStart, + int contextEnd, + float x, + float y, + boolean rtl) { + DrawText.COMPANION.apply( + mBuffer, textId, start, end, + contextStart, contextEnd, x, y, rtl); + } + + /** + * Draw a text on canvas at relative to position (x, y), + * offset panX and panY. + * <br> + * The panning factors (panX, panY) mapped to the + * resulting bounding box of the text, in such a way that a + * panning factor of (0.0, 0.0) would center the text at (x, y) + * <ul> + * <li> Panning of -1.0, -1.0 - the text above & right of x,y.</li> + * <li>Panning of 1.0, 1.0 - the text is below and to the left</li> + * <li>Panning of 1.0, 0.0 - the test is centered & to the right of x,y</li> + * </ul> + * Setting panY to NaN results in y being the baseline of the text. + * + * @param text text to draw + * @param x Coordinate of the Anchor + * @param y Coordinate of the Anchor + * @param panX justifies text -1.0=right, 0.0=center, 1.0=left + * @param panY position text -1.0=above, 0.0=center, 1.0=below, Nan=baseline + * @param flags 1 = RTL + */ + public void drawTextAnchored(String text, + float x, + float y, + float panX, + float panY, + int flags) { + int textId = addText(text); + DrawTextAnchored.COMPANION.apply( + mBuffer, textId, + x, y, + panX, panY, + flags); + } + + /** + * Add a text and id so that it can be used + * + * @param text + * @return + */ + public int createTextId(String text) { + return addText(text); + } + + /** + * Merge two text (from id's) output one id + * @param id1 left id + * @param id2 right id + * @return new id that merges the two text + */ + public int textMerge(int id1, int id2) { + int textId = addText(id1 + "+" + id2); + TextMerge.COMPANION.apply(mBuffer, textId, id1, id2); + return textId; + } + + public static final int PAD_AFTER_SPACE = TextFromFloat.PAD_AFTER_SPACE; + public static final int PAD_AFTER_NONE = TextFromFloat.PAD_AFTER_NONE; + public static final int PAD_AFTER_ZERO = TextFromFloat.PAD_AFTER_ZERO; + public static final int PAD_PRE_SPACE = TextFromFloat.PAD_PRE_SPACE; + public static final int PAD_PRE_NONE = TextFromFloat.PAD_PRE_NONE; + public static final int PAD_PRE_ZERO = TextFromFloat.PAD_PRE_ZERO; + + /** + * Create a TextFromFloat command which creates text from a Float. + * + * @param value The value to convert + * @param digitsBefore the digits before the decimal point + * @param digitsAfter the digits after the decimal point + * @param flags configure the behaviour using PAD_PRE_* and PAD_AFTER* flags + * @return id of the string that can be passed to drawTextAnchored + */ + public int createTextFromFloat(float value, short digitsBefore, + short digitsAfter, int flags) { + String placeHolder = Utils.floatToString(value) + + "(" + digitsBefore + "," + digitsAfter + "," + flags + ")"; + int id = mRemoteComposeState.dataGetId(placeHolder); + if (id == -1) { + id = mRemoteComposeState.cache(placeHolder); + // TextData.COMPANION.apply(mBuffer, id, text); + } + TextFromFloat.COMPANION.apply(mBuffer, id, value, digitsBefore, + digitsAfter, flags); + return id; + } + + /** + * Draw a text on canvas at relative to position (x, y), + * offset panX and panY. + * <br> + * The panning factors (panX, panY) mapped to the + * resulting bounding box of the text, in such a way that a + * panning factor of (0.0, 0.0) would center the text at (x, y) + * <ul> + * <li> Panning of -1.0, -1.0 - the text above & right of x,y.</li> + * <li>Panning of 1.0, 1.0 - the text is below and to the left</li> + * <li>Panning of 1.0, 0.0 - the test is centered & to the right of x,y</li> + * </ul> + * Setting panY to NaN results in y being the baseline of the text. + * + * @param textId text to draw + * @param x Coordinate of the Anchor + * @param y Coordinate of the Anchor + * @param panX justifies text -1.0=right, 0.0=center, 1.0=left + * @param panY position text -1.0=above, 0.0=center, 1.0=below, Nan=baseline + * @param flags 1 = RTL + */ + public void drawTextAnchored(int textId, + float x, + float y, + float panX, + float panY, + int flags) { + + DrawTextAnchored.COMPANION.apply( + mBuffer, textId, + x, y, + panX, panY, + flags); + } + + /** * draw an interpolation between two paths that have the same pattern * <p> * Warning paths objects are not immutable and this is not taken into consideration @@ -490,6 +656,10 @@ public class RemoteComposeBuffer { return id; } + /** + * Adds a paint Bundle to the doc + * @param paint + */ public void addPaint(PaintBundle paint) { PaintData.COMPANION.apply(mBuffer, paint); } @@ -499,7 +669,9 @@ public class RemoteComposeBuffer { mBuffer.setIndex(0); while (mBuffer.available()) { int opId = mBuffer.readByte(); - System.out.println(">>> " + opId); + if (DEBUG) { + Utils.log(">> " + opId); + } CompanionOperation operation = Operations.map.get(opId); if (operation == null) { throw new RuntimeException("Unknown operation encountered " + opId); @@ -519,7 +691,6 @@ public class RemoteComposeBuffer { Theme.COMPANION.apply(mBuffer, theme); } - static String version() { return "v1.0"; } @@ -654,8 +825,8 @@ public class RemoteComposeBuffer { /** * Add a pre-concat of the current matrix with the specified scale. * - * @param scaleX The amount to scale in X - * @param scaleY The amount to scale in Y + * @param scaleX The amount to scale in X + * @param scaleY The amount to scale in Y */ public void addMatrixScale(float scaleX, float scaleY) { MatrixScale.COMPANION.apply(mBuffer, scaleX, scaleY, Float.NaN, Float.NaN); @@ -673,12 +844,174 @@ public class RemoteComposeBuffer { MatrixScale.COMPANION.apply(mBuffer, scaleX, scaleY, centerX, centerY); } + /** + * sets the clip based on clip id + * @param pathId 0 clears the clip + */ public void addClipPath(int pathId) { ClipPath.COMPANION.apply(mBuffer, pathId); } + /** + * Sets the clip based on clip rec + * @param left + * @param top + * @param right + * @param bottom + */ public void addClipRect(float left, float top, float right, float bottom) { ClipRect.COMPANION.apply(mBuffer, left, top, right, bottom); } + + /** + * Add a float return a NaN number pointing to that float + * @param value + * @return + */ + public float addFloat(float value) { + int id = mRemoteComposeState.cacheFloat(value); + FloatConstant.COMPANION.apply(mBuffer, id, value); + return Utils.asNan(id); + } + + /** + * Add a float that is a computation based on variables + * @param value A RPN style float operation i.e. "4, 3, ADD" outputs 7 + * @return NaN id of the result of the calculation + */ + public float addAnimatedFloat(float... value) { + int id = mRemoteComposeState.cache(value); + FloatExpression.COMPANION.apply(mBuffer, id, value, null); + return Utils.asNan(id); + } + + /** + * Add a float that is a computation based on variables. + * see packAnimation + * @param value A RPN style float operation i.e. "4, 3, ADD" outputs 7 + * @param animation Array of floats that represents animation + * @return NaN id of the result of the calculation + */ + public float addAnimatedFloat(float[] value, float[] animation) { + int id = mRemoteComposeState.cache(value); + FloatExpression.COMPANION.apply(mBuffer, id, value, animation); + return Utils.asNan(id); + } + + /** + * Add a color that represents the tween between two colors + * @param color1 + * @param color2 + * @param tween + * @return id of the color (color ids are short) + */ + public short addColorExpression(int color1, int color2, float tween) { + ColorExpression c = new ColorExpression(0, 0, color1, color2, tween); + short id = (short) mRemoteComposeState.cache(c); + c.mId = id; + c.write(mBuffer); + return id; + } + + /** + * Add a color that represents the tween between two colors where color1 + * is the id of a color + * @param color1 + * @param color2 + * @param tween + * @return id of the color (color ids are short) + */ + public short addColorExpression(short color1, int color2, float tween) { + ColorExpression c = new ColorExpression(0, 1, color1, color2, tween); + short id = (short) mRemoteComposeState.cache(c); + c.mId = id; + c.write(mBuffer); + return id; + } + + /** + * Add a color that represents the tween between two colors where color2 + * is the id of a color + * @param color1 + * @param color2 + * @param tween + * @return id of the color (color ids are short) + */ + public short addColorExpression(int color1, short color2, float tween) { + ColorExpression c = new ColorExpression(0, 2, color1, color2, tween); + short id = (short) mRemoteComposeState.cache(c); + c.mId = id; + c.write(mBuffer); + return id; + } + + /** + * Add a color that represents the tween between two colors where color1 & + * color2 are the ids of colors + * @param color1 + * @param color2 + * @param tween + * @return id of the color (color ids are short) + */ + public short addColorExpression(short color1, short color2, float tween) { + ColorExpression c = new ColorExpression(0, 3, color1, color2, tween); + short id = (short) mRemoteComposeState.cache(c); + c.mId = id; + c.write(mBuffer); + return id; + } + + /** + * Color calculated by Hue saturation and value. + * (as floats they can be variables used to create color transitions) + * @param hue + * @param sat + * @param value + * @return id of the color (color ids are short) + */ + public short addColorExpression(float hue, float sat, float value) { + ColorExpression c = new ColorExpression(0, hue, sat, value); + short id = (short) mRemoteComposeState.cache(c); + c.mId = id; + c.write(mBuffer); + return id; + } + + /** + * Color calculated by Alpha, Hue saturation and value. + * (as floats they can be variables used to create color transitions) + * @param alpha + * @param hue + * @param sat + * @param value + * @return id of the color (color ids are short) + */ + public short addColorExpression(int alpha, float hue, float sat, float value) { + ColorExpression c = new ColorExpression(0, alpha, hue, sat, value); + short id = (short) mRemoteComposeState.cache(c); + c.mId = id; + c.write(mBuffer); + return id; + } + + /** + * create and animation based on description and return as an array of + * floats. see addAnimatedFloat + * @param duration + * @param type + * @param spec + * @param initialValue + * @param wrap + * @return + */ + public static float[] packAnimation(float duration, + int type, + float[] spec, + float initialValue, + float wrap) { + + return FloatAnimation.packToFloatArray(duration, type, spec, initialValue, wrap); + } + } diff --git a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java index 17e8c839a080..66a37e677499 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java +++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteComposeState.java @@ -15,26 +15,53 @@ */ package com.android.internal.widget.remotecompose.core; +import static com.android.internal.widget.remotecompose.core.RemoteContext.ID_CONTINUOUS_SEC; +import static com.android.internal.widget.remotecompose.core.RemoteContext.ID_TIME_IN_MIN; +import static com.android.internal.widget.remotecompose.core.RemoteContext.ID_TIME_IN_SEC; +import static com.android.internal.widget.remotecompose.core.RemoteContext.ID_WINDOW_HEIGHT; +import static com.android.internal.widget.remotecompose.core.RemoteContext.ID_WINDOW_WIDTH; + import com.android.internal.widget.remotecompose.core.operations.utilities.IntMap; +import java.util.ArrayList; import java.util.HashMap; /** * Represents runtime state for a RemoteCompose document + * State includes things like the value of variables */ public class RemoteComposeState { - + public static final int START_ID = 42; + private static final int MAX_FLOATS = 500; private final IntMap<Object> mIntDataMap = new IntMap<>(); private final IntMap<Boolean> mIntWrittenMap = new IntMap<>(); private final HashMap<Object, Integer> mDataIntMap = new HashMap(); + private final float[] mFloatMap = new float[MAX_FLOATS]; // efficient cache + private final int[] mColorMap = new int[MAX_FLOATS]; // efficient cache + private int mNextId = START_ID; - private static int sNextId = 42; + { + for (int i = 0; i < mFloatMap.length; i++) { + mFloatMap[i] = Float.NaN; + } + } - public Object getFromId(int id) { + /** + * Get Object based on id. The system will cache things like bitmaps + * Paths etc. They can be accessed with this command + * @param id + * @return + */ + public Object getFromId(int id) { return mIntDataMap.get(id); } - public boolean containsId(int id) { + /** + * true if the cache contain this id + * @param id + * @return + */ + public boolean containsId(int id) { return mIntDataMap.get(id) != null; } @@ -69,6 +96,65 @@ public class RemoteComposeState { } /** + * Insert an item in the cache + */ + public void update(int id, Object item) { + mDataIntMap.remove(mIntDataMap.get(id)); + mDataIntMap.put(item, id); + mIntDataMap.put(id, item); + } + + /** + * Insert an item in the cache + */ + public int cacheFloat(float item) { + int id = nextId(); + mFloatMap[id] = item; + return id; + } + + /** + * Insert an item in the cache + */ + public void cacheFloat(int id, float item) { + mFloatMap[id] = item; + } + + /** + * Insert an item in the cache + */ + public void updateFloat(int id, float item) { + mFloatMap[id] = item; + } + + /** + * get float + */ + public float getFloat(int id) { + return mFloatMap[id]; + } + + /** + * Get the float value + * + * @param id + * @return + */ + public int getColor(int id) { + return mColorMap[id]; + } + + /** + * Modify the color at id. + * @param id + * @param color + */ + public void updateColor(int id, int color) { + mColorMap[id] = color; + } + + + /** * Method to determine if a cached value has been written to the documents WireBuffer based on * its id. */ @@ -79,22 +165,90 @@ public class RemoteComposeState { /** * Method to mark that a value, represented by its id, has been written to the WireBuffer */ - public void markWritten(int id) { + public void markWritten(int id) { mIntWrittenMap.put(id, true); } /** - * Clear the record of the values that have been written to the WireBuffer. + * Clear the record of the values that have been written to the WireBuffer. */ void reset() { mIntWrittenMap.clear(); } - public static int nextId() { - return sNextId++; + /** + * Get the next available id + * @return + */ + public int nextId() { + return mNextId++; } - public static void setNextId(int id) { - sNextId = id; + + /** + * Set the next id + * @param id + */ + public void setNextId(int id) { + mNextId = id; + } + + IntMap<ArrayList<VariableSupport>> mVarListeners = new IntMap<>(); + ArrayList<VariableSupport> mAllVarListeners = new ArrayList<>(); + + private void add(int id, VariableSupport variableSupport) { + ArrayList<VariableSupport> v = mVarListeners.get(id); + if (v == null) { + v = new ArrayList<VariableSupport>(); + mVarListeners.put(id, v); + } + v.add(variableSupport); + mAllVarListeners.add(variableSupport); + } + + /** + * Commands that listen to variables add themselves. + * @param id + * @param variableSupport + */ + public void listenToVar(int id, VariableSupport variableSupport) { + add(id, variableSupport); + } + + /** + * List of Commands that need to be updated + * @param context + * @return + */ + public int getOpsToUpdate(RemoteContext context) { + for (VariableSupport vs : mAllVarListeners) { + vs.updateVariables(context); + } + if (mVarListeners.get(ID_CONTINUOUS_SEC) != null) { + return 1; + } + if (mVarListeners.get(ID_TIME_IN_SEC) != null) { + return 1000; + } + if (mVarListeners.get(ID_TIME_IN_MIN) != null) { + return 1000 * 60; + } + return -1; + } + + /** + * Set the width of the overall document on screen. + * @param width + */ + public void setWindowWidth(float width) { + updateFloat(ID_WINDOW_WIDTH, width); + } + + /** + * Set the width of the overall document on screen. + * @param height + */ + public void setWindowHeight(float height) { + updateFloat(ID_WINDOW_HEIGHT, height); } } diff --git a/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java b/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java index d16cbc5a1a16..7e721684be0a 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java +++ b/core/java/com/android/internal/widget/remotecompose/core/RemoteContext.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2023 The Android Open Source Project + * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,7 +15,10 @@ */ package com.android.internal.widget.remotecompose.core; +import com.android.internal.widget.remotecompose.core.operations.FloatExpression; +import com.android.internal.widget.remotecompose.core.operations.ShaderData; import com.android.internal.widget.remotecompose.core.operations.Theme; +import com.android.internal.widget.remotecompose.core.operations.Utils; /** * Specify an abstract context used to playback RemoteCompose documents @@ -27,7 +30,7 @@ import com.android.internal.widget.remotecompose.core.operations.Theme; public abstract class RemoteContext { protected CoreDocument mDocument; public RemoteComposeState mRemoteComposeState; - + long mStart = System.nanoTime(); // todo This should be set at a hi level protected PaintContext mPaintContext = null; ContextMode mMode = ContextMode.UNSET; @@ -37,9 +40,40 @@ public abstract class RemoteContext { public float mWidth = 0f; public float mHeight = 0f; + /** + * Load a path under an id. + * Paths can be use in clip drawPath and drawTweenPath + * @param instanceId + * @param floatPath + */ public abstract void loadPathData(int instanceId, float[] floatPath); /** + * Associate a name with a give id. + * + * @param varName + * @param varId + * @param varType + */ + public abstract void loadVariableName(String varName, int varId, int varType); + + /** + * Save a color under a given id + * @param id + * @param color + */ + public abstract void loadColor(int id, int color); + + /** + * gets the time animation clock as float in seconds + * @return a monotonic time in seconds (arbitrary zero point) + */ + public float getAnimationTime() { + return (System.nanoTime() - mStart) * 1E-9f; + } + + + /** * The context can be used in a few different mode, allowing operations to skip being executed: * - UNSET : all operations will get executed * - DATA : only operations dealing with DATA (eg loading a bitmap) should execute @@ -96,6 +130,8 @@ public abstract class RemoteContext { public void header(int majorVersion, int minorVersion, int patchVersion, int width, int height, long capabilities ) { + mRemoteComposeState.setWindowWidth(width); + mRemoteComposeState.setWindowHeight(height); mDocument.setVersion(majorVersion, minorVersion, patchVersion); mDocument.setWidth(width); mDocument.setHeight(height); @@ -137,9 +173,105 @@ public abstract class RemoteContext { /////////////////////////////////////////////////////////////////////////////////////////////// // Data handling /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * Save a bitmap under an imageId + * @param imageId + * @param width + * @param height + * @param bitmap + */ public abstract void loadBitmap(int imageId, int width, int height, byte[] bitmap); + + /** + * Save a string under a given id + * @param id + * @param text + */ public abstract void loadText(int id, String text); + /** + * Get a string given an id + * @param id + * @return + */ + public abstract String getText(int id); + + /** + * Load a float + * @param id + * @param value + */ + public abstract void loadFloat(int id, float value); + + /** + * Load an animated float associated with an id + * Todo: Remove? + * @param id + * @param animatedFloat + */ + public abstract void loadAnimatedFloat(int id, FloatExpression animatedFloat); + + /** + * Save a shader under and ID + * @param id + * @param value + */ + public abstract void loadShader(int id, ShaderData value); + + /** + * Get a float given an id + * @param id + * @return + */ + public abstract float getFloat(int id); + + /** + * Get the color given and ID + * @param id + * @return + */ + public abstract int getColor(int id); + + /** + * called to notify system that a command is interested in a variable + * @param id + * @param variableSupport + */ + public abstract void listensTo(int id, VariableSupport variableSupport); + + /** + * Notify commands with variables have changed + * @return + */ + public abstract int updateOps(); + + /** + * Get a shader given the id + * @param id + * @return + */ + public abstract ShaderData getShader(int id); + + public static final int ID_CONTINUOUS_SEC = 1; + public static final int ID_TIME_IN_SEC = 2; + public static final int ID_TIME_IN_MIN = 3; + public static final int ID_TIME_IN_HR = 4; + public static final int ID_WINDOW_WIDTH = 5; + public static final int ID_WINDOW_HEIGHT = 6; + public static final int ID_COMPONENT_WIDTH = 7; + public static final int ID_COMPONENT_HEIGHT = 8; + public static final int ID_CALENDAR_MONTH = 9; + + public static final float FLOAT_CONTINUOUS_SEC = Utils.asNan(ID_CONTINUOUS_SEC); + public static final float FLOAT_TIME_IN_SEC = Utils.asNan(ID_TIME_IN_SEC); + public static final float FLOAT_TIME_IN_MIN = Utils.asNan(ID_TIME_IN_MIN); + public static final float FLOAT_TIME_IN_HR = Utils.asNan(ID_TIME_IN_HR); + public static final float FLOAT_CALENDAR_MONTH = Utils.asNan(ID_CALENDAR_MONTH); + public static final float FLOAT_WINDOW_WIDTH = Utils.asNan(ID_WINDOW_WIDTH); + public static final float FLOAT_WINDOW_HEIGHT = Utils.asNan(ID_WINDOW_HEIGHT); + public static final float FLOAT_COMPONENT_WIDTH = Utils.asNan(ID_COMPONENT_WIDTH); + public static final float FLOAT_COMPONENT_HEIGHT = Utils.asNan(ID_COMPONENT_HEIGHT); /////////////////////////////////////////////////////////////////////////////////////////////// // Click handling /////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/core/java/com/android/internal/widget/remotecompose/core/TimeVariables.java b/core/java/com/android/internal/widget/remotecompose/core/TimeVariables.java new file mode 100644 index 000000000000..e9708b75de27 --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/TimeVariables.java @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.widget.remotecompose.core; + +import java.time.LocalDateTime; + +/** + * This generates the standard system variables for time. + */ +public class TimeVariables { + /** + * This class populates all time variables in the system + * @param context + */ + public void updateTime(RemoteContext context) { + LocalDateTime dateTime = LocalDateTime.now(); + // This define the time in the format + // seconds run from Midnight=0 quantized to seconds hour 0..3599 + // minutes run from Midnight=0 quantized to minutes 0..1439 + // hours run from Midnight=0 quantized to Hours 0-23 + // CONTINUOUS_SEC is seconds from midnight looping every hour 0-3600 + // CONTINUOUS_SEC is accurate to milliseconds due to float precession + int month = dateTime.getDayOfMonth(); + int hour = dateTime.getHour(); + int minute = dateTime.getMinute(); + int seconds = dateTime.getSecond(); + int currentMinute = hour * 60 + minute; + int currentSeconds = minute * 60 + seconds; + float sec = currentSeconds + dateTime.getNano() * 1E-9f; + + context.loadFloat(RemoteContext.ID_CONTINUOUS_SEC, sec); + context.loadFloat(RemoteContext.ID_TIME_IN_SEC, currentSeconds); + context.loadFloat(RemoteContext.ID_TIME_IN_MIN, currentMinute); + context.loadFloat(RemoteContext.ID_TIME_IN_HR, hour); + context.loadFloat(RemoteContext.ID_CALENDAR_MONTH, month); + + } +} diff --git a/core/java/com/android/internal/widget/remotecompose/core/VariableSupport.java b/core/java/com/android/internal/widget/remotecompose/core/VariableSupport.java new file mode 100644 index 000000000000..d59b1bc65256 --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/VariableSupport.java @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.widget.remotecompose.core; + +/** + * Interface for operators that interact with variables + * Threw this they register to listen to particular variables + * and are notified when they change + */ +public interface VariableSupport { + /** + * Call to allow an operator to register interest in variables. + * Typically they call context.listensTo(id, this) + * @param context + */ + void registerListening(RemoteContext context); + + /** + * Called to be notified that the variables you are interested have changed. + * @param context + */ + void updateVariables(RemoteContext context); +} diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/BitmapData.java b/core/java/com/android/internal/widget/remotecompose/core/operations/BitmapData.java index 76b714443990..f1863225b766 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/BitmapData.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/BitmapData.java @@ -51,11 +51,12 @@ public class BitmapData implements Operation { @Override public String toString() { - return "BITMAP DATA $imageId"; + return "BITMAP DATA " + mImageId; } public static class Companion implements CompanionOperation { - private Companion() {} + private Companion() { + } @Override public String name() { diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/ClipPath.java b/core/java/com/android/internal/widget/remotecompose/core/operations/ClipPath.java index 8d4a787148ef..e6d5fe7043fd 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/ClipPath.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/ClipPath.java @@ -24,6 +24,11 @@ import com.android.internal.widget.remotecompose.core.WireBuffer; import java.util.List; +/** + * Defines a path that clips a the subsequent drawing commands + * Use MatrixSave and MatrixRestore commands to remove clip + * TODO allow id 0 to mean null? + */ public class ClipPath extends PaintOperation { public static final Companion COMPANION = new Companion(); int mId; @@ -93,5 +98,4 @@ public class ClipPath extends PaintOperation { public void paint(PaintContext context) { context.clipPath(mId, mRegionOp); } -} - +}
\ No newline at end of file diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/ClipRect.java b/core/java/com/android/internal/widget/remotecompose/core/operations/ClipRect.java index 803618a91737..613ecebf9100 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/ClipRect.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/ClipRect.java @@ -15,88 +15,36 @@ */ package com.android.internal.widget.remotecompose.core.operations; -import com.android.internal.widget.remotecompose.core.CompanionOperation; import com.android.internal.widget.remotecompose.core.Operation; import com.android.internal.widget.remotecompose.core.Operations; import com.android.internal.widget.remotecompose.core.PaintContext; -import com.android.internal.widget.remotecompose.core.PaintOperation; -import com.android.internal.widget.remotecompose.core.WireBuffer; -import java.util.List; - -public class ClipRect extends PaintOperation { - public static final Companion COMPANION = new Companion(); - float mLeft; - float mTop; - float mRight; - float mBottom; +/** + * Support clip with a rectangle + */ +public class ClipRect extends DrawBase4 { + public static final Companion COMPANION = + new Companion(Operations.CLIP_RECT) { + @Override + public Operation construct(float x1, + float y1, + float x2, + float y2) { + return new ClipRect(x1, y1, x2, y2); + } + }; public ClipRect( float left, float top, float right, float bottom) { - mLeft = left; - mTop = top; - mRight = right; - mBottom = bottom; - - } - - @Override - public void write(WireBuffer buffer) { - COMPANION.apply(buffer, mLeft, mTop, mRight, mBottom); - } - - @Override - public String toString() { - return "ClipRect " + mLeft + " " + mTop - + " " + mRight + " " + mBottom + ";"; - } - - public static class Companion implements CompanionOperation { - private Companion() { - } - - @Override - public void read(WireBuffer buffer, List<Operation> operations) { - float sLeft = buffer.readFloat(); - float srcTop = buffer.readFloat(); - float srcRight = buffer.readFloat(); - float srcBottom = buffer.readFloat(); - - ClipRect op = new ClipRect(sLeft, srcTop, srcRight, srcBottom); - operations.add(op); - } - - @Override - public String name() { - return "ClipRect"; - } - - @Override - public int id() { - return Operations.CLIP_RECT; - } - - public void apply(WireBuffer buffer, - float left, - float top, - float right, - float bottom) { - buffer.start(Operations.CLIP_RECT); - buffer.writeFloat(left); - buffer.writeFloat(top); - buffer.writeFloat(right); - buffer.writeFloat(bottom); - } + super(left, top, right, bottom); + mName = "ClipRect"; } @Override public void paint(PaintContext context) { - context.clipRect(mLeft, - mTop, - mRight, - mBottom); + context.clipRect(mX1, mY1, mX2, mY2); } } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/ColorExpression.java b/core/java/com/android/internal/widget/remotecompose/core/operations/ColorExpression.java new file mode 100644 index 000000000000..7d28cea35850 --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/ColorExpression.java @@ -0,0 +1,242 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.widget.remotecompose.core.operations; + +import com.android.internal.widget.remotecompose.core.CompanionOperation; +import com.android.internal.widget.remotecompose.core.Operation; +import com.android.internal.widget.remotecompose.core.Operations; +import com.android.internal.widget.remotecompose.core.RemoteContext; +import com.android.internal.widget.remotecompose.core.VariableSupport; +import com.android.internal.widget.remotecompose.core.WireBuffer; + +import java.util.List; + +/** + * Operation to Colors + * Color modes + * mMode = 0 two colors and a tween + * mMode = 1 color1 is a colorID. + * mMode = 2 color2 is a colorID. + * mMode = 3 color1 & color2 are ids + * mMode = 4 H S V mode + */ +public class ColorExpression implements Operation, VariableSupport { + public int mId; + int mMode; + public int mColor1; + public int mColor2; + public float mTween = 0.0f; + + + public float mHue = 0; // only in Mode 4 + public float mSat = 0; + public float mValue = 0; + public float mOutHue = 0; // only in Mode 4 + public float mOutSat = 0; + public float mOutValue = 0; + public int mAlpha = 0xFF; // only used in hsv mode + + public float mOutTween = 0.0f; + public int mOutColor1; + public int mOutColor2; + public static final Companion COMPANION = new Companion(); + public static final int HSV_MODE = 4; + public ColorExpression(int id, float hue, float sat, float value) { + mMode = HSV_MODE; + mAlpha = 0xFF; + mOutHue = mHue = hue; + mOutSat = mSat = sat; + mOutValue = mValue = value; + mColor1 = Float.floatToRawIntBits(hue); + mColor2 = Float.floatToRawIntBits(sat); + mTween = value; + } + public ColorExpression(int id, int alpha, float hue, float sat, float value) { + mMode = HSV_MODE; + mAlpha = alpha; + mOutHue = mHue = hue; + mOutSat = mSat = sat; + mOutValue = mValue = value; + mColor1 = Float.floatToRawIntBits(hue); + mColor2 = Float.floatToRawIntBits(sat); + mTween = value; + } + + public ColorExpression(int id, int mode, int color1, int color2, float tween) { + this.mId = id; + this.mMode = mode & 0xFF; + this.mAlpha = (mode >> 16) & 0xFF; + if (mMode == HSV_MODE) { + mOutHue = mHue = Float.intBitsToFloat(color1); + mOutSat = mSat = Float.intBitsToFloat(color2); + mOutValue = mValue = tween; + } + this.mColor1 = color1; + this.mColor2 = color2; + this.mTween = tween; + this.mOutTween = tween; + this.mOutColor1 = color1; + this.mOutColor2 = color2; + + } + + @Override + public void updateVariables(RemoteContext context) { + if (mMode == 4) { + if (Float.isNaN(mHue)) { + mOutHue = context.getFloat(Utils.idFromNan(mHue)); + } + if (Float.isNaN(mSat)) { + mOutSat = context.getFloat(Utils.idFromNan(mSat)); + } + if (Float.isNaN(mValue)) { + mOutValue = context.getFloat(Utils.idFromNan(mValue)); + } + } + if (Float.isNaN(mTween)) { + mOutTween = context.getFloat(Utils.idFromNan(mTween)); + } + if ((mMode & 1) == 1) { + mOutColor1 = context.getColor(mColor1); + } + if ((mMode & 2) == 2) { + mOutColor2 = context.getColor(mColor2); + } + } + + + @Override + public void registerListening(RemoteContext context) { + if (mMode == 4) { + if (Float.isNaN(mHue)) { + context.listensTo(Utils.idFromNan(mHue), this); + } + if (Float.isNaN(mSat)) { + context.listensTo(Utils.idFromNan(mSat), this); + } + if (Float.isNaN(mValue)) { + context.listensTo(Utils.idFromNan(mValue), this); + } + return; + } + if (Float.isNaN(mTween)) { + context.listensTo(Utils.idFromNan(mTween), this); + } + if ((mMode & 1) == 1) { + context.listensTo(mColor1, this); + } + if ((mMode & 2) == 2) { + context.listensTo(mColor2, this); + } + } + + @Override + public void apply(RemoteContext context) { + if (mMode == 4) { + context.loadColor(mId, (mAlpha << 24) + | (0xFFFFFF & Utils.hsvToRgb(mOutHue, mOutSat, mOutValue))); + return; + } + if (mOutTween == 0.0) { + context.loadColor(mId, mColor1); + } else { + if ((mMode & 1) == 1) { + mOutColor1 = context.getColor(mColor1); + } + if ((mMode & 2) == 2) { + mOutColor2 = context.getColor(mColor2); + } + + context.loadColor(mId, + Utils.interpolateColor(mOutColor1, mOutColor2, mOutTween)); + } + + } + + @Override + public void write(WireBuffer buffer) { + int mode = mMode | (mAlpha << 16); + COMPANION.apply(buffer, mId, mode, mColor1, mColor2, mTween); + } + + @Override + public String toString() { + if (mMode == 4) { + return "ColorExpression[" + mId + "] = hsv (" + Utils.floatToString(mHue) + + ", " + Utils.floatToString(mSat) + + ", " + Utils.floatToString(mValue) + ")"; + } + + String c1 = (mMode & 1) == 1 ? "[" + mColor1 + "]" : Utils.colorInt(mColor1); + String c2 = (mMode & 2) == 2 ? "[" + mColor2 + "]" : Utils.colorInt(mColor2); + return "ColorExpression[" + mId + "] = tween(" + c1 + + ", " + c2 + ", " + + Utils.floatToString(mTween) + ")"; + } + + public static class Companion implements CompanionOperation { + private Companion() { + } + + @Override + public String name() { + return "ColorExpression"; + } + + @Override + public int id() { + return Operations.COLOR_EXPRESSIONS; + } + + /** + * Call to write a ColorExpression object on the buffer + * @param buffer + * @param id of the ColorExpression object + * @param mode if colors are id or actual values + * @param color1 + * @param color2 + * @param tween + */ + public void apply(WireBuffer buffer, + int id, int mode, + int color1, int color2, float tween) { + buffer.start(Operations.COLOR_EXPRESSIONS); + buffer.writeInt(id); + buffer.writeInt(mode); + buffer.writeInt(color1); + buffer.writeInt(color2); + buffer.writeFloat(tween); + + } + + @Override + public void read(WireBuffer buffer, List<Operation> operations) { + int id = buffer.readInt(); + int mode = buffer.readInt(); + int color1 = buffer.readInt(); + int color2 = buffer.readInt(); + float tween = buffer.readFloat(); + + operations.add(new ColorExpression(id, mode, color1, color2, tween)); + } + } + + @Override + public String deepToString(String indent) { + return indent + toString(); + } + +} diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawArc.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawArc.java index e829975cd39b..c1768647bde6 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawArc.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawArc.java @@ -15,107 +15,36 @@ */ package com.android.internal.widget.remotecompose.core.operations; -import com.android.internal.widget.remotecompose.core.CompanionOperation; import com.android.internal.widget.remotecompose.core.Operation; import com.android.internal.widget.remotecompose.core.Operations; import com.android.internal.widget.remotecompose.core.PaintContext; -import com.android.internal.widget.remotecompose.core.PaintOperation; -import com.android.internal.widget.remotecompose.core.WireBuffer; -import java.util.List; - -public class DrawArc extends PaintOperation { - public static final Companion COMPANION = new Companion(); - float mLeft; - float mTop; - float mRight; - float mBottom; - float mStartAngle; - float mSweepAngle; - - public DrawArc( - float left, - float top, - float right, - float bottom, - float startAngle, - float sweepAngle) { - mLeft = left; - mTop = top; - mRight = right; - mBottom = bottom; - mStartAngle = startAngle; - mSweepAngle = sweepAngle; - } - - @Override - public void write(WireBuffer buffer) { - COMPANION.apply(buffer, mLeft, - mTop, - mRight, - mBottom, - mStartAngle, - mSweepAngle); - } - - @Override - public String toString() { - return "DrawArc " + mLeft + " " + mTop - + " " + mRight + " " + mBottom + " " - + "- " + mStartAngle + " " + mSweepAngle + ";"; - } - - public static class Companion implements CompanionOperation { - private Companion() { - } - - @Override - public void read(WireBuffer buffer, List<Operation> operations) { - float sLeft = buffer.readFloat(); - float srcTop = buffer.readFloat(); - float srcRight = buffer.readFloat(); - float srcBottom = buffer.readFloat(); - float mStartAngle = buffer.readFloat(); - float mSweepAngle = buffer.readFloat(); - DrawArc op = new DrawArc(sLeft, srcTop, srcRight, srcBottom, - mStartAngle, mSweepAngle); - operations.add(op); - } - - @Override - public String name() { - return "DrawArc"; - } - - @Override - public int id() { - return Operations.DRAW_ARC; - } - - public void apply(WireBuffer buffer, - float left, - float top, - float right, - float bottom, - float startAngle, - float sweepAngle) { - buffer.start(Operations.DRAW_ARC); - buffer.writeFloat(left); - buffer.writeFloat(top); - buffer.writeFloat(right); - buffer.writeFloat(bottom); - buffer.writeFloat(startAngle); - buffer.writeFloat(sweepAngle); - } +public class DrawArc extends DrawBase6 { + public static final Companion COMPANION = + new Companion(Operations.DRAW_ARC) { + @Override + public Operation construct(float v1, + float v2, + float v3, + float v4, + float v5, + float v6) { + return new DrawArc(v1, v2, v3, v4, v5, v6); + } + }; + + public DrawArc(float v1, + float v2, + float v3, + float v4, + float v5, + float v6) { + super(v1, v2, v3, v4, v5, v6); + mName = "DrawArc"; } @Override public void paint(PaintContext context) { - context.drawArc(mLeft, - mTop, - mRight, - mBottom, - mStartAngle, - mSweepAngle); + context.drawArc(mV1, mV2, mV3, mV4, mV5, mV6); } } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase2.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase2.java new file mode 100644 index 000000000000..0963c1337b70 --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase2.java @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.widget.remotecompose.core.operations; + +import static com.android.internal.widget.remotecompose.core.operations.Utils.floatToString; + +import com.android.internal.widget.remotecompose.core.CompanionOperation; +import com.android.internal.widget.remotecompose.core.Operation; +import com.android.internal.widget.remotecompose.core.Operations; +import com.android.internal.widget.remotecompose.core.PaintOperation; +import com.android.internal.widget.remotecompose.core.RemoteContext; +import com.android.internal.widget.remotecompose.core.VariableSupport; +import com.android.internal.widget.remotecompose.core.WireBuffer; + +import java.util.List; + +/** + * Base class for commands that take 3 float + */ +public abstract class DrawBase2 extends PaintOperation + implements VariableSupport { + public static final Companion COMPANION = + new Companion(Operations.DRAW_CIRCLE) { + @Override + public Operation construct(float x1, float y1) { + // subclass should return new DrawX(x1, y1); + return null; + } + }; + protected String mName = "DrawRectBase"; + float mV1; + float mV2; + float mValue1; + float mValue2; + + public DrawBase2(float v1, float v2) { + mValue1 = v1; + mValue2 = v2; + mV1 = v1; + mV2 = v2; + } + + @Override + public void updateVariables(RemoteContext context) { + mV1 = (Float.isNaN(mValue1)) + ? context.getFloat(Utils.idFromNan(mValue1)) : mValue1; + mV2 = (Float.isNaN(mValue2)) + ? context.getFloat(Utils.idFromNan(mValue2)) : mValue2; + } + + @Override + public void registerListening(RemoteContext context) { + if (Float.isNaN(mValue1)) { + context.listensTo(Utils.idFromNan(mValue1), this); + } + if (Float.isNaN(mValue2)) { + context.listensTo(Utils.idFromNan(mValue2), this); + } + } + + @Override + public void write(WireBuffer buffer) { + COMPANION.apply(buffer, mV1, mV2); + } + + @Override + public String toString() { + return mName + " " + floatToString(mV1) + " " + floatToString(mV2); + } + + public static class Companion implements CompanionOperation { + public final int OP_CODE; + + protected Companion(int code) { + OP_CODE = code; + } + + @Override + public void read(WireBuffer buffer, List<Operation> operations) { + float v1 = buffer.readFloat(); + float v2 = buffer.readFloat(); + + Operation op = construct(v1, v2); + operations.add(op); + } + + /** + * Override to construct a 2 float value operation + * @param x1 + * @param y1 + * @return + */ + public Operation construct(float x1, float y1) { + return null; + } + + @Override + public String name() { + return "DrawRect"; + } + + @Override + public int id() { + return OP_CODE; + } + + /** + * Writes out the operation to the buffer + * @param buffer + * @param x1 + * @param y1 + */ + public void apply(WireBuffer buffer, + float x1, + float y1) { + buffer.start(OP_CODE); + buffer.writeFloat(x1); + buffer.writeFloat(y1); + } + } +} diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase3.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase3.java new file mode 100644 index 000000000000..56b2f1f7bb86 --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase3.java @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.widget.remotecompose.core.operations; + +import static com.android.internal.widget.remotecompose.core.operations.Utils.floatToString; + +import com.android.internal.widget.remotecompose.core.CompanionOperation; +import com.android.internal.widget.remotecompose.core.Operation; +import com.android.internal.widget.remotecompose.core.Operations; +import com.android.internal.widget.remotecompose.core.PaintOperation; +import com.android.internal.widget.remotecompose.core.RemoteContext; +import com.android.internal.widget.remotecompose.core.VariableSupport; +import com.android.internal.widget.remotecompose.core.WireBuffer; + +import java.util.List; + +/** + * Base class for commands that take 3 float + */ +public abstract class DrawBase3 extends PaintOperation + implements VariableSupport { + public static final Companion COMPANION = + new Companion(Operations.DRAW_CIRCLE) { + @Override + public Operation construct(float x1, float y1, float x2) { + // subclass should return new DrawX(x1, y1, x2, y2); + return null; + } + }; + protected String mName = "DrawRectBase"; + float mV1; + float mV2; + float mV3; + float mValue1; + float mValue2; + float mValue3; + + public DrawBase3( + float v1, + float v2, + float v3) { + mValue1 = v1; + mValue2 = v2; + mValue3 = v3; + + mV1 = v1; + mV2 = v2; + mV3 = v3; + } + + @Override + public void updateVariables(RemoteContext context) { + mV1 = (Float.isNaN(mValue1)) + ? context.getFloat(Utils.idFromNan(mValue1)) : mValue1; + mV2 = (Float.isNaN(mValue2)) + ? context.getFloat(Utils.idFromNan(mValue2)) : mValue2; + mV3 = (Float.isNaN(mValue3)) + ? context.getFloat(Utils.idFromNan(mValue3)) : mValue3; + } + + @Override + public void registerListening(RemoteContext context) { + if (Float.isNaN(mValue1)) { + context.listensTo(Utils.idFromNan(mValue1), this); + } + if (Float.isNaN(mValue2)) { + context.listensTo(Utils.idFromNan(mValue2), this); + } + if (Float.isNaN(mValue3)) { + context.listensTo(Utils.idFromNan(mValue3), this); + } + } + + @Override + public void write(WireBuffer buffer) { + COMPANION.apply(buffer, mV1, mV2, mV3); + } + + @Override + public String toString() { + return mName + " " + floatToString(mV1) + " " + floatToString(mV2) + + " " + floatToString(mV3); + } + + public static class Companion implements CompanionOperation { + public final int OP_CODE; + + protected Companion(int code) { + OP_CODE = code; + } + + @Override + public void read(WireBuffer buffer, List<Operation> operations) { + float v1 = buffer.readFloat(); + float v2 = buffer.readFloat(); + float v3 = buffer.readFloat(); + + Operation op = construct(v1, v2, v3); + operations.add(op); + } + + /** + * Construct and Operation from the 3 variables. + * This must be overridden by subclasses + * @param x1 + * @param y1 + * @param x2 + * @return + */ + public Operation construct(float x1, + float y1, + float x2) { + return null; + } + + @Override + public String name() { + return "DrawRect"; + } + + @Override + public int id() { + return OP_CODE; + } + + /** + * Writes out the operation to the buffer + * @param buffer + * @param x1 + * @param y1 + * @param x2 + */ + public void apply(WireBuffer buffer, + float x1, + float y1, + float x2) { + buffer.start(OP_CODE); + buffer.writeFloat(x1); + buffer.writeFloat(y1); + buffer.writeFloat(x2); + + } + } +} diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase4.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase4.java new file mode 100644 index 000000000000..ec35a160079c --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase4.java @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.widget.remotecompose.core.operations; + +import static com.android.internal.widget.remotecompose.core.operations.Utils.floatToString; + +import com.android.internal.widget.remotecompose.core.CompanionOperation; +import com.android.internal.widget.remotecompose.core.Operation; +import com.android.internal.widget.remotecompose.core.Operations; +import com.android.internal.widget.remotecompose.core.PaintOperation; +import com.android.internal.widget.remotecompose.core.RemoteContext; +import com.android.internal.widget.remotecompose.core.VariableSupport; +import com.android.internal.widget.remotecompose.core.WireBuffer; + +import java.util.List; + +/** + * Base class for draw commands that take 4 floats + */ +public abstract class DrawBase4 extends PaintOperation + implements VariableSupport { + public static final Companion COMPANION = + new Companion(Operations.DRAW_RECT) { + @Override + public Operation construct(float x1, float y1, float x2, float y2) { + // return new DrawRectBase(x1, y1, x2, y2); + return null; + } + }; + protected String mName = "DrawRectBase"; + float mX1; + float mY1; + float mX2; + float mY2; + float mX1Value; + float mY1Value; + float mX2Value; + float mY2Value; + + public DrawBase4( + float x1, + float y1, + float x2, + float y2) { + mX1Value = x1; + mY1Value = y1; + mX2Value = x2; + mY2Value = y2; + + mX1 = x1; + mY1 = y1; + mX2 = x2; + mY2 = y2; + } + + @Override + public void updateVariables(RemoteContext context) { + mX1 = (Float.isNaN(mX1Value)) + ? context.getFloat(Utils.idFromNan(mX1Value)) : mX1Value; + mY1 = (Float.isNaN(mY1Value)) + ? context.getFloat(Utils.idFromNan(mY1Value)) : mY1Value; + mX2 = (Float.isNaN(mX2Value)) + ? context.getFloat(Utils.idFromNan(mX2Value)) : mX2Value; + mY2 = (Float.isNaN(mY2Value)) + ? context.getFloat(Utils.idFromNan(mY2Value)) : mY2Value; + } + + @Override + public void registerListening(RemoteContext context) { + if (Float.isNaN(mX1Value)) { + context.listensTo(Utils.idFromNan(mX1Value), this); + } + if (Float.isNaN(mY1Value)) { + context.listensTo(Utils.idFromNan(mY1Value), this); + } + if (Float.isNaN(mX2Value)) { + context.listensTo(Utils.idFromNan(mX2Value), this); + } + if (Float.isNaN(mY2Value)) { + context.listensTo(Utils.idFromNan(mY2Value), this); + } + } + + @Override + public void write(WireBuffer buffer) { + COMPANION.apply(buffer, mX1, mY1, mX2, mY2); + } + + @Override + public String toString() { + return mName + " " + floatToString(mX1Value, mX1) + " " + floatToString(mY1Value, mY1) + + " " + floatToString(mX2Value, mX2) + " " + floatToString(mY2Value, mY2); + } + + public static class Companion implements CompanionOperation { + public final int OP_CODE; + + protected Companion(int code) { + OP_CODE = code; + } + + @Override + public void read(WireBuffer buffer, List<Operation> operations) { + float sLeft = buffer.readFloat(); + float srcTop = buffer.readFloat(); + float srcRight = buffer.readFloat(); + float srcBottom = buffer.readFloat(); + + Operation op = construct(sLeft, srcTop, srcRight, srcBottom); + operations.add(op); + } + + /** + * Construct and Operation from the 3 variables. + * @param x1 + * @param y1 + * @param x2 + * @param y2 + * @return + */ + public Operation construct(float x1, + float y1, + float x2, + float y2) { + return null; + } + + @Override + public String name() { + return "DrawRect"; + } + + @Override + public int id() { + return OP_CODE; + } + + /** + * Writes out the operation to the buffer + * @param buffer + * @param x1 + * @param y1 + * @param x2 + * @param y2 + */ + public void apply(WireBuffer buffer, + float x1, + float y1, + float x2, + float y2) { + buffer.start(OP_CODE); + buffer.writeFloat(x1); + buffer.writeFloat(y1); + buffer.writeFloat(x2); + buffer.writeFloat(y2); + } + } +} diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase6.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase6.java new file mode 100644 index 000000000000..2f4335e7f412 --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBase6.java @@ -0,0 +1,202 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.widget.remotecompose.core.operations; + +import static com.android.internal.widget.remotecompose.core.operations.Utils.floatToString; + +import com.android.internal.widget.remotecompose.core.CompanionOperation; +import com.android.internal.widget.remotecompose.core.Operation; +import com.android.internal.widget.remotecompose.core.Operations; +import com.android.internal.widget.remotecompose.core.PaintOperation; +import com.android.internal.widget.remotecompose.core.RemoteContext; +import com.android.internal.widget.remotecompose.core.VariableSupport; +import com.android.internal.widget.remotecompose.core.WireBuffer; + +import java.util.List; + +/** + * Base class for draw commands the take 6 floats + */ +public abstract class DrawBase6 extends PaintOperation + implements VariableSupport { + public static final Companion COMPANION = + new Companion(Operations.DRAW_RECT) { + public Operation construct(float x1, float y1, float x2, float y2) { + // return new DrawRectBase(x1, y1, x2, y2); + return null; + } + }; + protected String mName = "DrawRectBase"; + float mV1; + float mV2; + float mV3; + float mV4; + float mV5; + float mV6; + float mValue1; + float mValue2; + float mValue3; + float mValue4; + float mValue5; + float mValue6; + + public DrawBase6( + float v1, + float v2, + float v3, + float v4, + float v5, + float v6) { + mValue1 = v1; + mValue2 = v2; + mValue3 = v3; + mValue4 = v4; + mValue5 = v5; + mValue6 = v6; + + mV1 = v1; + mV2 = v2; + mV3 = v3; + mV4 = v4; + mV5 = v5; + mV6 = v6; + } + + @Override + public void updateVariables(RemoteContext context) { + mV1 = (Float.isNaN(mValue1)) + ? context.getFloat(Utils.idFromNan(mValue1)) : mValue1; + mV2 = (Float.isNaN(mValue2)) + ? context.getFloat(Utils.idFromNan(mValue2)) : mValue2; + mV3 = (Float.isNaN(mValue3)) + ? context.getFloat(Utils.idFromNan(mValue3)) : mValue3; + mV4 = (Float.isNaN(mValue4)) + ? context.getFloat(Utils.idFromNan(mValue4)) : mValue4; + mV5 = (Float.isNaN(mValue5)) + ? context.getFloat(Utils.idFromNan(mValue5)) : mValue5; + mV6 = (Float.isNaN(mValue6)) + ? context.getFloat(Utils.idFromNan(mValue6)) : mValue6; + } + + @Override + public void registerListening(RemoteContext context) { + if (Float.isNaN(mValue1)) { + context.listensTo(Utils.idFromNan(mValue1), this); + } + if (Float.isNaN(mValue2)) { + context.listensTo(Utils.idFromNan(mValue2), this); + } + if (Float.isNaN(mValue3)) { + context.listensTo(Utils.idFromNan(mValue3), this); + } + if (Float.isNaN(mValue4)) { + context.listensTo(Utils.idFromNan(mValue4), this); + } + if (Float.isNaN(mValue5)) { + context.listensTo(Utils.idFromNan(mValue5), this); + } + if (Float.isNaN(mValue6)) { + context.listensTo(Utils.idFromNan(mValue6), this); + } + } + + @Override + public void write(WireBuffer buffer) { + COMPANION.apply(buffer, mV1, mV2, mV3, mV4, mV5, mV6); + } + + @Override + public String toString() { + return mName + " " + floatToString(mV1) + " " + floatToString(mV2) + + " " + floatToString(mV3) + " " + floatToString(mV4); + } + + public static class Companion implements CompanionOperation { + public final int OP_CODE; + + protected Companion(int code) { + OP_CODE = code; + } + + @Override + public void read(WireBuffer buffer, List<Operation> operations) { + float sv1 = buffer.readFloat(); + float sv2 = buffer.readFloat(); + float sv3 = buffer.readFloat(); + float sv4 = buffer.readFloat(); + float sv5 = buffer.readFloat(); + float sv6 = buffer.readFloat(); + + Operation op = construct(sv1, sv2, sv3, sv4, sv5, sv6); + operations.add(op); + } + + /** + * writes out a the operation to the buffer. + * @param v1 + * @param v2 + * @param v3 + * @param v4 + * @param v5 + * @param v6 + * @return + */ + public Operation construct(float v1, + float v2, + float v3, + float v4, + float v5, + float v6) { + return null; + } + + @Override + public String name() { + return "DrawRect"; + } + + @Override + public int id() { + return OP_CODE; + } + + /** + * Writes out the operation to the buffer + * @param buffer + * @param v1 + * @param v2 + * @param v3 + * @param v4 + * @param v5 + * @param v6 + */ + public void apply(WireBuffer buffer, + float v1, + float v2, + float v3, + float v4, + float v5, + float v6) { + buffer.start(OP_CODE); + buffer.writeFloat(v1); + buffer.writeFloat(v2); + buffer.writeFloat(v3); + buffer.writeFloat(v4); + buffer.writeFloat(v5); + buffer.writeFloat(v6); + } + } +} diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmap.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmap.java index 2e971f533ed2..ca40d12fd3f9 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmap.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawBitmap.java @@ -20,16 +20,22 @@ import com.android.internal.widget.remotecompose.core.Operation; import com.android.internal.widget.remotecompose.core.Operations; import com.android.internal.widget.remotecompose.core.PaintContext; import com.android.internal.widget.remotecompose.core.PaintOperation; +import com.android.internal.widget.remotecompose.core.RemoteContext; +import com.android.internal.widget.remotecompose.core.VariableSupport; import com.android.internal.widget.remotecompose.core.WireBuffer; import java.util.List; -public class DrawBitmap extends PaintOperation { +public class DrawBitmap extends PaintOperation implements VariableSupport { public static final Companion COMPANION = new Companion(); float mLeft; float mTop; float mRight; float mBottom; + float mOutputLeft; + float mOutputTop; + float mOutputRight; + float mOutputBottom; int mId; int mDescriptionId = 0; @@ -49,6 +55,34 @@ public class DrawBitmap extends PaintOperation { } @Override + public void updateVariables(RemoteContext context) { + mOutputLeft = (Float.isNaN(mLeft)) + ? context.getFloat(Utils.idFromNan(mLeft)) : mLeft; + mOutputTop = (Float.isNaN(mTop)) + ? context.getFloat(Utils.idFromNan(mTop)) : mTop; + mOutputRight = (Float.isNaN(mRight)) + ? context.getFloat(Utils.idFromNan(mRight)) : mRight; + mOutputBottom = (Float.isNaN(mBottom)) + ? context.getFloat(Utils.idFromNan(mBottom)) : mBottom; + } + + @Override + public void registerListening(RemoteContext context) { + if (Float.isNaN(mLeft)) { + context.listensTo(Utils.idFromNan(mLeft), this); + } + if (Float.isNaN(mTop)) { + context.listensTo(Utils.idFromNan(mTop), this); + } + if (Float.isNaN(mRight)) { + context.listensTo(Utils.idFromNan(mRight), this); + } + if (Float.isNaN(mBottom)) { + context.listensTo(Utils.idFromNan(mBottom), this); + } + } + + @Override public void write(WireBuffer buffer) { COMPANION.apply(buffer, mId, mLeft, mTop, mRight, mBottom, mDescriptionId); } @@ -105,9 +139,9 @@ public class DrawBitmap extends PaintOperation { @Override public void paint(PaintContext context) { - context.drawBitmap(mId, mLeft, - mTop, - mRight, - mBottom); + context.drawBitmap(mId, mOutputLeft, + mOutputTop, + mOutputRight, + mOutputBottom); } } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawCircle.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawCircle.java index 9ce754da1b1b..3a22e4f72720 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawCircle.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawCircle.java @@ -1,89 +1,31 @@ -/* - * Copyright (C) 2024 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ package com.android.internal.widget.remotecompose.core.operations; -import com.android.internal.widget.remotecompose.core.CompanionOperation; import com.android.internal.widget.remotecompose.core.Operation; import com.android.internal.widget.remotecompose.core.Operations; import com.android.internal.widget.remotecompose.core.PaintContext; -import com.android.internal.widget.remotecompose.core.PaintOperation; -import com.android.internal.widget.remotecompose.core.WireBuffer; -import java.util.List; - -public class DrawCircle extends PaintOperation { - public static final Companion COMPANION = new Companion(); - float mCenterX; - float mCenterY; - float mRadius; - - public DrawCircle(float centerX, float centerY, float radius) { - mCenterX = centerX; - mCenterY = centerY; - mRadius = radius; - } - - @Override - public void write(WireBuffer buffer) { - COMPANION.apply(buffer, mCenterX, - mCenterY, - mRadius); - } - - @Override - public String toString() { - return ""; - } - - public static class Companion implements CompanionOperation { - private Companion() { - } - - @Override - public void read(WireBuffer buffer, List<Operation> operations) { - float centerX = buffer.readFloat(); - float centerY = buffer.readFloat(); - float radius = buffer.readFloat(); - - DrawCircle op = new DrawCircle(centerX, centerY, radius); - operations.add(op); - } - - @Override - public String name() { - return ""; - } - - @Override - public int id() { - return 0; - } - - public void apply(WireBuffer buffer, float centerX, float centerY, float radius) { - buffer.start(Operations.DRAW_CIRCLE); - buffer.writeFloat(centerX); - buffer.writeFloat(centerY); - buffer.writeFloat(radius); - } +public class DrawCircle extends DrawBase3 { + public static final Companion COMPANION = + new Companion(Operations.DRAW_CIRCLE) { + @Override + public Operation construct(float x1, + float y1, + float x2 + ) { + return new DrawCircle(x1, y1, x2); + } + }; + + public DrawCircle( + float left, + float top, + float right) { + super(left, top, right); + mName = "DrawCircle"; } @Override public void paint(PaintContext context) { - context.drawCircle(mCenterX, - mCenterY, - mRadius); + context.drawCircle(mV1, mV2, mV3); } } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawLine.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawLine.java index c7a8315a2274..c70c6eaa449d 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawLine.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawLine.java @@ -15,83 +15,28 @@ */ package com.android.internal.widget.remotecompose.core.operations; -import com.android.internal.widget.remotecompose.core.CompanionOperation; import com.android.internal.widget.remotecompose.core.Operation; import com.android.internal.widget.remotecompose.core.Operations; import com.android.internal.widget.remotecompose.core.PaintContext; -import com.android.internal.widget.remotecompose.core.PaintOperation; -import com.android.internal.widget.remotecompose.core.WireBuffer; - -import java.util.List; - -public class DrawLine extends PaintOperation { - public static final Companion COMPANION = new Companion(); - float mX1; - float mY1; - float mX2; - float mY2; - - public DrawLine( - float x1, - float y1, - float x2, - float y2) { - mX1 = x1; - mY1 = y1; - mX2 = x2; - mY2 = y2; - } - - @Override - public void write(WireBuffer buffer) { - COMPANION.apply(buffer, mX1, - mY1, - mX2, - mY2); - } - - @Override - public String toString() { - return "DrawArc " + mX1 + " " + mY1 - + " " + mX2 + " " + mY2 + ";"; - } - - public static class Companion implements CompanionOperation { - private Companion() { - } - - @Override - public void read(WireBuffer buffer, List<Operation> operations) { - float x1 = buffer.readFloat(); - float y1 = buffer.readFloat(); - float x2 = buffer.readFloat(); - float y2 = buffer.readFloat(); - - DrawLine op = new DrawLine(x1, y1, x2, y2); - operations.add(op); - } - - @Override - public String name() { - return "DrawLine"; - } +public class DrawLine extends DrawBase4 { + public static final Companion COMPANION = new Companion(Operations.DRAW_LINE) { @Override - public int id() { - return Operations.DRAW_LINE; + public Operation construct(float x1, + float y1, + float x2, + float y2) { + return new DrawLine(x1, y1, x2, y2); } + }; - public void apply(WireBuffer buffer, - float x1, - float y1, - float x2, - float y2) { - buffer.start(Operations.DRAW_LINE); - buffer.writeFloat(x1); - buffer.writeFloat(y1); - buffer.writeFloat(x2); - buffer.writeFloat(y2); - } + public DrawLine( + float left, + float top, + float right, + float bottom) { + super(left, top, right, bottom); + mName = "DrawLine"; } @Override diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawOval.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawOval.java index 714375335cb2..ba1799422e80 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawOval.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawOval.java @@ -15,88 +15,33 @@ */ package com.android.internal.widget.remotecompose.core.operations; -import com.android.internal.widget.remotecompose.core.CompanionOperation; import com.android.internal.widget.remotecompose.core.Operation; import com.android.internal.widget.remotecompose.core.Operations; import com.android.internal.widget.remotecompose.core.PaintContext; -import com.android.internal.widget.remotecompose.core.PaintOperation; -import com.android.internal.widget.remotecompose.core.WireBuffer; - -import java.util.List; - -public class DrawOval extends PaintOperation { - public static final Companion COMPANION = new Companion(); - float mLeft; - float mTop; - float mRight; - float mBottom; +public class DrawOval extends DrawBase4 { + public static final Companion COMPANION = + new Companion(Operations.DRAW_OVAL) { + @Override + public Operation construct(float x1, + float y1, + float x2, + float y2) { + return new DrawOval(x1, y1, x2, y2); + } + }; public DrawOval( float left, float top, float right, float bottom) { - mLeft = left; - mTop = top; - mRight = right; - mBottom = bottom; - } - - @Override - public void write(WireBuffer buffer) { - COMPANION.apply(buffer, mLeft, mTop, mRight, mBottom); - } - - @Override - public String toString() { - return "DrawOval " + mLeft + " " + mTop - + " " + mRight + " " + mBottom + ";"; - } - - public static class Companion implements CompanionOperation { - private Companion() { - } - - @Override - public void read(WireBuffer buffer, List<Operation> operations) { - float sLeft = buffer.readFloat(); - float srcTop = buffer.readFloat(); - float srcRight = buffer.readFloat(); - float srcBottom = buffer.readFloat(); - - DrawOval op = new DrawOval(sLeft, srcTop, srcRight, srcBottom); - operations.add(op); - } - - @Override - public String name() { - return "DrawOval"; - } - - @Override - public int id() { - return Operations.DRAW_OVAL; - } - - public void apply(WireBuffer buffer, - float left, - float top, - float right, - float bottom) { - buffer.start(Operations.DRAW_OVAL); - buffer.writeFloat(left); - buffer.writeFloat(top); - buffer.writeFloat(right); - buffer.writeFloat(bottom); - } + super(left, top, right, bottom); + mName = "DrawOval"; } @Override public void paint(PaintContext context) { - context.drawOval(mLeft, - mTop, - mRight, - mBottom); + context.drawOval(mX1, mY1, mX2, mY2); } } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawPath.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawPath.java index 7b8a9e95d9cb..6dbc5a628c98 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawPath.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawPath.java @@ -41,7 +41,7 @@ public class DrawPath extends PaintOperation { @Override public String toString() { - return "DrawPath " + ";"; + return "DrawPath " + "[" + mId + "]" + ", " + mStart + ", " + mEnd; } public static class Companion implements CompanionOperation { diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRect.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRect.java index 4775241faa6f..633aed4a4dbe 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRect.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRect.java @@ -15,88 +15,37 @@ */ package com.android.internal.widget.remotecompose.core.operations; -import com.android.internal.widget.remotecompose.core.CompanionOperation; import com.android.internal.widget.remotecompose.core.Operation; import com.android.internal.widget.remotecompose.core.Operations; import com.android.internal.widget.remotecompose.core.PaintContext; -import com.android.internal.widget.remotecompose.core.PaintOperation; -import com.android.internal.widget.remotecompose.core.WireBuffer; -import java.util.List; - -public class DrawRect extends PaintOperation { - public static final Companion COMPANION = new Companion(); - float mLeft; - float mTop; - float mRight; - float mBottom; +/** + * Draw a Rectangle + */ +public class DrawRect extends DrawBase4 { + public static final Companion COMPANION = + new Companion(Operations.DRAW_RECT) { + @Override + public Operation construct(float x1, + float y1, + float x2, + float y2) { + return new DrawRect(x1, y1, x2, y2); + } + }; public DrawRect( float left, float top, float right, float bottom) { - mLeft = left; - mTop = top; - mRight = right; - mBottom = bottom; - } - - @Override - public void write(WireBuffer buffer) { - COMPANION.apply(buffer, mLeft, mTop, mRight, mBottom); - } - - @Override - public String toString() { - return "DrawRect " + mLeft + " " + mTop - + " " + mRight + " " + mBottom + ";"; - } - - public static class Companion implements CompanionOperation { - private Companion() { - } - - @Override - public void read(WireBuffer buffer, List<Operation> operations) { - float sLeft = buffer.readFloat(); - float srcTop = buffer.readFloat(); - float srcRight = buffer.readFloat(); - float srcBottom = buffer.readFloat(); - - DrawRect op = new DrawRect(sLeft, srcTop, srcRight, srcBottom); - operations.add(op); - } - - @Override - public String name() { - return "DrawRect"; - } - - @Override - public int id() { - return Operations.DRAW_RECT; - } - - public void apply(WireBuffer buffer, - float left, - float top, - float right, - float bottom) { - buffer.start(Operations.DRAW_RECT); - buffer.writeFloat(left); - buffer.writeFloat(top); - buffer.writeFloat(right); - buffer.writeFloat(bottom); - } + super(left, top, right, bottom); + mName = "DrawRect"; } @Override public void paint(PaintContext context) { - context.drawRect(mLeft, - mTop, - mRight, - mBottom); + context.drawRect(mX1, mY1, mX2, mY2); } } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRoundRect.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRoundRect.java index 8da16e768b7f..b9d0a6728b95 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRoundRect.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawRoundRect.java @@ -15,104 +15,40 @@ */ package com.android.internal.widget.remotecompose.core.operations; -import com.android.internal.widget.remotecompose.core.CompanionOperation; import com.android.internal.widget.remotecompose.core.Operation; import com.android.internal.widget.remotecompose.core.Operations; import com.android.internal.widget.remotecompose.core.PaintContext; -import com.android.internal.widget.remotecompose.core.PaintOperation; -import com.android.internal.widget.remotecompose.core.WireBuffer; -import java.util.List; - -public class DrawRoundRect extends PaintOperation { - public static final Companion COMPANION = new Companion(); - float mLeft; - float mTop; - float mRight; - float mBottom; - float mRadiusX; - float mRadiusY; - - public DrawRoundRect( - float left, - float top, - float right, - float bottom, - float radiusX, - float radiusY) { - mLeft = left; - mTop = top; - mRight = right; - mBottom = bottom; - mRadiusX = radiusX; - mRadiusY = radiusY; - } - - @Override - public void write(WireBuffer buffer) { - COMPANION.apply(buffer, mLeft, mTop, mRight, mBottom, mRadiusX, mRadiusY); - } - - @Override - public String toString() { - return "DrawRoundRect " + mLeft + " " + mTop - + " " + mRight + " " + mBottom - + " (" + mRadiusX + " " + mRadiusY + ");"; - } - - public static class Companion implements CompanionOperation { - private Companion() { - } - - @Override - public void read(WireBuffer buffer, List<Operation> operations) { - float sLeft = buffer.readFloat(); - float srcTop = buffer.readFloat(); - float srcRight = buffer.readFloat(); - float srcBottom = buffer.readFloat(); - float srcRadiusX = buffer.readFloat(); - float srcRadiusY = buffer.readFloat(); - - DrawRoundRect op = new DrawRoundRect(sLeft, srcTop, srcRight, - srcBottom, srcRadiusX, srcRadiusY); - operations.add(op); - } - - @Override - public String name() { - return "DrawOval"; - } - - @Override - public int id() { - return Operations.DRAW_ROUND_RECT; - } - - public void apply(WireBuffer buffer, - float left, - float top, - float right, - float bottom, - float radiusX, - float radiusY) { - buffer.start(Operations.DRAW_ROUND_RECT); - buffer.writeFloat(left); - buffer.writeFloat(top); - buffer.writeFloat(right); - buffer.writeFloat(bottom); - buffer.writeFloat(radiusX); - buffer.writeFloat(radiusY); - } +/** + * Draw a rounded rectangle + */ +public class DrawRoundRect extends DrawBase6 { + public static final Companion COMPANION = + new Companion(Operations.DRAW_ROUND_RECT) { + @Override + public Operation construct(float v1, + float v2, + float v3, + float v4, + float v5, + float v6) { + return new DrawRoundRect(v1, v2, v3, v4, v5, v6); + } + }; + + public DrawRoundRect(float v1, + float v2, + float v3, + float v4, + float v5, + float v6) { + super(v1, v2, v3, v4, v5, v6); + mName = "ClipRect"; } @Override public void paint(PaintContext context) { - context.drawRoundRect(mLeft, - mTop, - mRight, - mBottom, - mRadiusX, - mRadiusY + context.drawRoundRect(mV1, mV2, mV3, mV4, mV5, mV6 ); } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawText.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawText.java new file mode 100644 index 000000000000..f8f8afdf68cd --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawText.java @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.widget.remotecompose.core.operations; + +import com.android.internal.widget.remotecompose.core.CompanionOperation; +import com.android.internal.widget.remotecompose.core.Operation; +import com.android.internal.widget.remotecompose.core.Operations; +import com.android.internal.widget.remotecompose.core.PaintContext; +import com.android.internal.widget.remotecompose.core.PaintOperation; +import com.android.internal.widget.remotecompose.core.WireBuffer; + +import java.util.List; + +/** + * Draw Text + */ +public class DrawText extends PaintOperation { + public static final Companion COMPANION = new Companion(); + int mTextID; + int mStart = 0; + int mEnd = 0; + int mContextStart = 0; + int mContextEnd = 0; + float mX = 0f; + float mY = 0f; + boolean mRtl = false; + + public DrawText(int textID, + int start, + int end, + int contextStart, + int contextEnd, + float x, + float y, + boolean rtl) { + mTextID = textID; + mStart = start; + mEnd = end; + mContextStart = contextStart; + mContextEnd = contextEnd; + mX = x; + mY = y; + mRtl = rtl; + } + + @Override + public void write(WireBuffer buffer) { + COMPANION.apply(buffer, mTextID, mStart, mEnd, mContextStart, mContextEnd, mX, mY, mRtl); + + } + + @Override + public String toString() { + return "DrawTextRun [" + mTextID + "] " + mStart + ", " + mEnd + ", " + mX + ", " + mY; + } + + public static class Companion implements CompanionOperation { + private Companion() { + } + + @Override + public void read(WireBuffer buffer, List<Operation> operations) { + int text = buffer.readInt(); + int start = buffer.readInt(); + int end = buffer.readInt(); + int contextStart = buffer.readInt(); + int contextEnd = buffer.readInt(); + float x = buffer.readFloat(); + float y = buffer.readFloat(); + boolean rtl = buffer.readBoolean(); + DrawText op = new DrawText(text, start, end, contextStart, contextEnd, x, y, rtl); + + operations.add(op); + } + + @Override + public String name() { + return ""; + } + + @Override + public int id() { + return 0; + } + + /** + * Writes out the operation to the buffer + * @param buffer + * @param textID + * @param start + * @param end + * @param contextStart + * @param contextEnd + * @param x + * @param y + * @param rtl + */ + public void apply(WireBuffer buffer, + int textID, + int start, + int end, + int contextStart, + int contextEnd, + float x, + float y, + boolean rtl) { + buffer.start(Operations.DRAW_TEXT_RUN); + buffer.writeInt(textID); + buffer.writeInt(start); + buffer.writeInt(end); + buffer.writeInt(contextStart); + buffer.writeInt(contextEnd); + buffer.writeFloat(x); + buffer.writeFloat(y); + buffer.writeBoolean(rtl); + } + } + + @Override + public void paint(PaintContext context) { + context.drawTextRun(mTextID, mStart, mEnd, mContextStart, mContextEnd, mX, mY, mRtl); + } +} diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextAnchored.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextAnchored.java new file mode 100644 index 000000000000..4f0641f34d84 --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextAnchored.java @@ -0,0 +1,204 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.widget.remotecompose.core.operations; + +import com.android.internal.widget.remotecompose.core.CompanionOperation; +import com.android.internal.widget.remotecompose.core.Operation; +import com.android.internal.widget.remotecompose.core.Operations; +import com.android.internal.widget.remotecompose.core.PaintContext; +import com.android.internal.widget.remotecompose.core.PaintOperation; +import com.android.internal.widget.remotecompose.core.RemoteContext; +import com.android.internal.widget.remotecompose.core.VariableSupport; +import com.android.internal.widget.remotecompose.core.WireBuffer; + +import java.util.List; + +/** + * Draw Text in Anchored to a point + */ +public class DrawTextAnchored extends PaintOperation implements VariableSupport { + public static final Companion COMPANION = new Companion(); + int mTextID; + float mX; + float mY; + float mPanX; + float mPanY; + int mFlags; + float mOutX; + float mOutY; + float mOutPanX; + float mOutPanY; + + public static final int ANCHOR_TEXT_RTL = 1; + public static final int ANCHOR_MONOSPACE_MEASURE = 2; + + public DrawTextAnchored(int textID, + float x, + float y, + float panX, + float panY, + int flags) { + mTextID = textID; + mX = x; + mY = y; + mOutX = mX; + mOutY = mY; + mFlags = flags; + mOutPanX = mPanX = panX; + mOutPanY = mPanY = panY; + } + + @Override + public void updateVariables(RemoteContext context) { + mOutX = (Float.isNaN(mX)) + ? context.getFloat(Utils.idFromNan(mX)) : mX; + mOutY = (Float.isNaN(mY)) + ? context.getFloat(Utils.idFromNan(mY)) : mY; + mOutPanX = (Float.isNaN(mPanX)) + ? context.getFloat(Utils.idFromNan(mPanX)) : mPanX; + mOutPanY = (Float.isNaN(mPanY)) + ? context.getFloat(Utils.idFromNan(mPanY)) : mPanY; + } + + @Override + public void registerListening(RemoteContext context) { + if (Float.isNaN(mX)) { + context.listensTo(Utils.idFromNan(mX), this); + } + if (Float.isNaN(mY)) { + context.listensTo(Utils.idFromNan(mY), this); + } + if (Float.isNaN(mPanX)) { + context.listensTo(Utils.idFromNan(mPanX), this); + } + if (Float.isNaN(mPanY) && Utils.idFromNan(mPanY) > 0) { + context.listensTo(Utils.idFromNan(mPanY), this); + } + } + + @Override + public void write(WireBuffer buffer) { + COMPANION.apply(buffer, mTextID, mX, + mY, + mPanX, + mPanY, + mFlags); + } + + @Override + public String toString() { + return "DrawTextAnchored [" + mTextID + "] " + floatToStr(mX) + ", " + + floatToStr(mY) + ", " + + floatToStr(mPanX) + ", " + floatToStr(mPanY) + ", " + + Integer.toBinaryString(mFlags); + } + + private static String floatToStr(float v) { + if (Float.isNaN(v)) { + return "[" + Utils.idFromNan(v) + "]"; + } + return Float.toString(v); + } + + public static class Companion implements CompanionOperation { + private Companion() { + } + + @Override + public void read(WireBuffer buffer, List<Operation> operations) { + int textID = buffer.readInt(); + float x = buffer.readFloat(); + float y = buffer.readFloat(); + float panX = buffer.readFloat(); + float panY = buffer.readFloat(); + int flags = buffer.readInt(); + + DrawTextAnchored op = new DrawTextAnchored(textID, + x, y, + panX, panY, + flags); + + operations.add(op); + } + + @Override + public String name() { + return ""; + } + + @Override + public int id() { + return 0; + } + + /** + * Writes out the operation to the buffer + * @param buffer + * @param textID + * @param x + * @param y + * @param panX + * @param panY + * @param flags + */ + public void apply(WireBuffer buffer, + int textID, + float x, + float y, + float panX, + float panY, + int flags) { + buffer.start(Operations.DRAW_TEXT_ANCHOR); + buffer.writeInt(textID); + buffer.writeFloat(x); + buffer.writeFloat(y); + buffer.writeFloat(panX); + buffer.writeFloat(panY); + buffer.writeInt(flags); + } + } + + float[] mBounds = new float[4]; + + private float getHorizontalOffset() { + // TODO scale TextSize / BaseTextSize; + float scale = 1.0f; + + float textWidth = scale * (mBounds[2] - mBounds[0]); + float boxWidth = 0; + return (boxWidth - textWidth) * (1 + mOutPanX) / 2.f + - (scale * mBounds[0]); + } + + private float getVerticalOffset() { + // TODO scale TextSize / BaseTextSize; + float scale = 1.0f; + float boxHeight = 0; + float textHeight = scale * (mBounds[3] - mBounds[1]); + return (boxHeight - textHeight) * (1 - mOutPanY) / 2 + - (scale * mBounds[1]); + } + + @Override + public void paint(PaintContext context) { + context.getTextBounds(mTextID, 0, -1, + (mFlags & ANCHOR_MONOSPACE_MEASURE) != 0, mBounds); + float x = mOutX + getHorizontalOffset(); + float y = (Float.isNaN(mOutPanY)) ? mOutY : mOutY + getVerticalOffset(); + context.drawTextRun(mTextID, 0, -1, 0, 1, x, y, + (mFlags & ANCHOR_TEXT_RTL) == 1); + } +} diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextOnPath.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextOnPath.java index 1856e3097ec0..b1a01724c315 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextOnPath.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTextOnPath.java @@ -24,6 +24,9 @@ import com.android.internal.widget.remotecompose.core.WireBuffer; import java.util.List; +/** + * Draw text along a path. + */ public class DrawTextOnPath extends PaintOperation { public static final Companion COMPANION = new Companion(); int mPathId; @@ -45,7 +48,8 @@ public class DrawTextOnPath extends PaintOperation { @Override public String toString() { - return "DrawTextOnPath " + " " + mPathId + ";"; + return "DrawTextOnPath [" + mTextId + "] [" + mPathId + "] " + + mHOffset + ", " + mVOffset; } public static class Companion implements CompanionOperation { diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTweenPath.java b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTweenPath.java index ef0a4ad2eff3..48fc94ee5f27 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTweenPath.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/DrawTweenPath.java @@ -58,7 +58,7 @@ public class DrawTweenPath extends PaintOperation { public String toString() { return "DrawTweenPath " + mPath1Id + " " + mPath2Id + " " + mTween + " " + mStart + " " - + "- " + mStop + ";"; + + "- " + mStop; } public static class Companion implements CompanionOperation { diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/FloatConstant.java b/core/java/com/android/internal/widget/remotecompose/core/operations/FloatConstant.java new file mode 100644 index 000000000000..576b53f9fc6c --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/FloatConstant.java @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.widget.remotecompose.core.operations; + +import com.android.internal.widget.remotecompose.core.CompanionOperation; +import com.android.internal.widget.remotecompose.core.Operation; +import com.android.internal.widget.remotecompose.core.Operations; +import com.android.internal.widget.remotecompose.core.RemoteContext; +import com.android.internal.widget.remotecompose.core.WireBuffer; + +import java.util.List; + +/** + * Operation to deal with Text data + */ +public class FloatConstant implements Operation { + public int mTextId; + public float mValue; + public static final Companion COMPANION = new Companion(); + public static final int MAX_STRING_SIZE = 4000; + + public FloatConstant(int textId, float value) { + this.mTextId = textId; + this.mValue = value; + } + + @Override + public void write(WireBuffer buffer) { + COMPANION.apply(buffer, mTextId, mValue); + } + + @Override + public String toString() { + return "FloatConstant[" + mTextId + "] = " + mValue + ""; + } + + public static class Companion implements CompanionOperation { + private Companion() {} + + @Override + public String name() { + return "FloatExpression"; + } + + @Override + public int id() { + return Operations.DATA_FLOAT; + } + + /** + * Writes out the operation to the buffer + * @param buffer + * @param textId + * @param value + */ + public void apply(WireBuffer buffer, int textId, float value) { + buffer.start(Operations.DATA_FLOAT); + buffer.writeInt(textId); + buffer.writeFloat(value); + } + + @Override + public void read(WireBuffer buffer, List<Operation> operations) { + int textId = buffer.readInt(); + + float value = buffer.readFloat(); + operations.add(new FloatConstant(textId, value)); + } + } + + @Override + public void apply(RemoteContext context) { + context.loadFloat(mTextId, mValue); + } + + @Override + public String deepToString(String indent) { + return indent + toString(); + } +} diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/FloatExpression.java b/core/java/com/android/internal/widget/remotecompose/core/operations/FloatExpression.java new file mode 100644 index 000000000000..354f41b813e0 --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/FloatExpression.java @@ -0,0 +1,206 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.widget.remotecompose.core.operations; + +import com.android.internal.widget.remotecompose.core.CompanionOperation; +import com.android.internal.widget.remotecompose.core.Operation; +import com.android.internal.widget.remotecompose.core.Operations; +import com.android.internal.widget.remotecompose.core.RemoteContext; +import com.android.internal.widget.remotecompose.core.VariableSupport; +import com.android.internal.widget.remotecompose.core.WireBuffer; +import com.android.internal.widget.remotecompose.core.operations.utilities.AnimatedFloatExpression; +import com.android.internal.widget.remotecompose.core.operations.utilities.easing.FloatAnimation; + +import java.util.Arrays; +import java.util.List; + +/** + * Operation to deal with AnimatedFloats + * This is designed to be an optimized calculation for things like + * injecting the width of the component int draw rect + * As well as supporting generalized animation floats. + * The floats represent a RPN style calculator + */ +public class FloatExpression implements Operation, VariableSupport { + public int mId; + public float[] mSrcValue; + public float[] mSrcAnimation; + public FloatAnimation mFloatAnimation; + public float[] mPreCalcValue; + private float mLastChange = Float.NaN; + AnimatedFloatExpression mExp = new AnimatedFloatExpression(); + public static final Companion COMPANION = new Companion(); + public static final int MAX_STRING_SIZE = 4000; + + public FloatExpression(int id, float[] value, float[] animation) { + this.mId = id; + this.mSrcValue = value; + this.mSrcAnimation = animation; + if (mSrcAnimation != null) { + mFloatAnimation = new FloatAnimation(mSrcAnimation); + } + } + + @Override + public void updateVariables(RemoteContext context) { + if (mPreCalcValue == null || mPreCalcValue.length != mSrcValue.length) { + mPreCalcValue = new float[mSrcValue.length]; + } + //Utils.log("updateVariables "); + boolean value_changed = false; + for (int i = 0; i < mSrcValue.length; i++) { + float v = mSrcValue[i]; + if (Float.isNaN(v) && !AnimatedFloatExpression.isMathOperator(v)) { + float newValue = context.getFloat(Utils.idFromNan(v)); + if (mFloatAnimation != null) { + if (mPreCalcValue[i] != newValue) { + mLastChange = context.getAnimationTime(); + value_changed = true; + mPreCalcValue[i] = newValue; + } + } else { + mPreCalcValue[i] = newValue; + } + } else { + mPreCalcValue[i] = mSrcValue[i]; + } + } + if (value_changed && mFloatAnimation != null) { + float v = mExp.eval(Arrays.copyOf(mPreCalcValue, mPreCalcValue.length)); + if (Float.isNaN(mFloatAnimation.getTargetValue())) { + mFloatAnimation.setInitialValue(v); + } else { + mFloatAnimation.setInitialValue(mFloatAnimation.getTargetValue()); + } + mFloatAnimation.setTargetValue(v); + } + } + + @Override + public void registerListening(RemoteContext context) { + for (int i = 0; i < mSrcValue.length; i++) { + float v = mSrcValue[i]; + if (Float.isNaN(v) && !AnimatedFloatExpression.isMathOperator(v)) { + context.listensTo(Utils.idFromNan(v), this); + } + } + } + + @Override + public void apply(RemoteContext context) { + updateVariables(context); + float t = context.getAnimationTime(); + if (Float.isNaN(mLastChange)) { + mLastChange = t; + } + if (mFloatAnimation != null) { + float f = mFloatAnimation.get(t - mLastChange); + context.loadFloat(mId, f); + } else { + context.loadFloat(mId, mExp.eval(Arrays.copyOf(mPreCalcValue, mPreCalcValue.length))); + } + } + + @Override + public void write(WireBuffer buffer) { + COMPANION.apply(buffer, mId, mSrcValue, mSrcAnimation); + } + + @Override + public String toString() { + String[] labels = new String[mSrcValue.length]; + for (int i = 0; i < mSrcValue.length; i++) { + if (Float.isNaN(mSrcValue[i])) { + labels[i] = "[" + Utils.idFromNan(mSrcValue[i]) + "]"; + } + + } + return "FloatExpression[" + mId + "] = (" + + AnimatedFloatExpression.toString(mPreCalcValue, labels) + ")"; + } + + public static class Companion implements CompanionOperation { + private Companion() { + } + + @Override + public String name() { + return "FloatExpression"; + } + + @Override + public int id() { + return Operations.ANIMATED_FLOAT; + } + + /** + * Writes out the operation to the buffer + * @param buffer + * @param id + * @param value + * @param animation + */ + public void apply(WireBuffer buffer, int id, float[] value, float[] animation) { + buffer.start(Operations.ANIMATED_FLOAT); + buffer.writeInt(id); + + int len = value.length; + if (animation != null) { + len |= (animation.length << 16); + } + buffer.writeInt(len); + + for (int i = 0; i < value.length; i++) { + buffer.writeFloat(value[i]); + } + if (animation != null) { + for (int i = 0; i < animation.length; i++) { + buffer.writeFloat(animation[i]); + } + } + + } + + @Override + public void read(WireBuffer buffer, List<Operation> operations) { + int id = buffer.readInt(); + int len = buffer.readInt(); + int valueLen = len & 0xFFFF; + int animLen = (len >> 16) & 0xFFFF; + float[] values = new float[valueLen]; + for (int i = 0; i < values.length; i++) { + values[i] = buffer.readFloat(); + } + + float[] animation; + if (animLen != 0) { + animation = new float[animLen]; + for (int i = 0; i < animation.length; i++) { + animation[i] = buffer.readFloat(); + } + } else { + animation = null; + } + operations.add(new FloatExpression(id, values, animation)); + } + } + + @Override + public String deepToString(String indent) { + return indent + toString(); + } + +} diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRestore.java b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRestore.java index 482e0e22bd57..0dad45ce356b 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRestore.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRestore.java @@ -37,7 +37,7 @@ public class MatrixRestore extends PaintOperation { @Override public String toString() { - return "MatrixRestore;"; + return "MatrixRestore"; } public static class Companion implements CompanionOperation { diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRotate.java b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRotate.java index d6c89e0d2c64..bbf41351a1c5 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRotate.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixRotate.java @@ -15,68 +15,29 @@ */ package com.android.internal.widget.remotecompose.core.operations; -import com.android.internal.widget.remotecompose.core.CompanionOperation; import com.android.internal.widget.remotecompose.core.Operation; import com.android.internal.widget.remotecompose.core.Operations; import com.android.internal.widget.remotecompose.core.PaintContext; -import com.android.internal.widget.remotecompose.core.PaintOperation; -import com.android.internal.widget.remotecompose.core.WireBuffer; -import java.util.List; - -public class MatrixRotate extends PaintOperation { - public static final Companion COMPANION = new Companion(); - float mRotate, mPivotX, mPivotY; +public class MatrixRotate extends DrawBase3 { + public static final Companion COMPANION = + new Companion(Operations.MATRIX_ROTATE) { + @Override + public Operation construct(float rotate, + float pivotX, + float pivotY + ) { + return new MatrixRotate(rotate, pivotX, pivotY); + } + }; public MatrixRotate(float rotate, float pivotX, float pivotY) { - mRotate = rotate; - mPivotX = pivotX; - mPivotY = pivotY; - } - - @Override - public void write(WireBuffer buffer) { - COMPANION.apply(buffer, mRotate, mPivotX, mPivotY); - } - - @Override - public String toString() { - return "DrawArc " + mRotate + ", " + mPivotX + ", " + mPivotY + ";"; - } - - public static class Companion implements CompanionOperation { - private Companion() { - } - - @Override - public void read(WireBuffer buffer, List<Operation> operations) { - float rotate = buffer.readFloat(); - float pivotX = buffer.readFloat(); - float pivotY = buffer.readFloat(); - MatrixRotate op = new MatrixRotate(rotate, pivotX, pivotY); - operations.add(op); - } - - @Override - public String name() { - return "Matrix"; - } - - @Override - public int id() { - return Operations.MATRIX_ROTATE; - } - - public void apply(WireBuffer buffer, float rotate, float pivotX, float pivotY) { - buffer.start(Operations.MATRIX_ROTATE); - buffer.writeFloat(rotate); - buffer.writeFloat(pivotX); - buffer.writeFloat(pivotY); - } + super(rotate, pivotX, pivotY); + mName = "MatrixRotate"; } @Override public void paint(PaintContext context) { - context.matrixRotate(mRotate, mPivotX, mPivotY); + context.matrixRotate(mV1, mV2, mV3); } } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixScale.java b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixScale.java index 28aa68dd5884..04b940ba16c8 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixScale.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixScale.java @@ -15,74 +15,30 @@ */ package com.android.internal.widget.remotecompose.core.operations; -import com.android.internal.widget.remotecompose.core.CompanionOperation; import com.android.internal.widget.remotecompose.core.Operation; import com.android.internal.widget.remotecompose.core.Operations; import com.android.internal.widget.remotecompose.core.PaintContext; -import com.android.internal.widget.remotecompose.core.PaintOperation; -import com.android.internal.widget.remotecompose.core.WireBuffer; -import java.util.List; - -public class MatrixScale extends PaintOperation { - public static final Companion COMPANION = new Companion(); - float mScaleX, mScaleY; - float mCenterX, mCenterY; +public class MatrixScale extends DrawBase4 { + public static final Companion COMPANION = + new Companion(Operations.MATRIX_SCALE) { + @Override + public Operation construct(float scaleX, + float scaleY, + float centerX, + float centerY + ) { + return new MatrixScale(scaleX, scaleY, centerX, centerY); + } + }; public MatrixScale(float scaleX, float scaleY, float centerX, float centerY) { - mScaleX = scaleX; - mScaleY = scaleY; - mCenterX = centerX; - mCenterY = centerY; - } - - @Override - public void write(WireBuffer buffer) { - COMPANION.apply(buffer, mScaleX, mScaleY, mCenterX, mCenterY); - } - - @Override - public String toString() { - return "MatrixScale " + mScaleY + ", " + mScaleY + ";"; - } - - public static class Companion implements CompanionOperation { - private Companion() { - } - - @Override - public void read(WireBuffer buffer, List<Operation> operations) { - float scaleX = buffer.readFloat(); - float scaleY = buffer.readFloat(); - float centerX = buffer.readFloat(); - float centerY = buffer.readFloat(); - MatrixScale op = new MatrixScale(scaleX, scaleY, centerX, centerY); - operations.add(op); - } - - @Override - public String name() { - return "Matrix"; - } - - @Override - public int id() { - return Operations.MATRIX_SCALE; - } - - public void apply(WireBuffer buffer, float scaleX, float scaleY, - float centerX, float centerY) { - buffer.start(Operations.MATRIX_SCALE); - buffer.writeFloat(scaleX); - buffer.writeFloat(scaleY); - buffer.writeFloat(centerX); - buffer.writeFloat(centerY); - - } + super(scaleX, scaleY, centerX, centerY); + mName = "MatrixScale"; } @Override public void paint(PaintContext context) { - context.mtrixScale(mScaleX, mScaleY, mCenterX, mCenterY); + context.matrixScale(mX1, mY1, mX2, mY2); } } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixTranslate.java b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixTranslate.java index 32987521e041..4f34e987e064 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixTranslate.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/MatrixTranslate.java @@ -15,65 +15,28 @@ */ package com.android.internal.widget.remotecompose.core.operations; -import com.android.internal.widget.remotecompose.core.CompanionOperation; import com.android.internal.widget.remotecompose.core.Operation; import com.android.internal.widget.remotecompose.core.Operations; import com.android.internal.widget.remotecompose.core.PaintContext; -import com.android.internal.widget.remotecompose.core.PaintOperation; -import com.android.internal.widget.remotecompose.core.WireBuffer; -import java.util.List; - -public class MatrixTranslate extends PaintOperation { - public static final Companion COMPANION = new Companion(); - float mTranslateX, mTranslateY; +public class MatrixTranslate extends DrawBase2 { + public static final Companion COMPANION = + new Companion(Operations.MATRIX_TRANSLATE) { + @Override + public Operation construct(float x1, + float y1 + ) { + return new MatrixTranslate(x1, y1); + } + }; public MatrixTranslate(float translateX, float translateY) { - mTranslateX = translateX; - mTranslateY = translateY; - } - - @Override - public void write(WireBuffer buffer) { - COMPANION.apply(buffer, mTranslateX, mTranslateY); - } - - @Override - public String toString() { - return "DrawArc " + mTranslateY + ", " + mTranslateY + ";"; - } - - public static class Companion implements CompanionOperation { - private Companion() { - } - - @Override - public void read(WireBuffer buffer, List<Operation> operations) { - float translateX = buffer.readFloat(); - float translateY = buffer.readFloat(); - MatrixTranslate op = new MatrixTranslate(translateX, translateY); - operations.add(op); - } - - @Override - public String name() { - return "Matrix"; - } - - @Override - public int id() { - return Operations.MATRIX_TRANSLATE; - } - - public void apply(WireBuffer buffer, float translateX, float translateY) { - buffer.start(Operations.MATRIX_TRANSLATE); - buffer.writeFloat(translateX); - buffer.writeFloat(translateY); - } + super(translateX, translateY); + mName = "MatrixTranslate"; } @Override public void paint(PaintContext context) { - context.matrixTranslate(mTranslateX, mTranslateY); + context.matrixTranslate(mV1, mV2); } } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/NamedVariable.java b/core/java/com/android/internal/widget/remotecompose/core/operations/NamedVariable.java new file mode 100644 index 000000000000..0c5b286684d4 --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/NamedVariable.java @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.widget.remotecompose.core.operations; + +import com.android.internal.widget.remotecompose.core.CompanionOperation; +import com.android.internal.widget.remotecompose.core.Operation; +import com.android.internal.widget.remotecompose.core.Operations; +import com.android.internal.widget.remotecompose.core.RemoteContext; +import com.android.internal.widget.remotecompose.core.WireBuffer; + +import java.util.List; + +/** + * Operation to deal with Text data + */ +public class NamedVariable implements Operation { + public int mVarId; + public String mVarName; + public int mVarType; + public static final Companion COMPANION = new Companion(); + public static final int MAX_STRING_SIZE = 4000; + + public NamedVariable(int varId, int varType, String name) { + this.mVarId = varId; + this.mVarType = varType; + this.mVarName = name; + } + + @Override + public void write(WireBuffer buffer) { + COMPANION.apply(buffer, mVarId, mVarType, mVarName); + } + + @Override + public String toString() { + return "VariableName[" + mVarId + "] = \"" + + Utils.trimString(mVarName, 10) + "\" type=" + mVarType; + } + + public static class Companion implements CompanionOperation { + private Companion() { + } + + @Override + public String name() { + return "TextData"; + } + + @Override + public int id() { + return Operations.DATA_TEXT; + } + + /** + * Writes out the operation to the buffer + * @param buffer + * @param varId + * @param varType + * @param text + */ + public void apply(WireBuffer buffer, int varId, int varType, String text) { + buffer.start(Operations.DATA_TEXT); + buffer.writeInt(varId); + buffer.writeInt(varType); + buffer.writeUTF8(text); + } + + @Override + public void read(WireBuffer buffer, List<Operation> operations) { + int varId = buffer.readInt(); + int varType = buffer.readInt(); + String text = buffer.readUTF8(MAX_STRING_SIZE); + operations.add(new NamedVariable(varId, varType, text)); + } + } + + @Override + public void apply(RemoteContext context) { + context.loadVariableName(mVarName, mVarId, mVarType); + } + + @Override + public String deepToString(String indent) { + return indent + toString(); + } +} diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/PaintData.java b/core/java/com/android/internal/widget/remotecompose/core/operations/PaintData.java index e5683ece7919..0807bcdcfebb 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/PaintData.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/PaintData.java @@ -20,12 +20,14 @@ import com.android.internal.widget.remotecompose.core.Operation; import com.android.internal.widget.remotecompose.core.Operations; import com.android.internal.widget.remotecompose.core.PaintContext; import com.android.internal.widget.remotecompose.core.PaintOperation; +import com.android.internal.widget.remotecompose.core.RemoteContext; +import com.android.internal.widget.remotecompose.core.VariableSupport; import com.android.internal.widget.remotecompose.core.WireBuffer; import com.android.internal.widget.remotecompose.core.operations.paint.PaintBundle; import java.util.List; -public class PaintData extends PaintOperation { +public class PaintData extends PaintOperation implements VariableSupport { public PaintBundle mPaintData = new PaintBundle(); public static final Companion COMPANION = new Companion(); public static final int MAX_STRING_SIZE = 4000; @@ -34,6 +36,16 @@ public class PaintData extends PaintOperation { } @Override + public void updateVariables(RemoteContext context) { + mPaintData.updateVariables(context); + } + + @Override + public void registerListening(RemoteContext context) { + mPaintData.registerVars(context, this); + } + + @Override public void write(WireBuffer buffer) { COMPANION.apply(buffer, mPaintData); } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/PathData.java b/core/java/com/android/internal/widget/remotecompose/core/operations/PathData.java index 2646b27b1f51..e467e7b7f31e 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/PathData.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/PathData.java @@ -18,27 +18,50 @@ package com.android.internal.widget.remotecompose.core.operations; import com.android.internal.widget.remotecompose.core.CompanionOperation; import com.android.internal.widget.remotecompose.core.Operation; import com.android.internal.widget.remotecompose.core.Operations; -import com.android.internal.widget.remotecompose.core.PaintContext; import com.android.internal.widget.remotecompose.core.RemoteContext; +import com.android.internal.widget.remotecompose.core.VariableSupport; import com.android.internal.widget.remotecompose.core.WireBuffer; +import java.util.Arrays; import java.util.List; -public class PathData implements Operation { +public class PathData implements Operation, VariableSupport { public static final Companion COMPANION = new Companion(); int mInstanceId; - float[] mRef; float[] mFloatPath; - float[] mRetFloats; + float[] mOutputPath; PathData(int instanceId, float[] floatPath) { mInstanceId = instanceId; mFloatPath = floatPath; + mOutputPath = Arrays.copyOf(mFloatPath, mFloatPath.length); + } + + @Override + public void updateVariables(RemoteContext context) { + for (int i = 0; i < mFloatPath.length; i++) { + float v = mFloatPath[i]; + if (Utils.isVariable(v)) { + mOutputPath[i] = (Float.isNaN(v)) + ? context.getFloat(Utils.idFromNan(v)) : v; + } else { + mOutputPath[i] = v; + } + } + } + + @Override + public void registerListening(RemoteContext context) { + for (int i = 0; i < mFloatPath.length; i++) { + if (Float.isNaN(mFloatPath[i])) { + context.listensTo(Utils.idFromNan(mFloatPath[i]), this); + } + } } @Override public void write(WireBuffer buffer) { - COMPANION.apply(buffer, mInstanceId, mFloatPath); + COMPANION.apply(buffer, mInstanceId, mOutputPath); } @Override @@ -46,29 +69,35 @@ public class PathData implements Operation { return pathString(mFloatPath); } - public float[] getFloatPath(PaintContext context) { - float[] ret = mRetFloats; // Assume retFloats is declared elsewhere - if (ret == null) { - return mFloatPath; // Assume floatPath is declared elsewhere - } - float[] localRef = mRef; // Assume ref is of type Float[] - if (localRef == null) { - for (int i = 0; i < mFloatPath.length; i++) { - ret[i] = mFloatPath[i]; - } - } else { - for (int i = 0; i < mFloatPath.length; i++) { - float lr = localRef[i]; - if (Float.isNaN(lr)) { - ret[i] = Utils.getActualValue(lr); - } else { - ret[i] = mFloatPath[i]; - } - } - } - return ret; + @Override + public String toString() { + return "PathData[" + mInstanceId + "] = " + "\"" + deepToString(" ") + "\""; } + /** + * public float[] getFloatPath(PaintContext context) { + * float[] ret = mRetFloats; // Assume retFloats is declared elsewhere + * if (ret == null) { + * return mFloatPath; // Assume floatPath is declared elsewhere + * } + * float[] localRef = mRef; // Assume ref is of type Float[] + * if (localRef == null) { + * for (int i = 0; i < mFloatPath.length; i++) { + * ret[i] = mFloatPath[i]; + * } + * } else { + * for (int i = 0; i < mFloatPath.length; i++) { + * float lr = localRef[i]; + * if (Float.isNaN(lr)) { + * ret[i] = Utils.getActualValue(lr); + * } else { + * ret[i] = mFloatPath[i]; + * } + * } + * } + * return ret; + * } + */ public static final int MOVE = 10; public static final int LINE = 11; public static final int QUADRATIC = 12; @@ -155,7 +184,7 @@ public class PathData implements Operation { str.append("."); break; default: - str.append("X"); + str.append("[" + id + "]"); break; } } else { @@ -170,7 +199,7 @@ public class PathData implements Operation { @Override public void apply(RemoteContext context) { - context.loadPathData(mInstanceId, mFloatPath); + context.loadPathData(mInstanceId, mOutputPath); } } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentBehavior.java b/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentBehavior.java index 6d924eb70c50..997e8dc791ed 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentBehavior.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentBehavior.java @@ -94,7 +94,6 @@ public class RootContentBehavior implements RemoteComposeOperation { public static final int SCALE_CROP = 5; public static final int SCALE_FILL_BOUNDS = 6; - public static final Companion COMPANION = new Companion(); /** diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentDescription.java b/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentDescription.java index 64c7f3ef2d44..076b28edf981 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentDescription.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/RootContentDescription.java @@ -48,7 +48,7 @@ public class RootContentDescription implements RemoteComposeOperation { @Override public String toString() { - return "ROOT_CONTENT_DESCRIPTION " + mContentDescription; + return "RootContentDescription " + mContentDescription; } @Override diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/ShaderData.java b/core/java/com/android/internal/widget/remotecompose/core/operations/ShaderData.java new file mode 100644 index 000000000000..8463ac576774 --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/ShaderData.java @@ -0,0 +1,309 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.widget.remotecompose.core.operations; + +import com.android.internal.widget.remotecompose.core.CompanionOperation; +import com.android.internal.widget.remotecompose.core.Operation; +import com.android.internal.widget.remotecompose.core.Operations; +import com.android.internal.widget.remotecompose.core.RemoteContext; +import com.android.internal.widget.remotecompose.core.VariableSupport; +import com.android.internal.widget.remotecompose.core.WireBuffer; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; + +/** + * Operation to deal with bitmap data + * On getting an Image during a draw call the bitmap is compressed and saved + * in playback the image is decompressed + */ +public class ShaderData implements Operation, VariableSupport { + int mShaderTextId; // the actual text of a shader + int mShaderID; // allows shaders to be referenced by number + HashMap<String, float[]> mUniformRawFloatMap = null; + HashMap<String, float[]> mUniformFloatMap = null; + HashMap<String, int[]> mUniformIntMap = null; + HashMap<String, Integer> mUniformBitmapMap = null; + + public static final int MAX_IMAGE_DIMENSION = 8000; + + public static final Companion COMPANION = new Companion(); + + public ShaderData(int shaderID, + int shaderTextId, + HashMap<String, float[]> floatMap, + HashMap<String, int[]> intMap, + HashMap<String, Integer> bitmapMap) { + mShaderID = shaderID; + mShaderTextId = shaderTextId; + if (floatMap != null) { + mUniformFloatMap = new HashMap<>(); + mUniformRawFloatMap = new HashMap<>(); + + for (String name : floatMap.keySet()) { + mUniformRawFloatMap.put(name, floatMap.get(name)); + mUniformFloatMap.put(name, floatMap.get(name)); + } + } + + if (intMap != null) { + mUniformIntMap = new HashMap<>(); + for (String name : intMap.keySet()) { + mUniformIntMap.put(name, intMap.get(name)); + } + } + if (bitmapMap != null) { + mUniformBitmapMap = new HashMap<>(); + for (String name : bitmapMap.keySet()) { + mUniformBitmapMap.put(name, bitmapMap.get(name)); + } + } + + } + + public int getShaderTextId() { + return mShaderTextId; + } + + /** + * get names of all known floats + * @return + */ + public String[] getUniformFloatNames() { + if (mUniformFloatMap == null) return new String[0]; + return mUniformFloatMap.keySet().toArray(new String[0]); + } + + /** + * Get float values associated with the name + * @param name + * @return + */ + public float[] getUniformFloats(String name) { + return mUniformFloatMap.get(name); + } + + /** + * get the name of all know uniform integers + * @return + */ + public String[] getUniformIntegerNames() { + if (mUniformIntMap == null) return new String[0]; + return mUniformIntMap.keySet().toArray(new String[0]); + } + + /** + * Get Int value associated with the name + * @param name + * @return + */ + public int[] getUniformInts(String name) { + return mUniformIntMap.get(name); + } + + /** + * get list of uniform Bitmaps + * @return + */ + public String[] getUniformBitmapNames() { + if (mUniformBitmapMap == null) return new String[0]; + return mUniformBitmapMap.keySet().toArray(new String[0]); + } + + /** + * Get a bitmap stored under that name + * @param name + * @return + */ + public int getUniformBitmapId(String name) { + return mUniformBitmapMap.get(name); + } + + @Override + public void write(WireBuffer buffer) { + COMPANION.apply(buffer, mShaderID, mShaderTextId, + mUniformFloatMap, mUniformIntMap, mUniformBitmapMap); + } + + @Override + public String toString() { + return "SHADER DATA " + mShaderID; + } + + @Override + public void updateVariables(RemoteContext context) { + for (String name : mUniformRawFloatMap.keySet()) { + float[] value = mUniformRawFloatMap.get(name); + float[] out = null; + for (int i = 0; i < value.length; i++) { + if (Float.isNaN(value[i])) { + if (out == null) { // need to copy + out = Arrays.copyOf(value, value.length); + } + out[i] = context.getFloat(Utils.idFromNan(value[i])); + } + } + mUniformFloatMap.put(name, out == null ? value : out); + } + } + + @Override + public void registerListening(RemoteContext context) { + for (String name : mUniformRawFloatMap.keySet()) { + float[] value = mUniformRawFloatMap.get(name); + for (int i = 0; i < value.length; i++) { + if (Float.isNaN(value[i])) { + context.listensTo(Utils.idFromNan(value[i]), this); + } + } + } + } + + public static class Companion implements CompanionOperation { + private Companion() { + } + + @Override + public String name() { + return "BitmapData"; + } + + @Override + public int id() { + return Operations.DATA_SHADER; + } + + /** + * Writes out the operation to the buffer + * @param buffer + * @param shaderID + * @param shaderTextId + * @param floatMap + * @param intMap + * @param bitmapMap + */ + public void apply(WireBuffer buffer, int shaderID, int shaderTextId, + HashMap<String, float[]> floatMap, + HashMap<String, int[]> intMap, + HashMap<String, Integer> bitmapMap) { + buffer.start(Operations.DATA_SHADER); + buffer.writeInt(shaderID); + + buffer.writeInt(shaderTextId); + int floatSize = (floatMap == null) ? 0 : floatMap.size(); + int intSize = (intMap == null) ? 0 : intMap.size(); + int bitmapSize = (bitmapMap == null) ? 0 : bitmapMap.size(); + int sizes = floatSize | (intSize << 8) | (bitmapSize << 16); + buffer.writeInt(sizes); + + if (floatSize > 0) { + + for (String name : floatMap.keySet()) { + buffer.writeUTF8(name); + float[] values = floatMap.get(name); + buffer.writeInt(values.length); + + for (int i = 0; i < values.length; i++) { + buffer.writeFloat(values[i]); + } + } + } + + if (intSize > 0) { + for (String name : intMap.keySet()) { + buffer.writeUTF8(name); + int[] values = intMap.get(name); + buffer.writeInt(values.length); + for (int i = 0; i < values.length; i++) { + buffer.writeInt(values[i]); + } + } + } + if (bitmapSize > 0) { + for (String name : bitmapMap.keySet()) { + buffer.writeUTF8(name); + int value = bitmapMap.get(name); + buffer.writeInt(value); + } + } + } + + @Override + public void read(WireBuffer buffer, List<Operation> operations) { + int shaderID = buffer.readInt(); + int shaderTextId = buffer.readInt(); + HashMap<String, float[]> floatMap = null; + HashMap<String, int[]> intMap = null; + HashMap<String, Integer> bitmapMap = null; + + int sizes = buffer.readInt(); + + int floatMapSize = sizes & 0xFF; + if (floatMapSize > 0) { + floatMap = new HashMap<>(); + for (int i = 0; i < floatMapSize; i++) { + String name = buffer.readUTF8(); + int len = buffer.readInt(); + float[] val = new float[len]; + + for (int j = 0; j < len; j++) { + val[j] = buffer.readFloat(); + } + + floatMap.put(name, val); + } + } + int intMapSize = (sizes >> 8) & 0xFF; + + if (intMapSize > 0) { + + intMap = new HashMap<>(); + for (int i = 0; i < intMapSize; i++) { + String name = buffer.readUTF8(); + int len = buffer.readInt(); + int[] val = new int[len]; + for (int j = 0; j < len; j++) { + val[j] = buffer.readInt(); + } + intMap.put(name, val); + } + } + int bitmapMapSize = (sizes >> 16) & 0xFF; + + if (bitmapMapSize > 0) { + bitmapMap = new HashMap<>(); + for (int i = 0; i < bitmapMapSize; i++) { + String name = buffer.readUTF8(); + int val = buffer.readInt(); + bitmapMap.put(name, val); + } + } + operations.add(new ShaderData(shaderID, shaderTextId, + floatMap, intMap, bitmapMap)); + } + } + + @Override + public void apply(RemoteContext context) { + context.loadShader(mShaderID, this); + } + + @Override + public String deepToString(String indent) { + return indent + toString(); + } +} diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/TextData.java b/core/java/com/android/internal/widget/remotecompose/core/operations/TextData.java index 5b622ae96d0b..ed1344975256 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/TextData.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TextData.java @@ -44,11 +44,13 @@ public class TextData implements Operation { @Override public String toString() { - return "TEXT DATA " + mTextId + "\"" + mText + "\""; + return "TextData[" + mTextId + "] = \"" + + Utils.trimString(mText, 10) + "\""; } public static class Companion implements CompanionOperation { - private Companion() {} + private Companion() { + } @Override public String name() { diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/TextFromFloat.java b/core/java/com/android/internal/widget/remotecompose/core/operations/TextFromFloat.java new file mode 100644 index 000000000000..65a39a1eba04 --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TextFromFloat.java @@ -0,0 +1,172 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.widget.remotecompose.core.operations; + +import com.android.internal.widget.remotecompose.core.CompanionOperation; +import com.android.internal.widget.remotecompose.core.Operation; +import com.android.internal.widget.remotecompose.core.Operations; +import com.android.internal.widget.remotecompose.core.RemoteContext; +import com.android.internal.widget.remotecompose.core.VariableSupport; +import com.android.internal.widget.remotecompose.core.WireBuffer; +import com.android.internal.widget.remotecompose.core.operations.utilities.StringUtils; + +import java.util.List; + +/** + * Operation convert floats to text + * This command is structured [command][textID][before,after][flags] + * before and after define number of digits before and after the decimal point + */ +public class TextFromFloat implements Operation, VariableSupport { + public int mTextId; + public float mValue; + public float mOutValue; + public short mDigitsBefore; + public short mDigitsAfter; + public int mFlags; + public static final Companion COMPANION = new Companion(); + public static final int MAX_STRING_SIZE = 4000; + char mPre = ' '; + char mAfter = ' '; + // Theses flags define what how to/if fill the space + public static final int PAD_AFTER_SPACE = 0; // pad past point with space + public static final int PAD_AFTER_NONE = 1; // do not pad past last digit + public static final int PAD_AFTER_ZERO = 3; // pad with 0 past last digit + public static final int PAD_PRE_SPACE = 0; // pad before number with spaces + public static final int PAD_PRE_NONE = 4; // pad before number with 0s + public static final int PAD_PRE_ZERO = 12; // do not pad before number + + public TextFromFloat(int textId, float value, short digitsBefore, + short digitsAfter, int flags) { + this.mTextId = textId; + this.mValue = value; + this.mDigitsAfter = digitsAfter; + this.mDigitsBefore = digitsBefore; + this.mFlags = flags; + mOutValue = mValue; + switch (mFlags & 3) { + case PAD_AFTER_SPACE: + mAfter = ' '; + break; + case PAD_AFTER_NONE: + mAfter = 0; + break; + case PAD_AFTER_ZERO: + mAfter = '0'; + break; + } + switch (mFlags & 12) { + case PAD_PRE_SPACE: + mPre = ' '; + break; + case PAD_PRE_NONE: + mPre = 0; + break; + case PAD_PRE_ZERO: + mPre = '0'; + break; + } + } + + @Override + public void write(WireBuffer buffer) { + COMPANION.apply(buffer, mTextId, mValue, mDigitsAfter, mDigitsBefore, mFlags); + } + + @Override + public String toString() { + return "TextFromFloat[" + mTextId + "] = " + + Utils.floatToString(mValue) + " " + mDigitsBefore + + "." + mDigitsAfter + " " + mFlags; + } + + + @Override + public void updateVariables(RemoteContext context) { + if (Float.isNaN(mValue)) { + mOutValue = context.getFloat(Utils.idFromNan(mValue)); + } + + } + + + @Override + public void registerListening(RemoteContext context) { + if (Float.isNaN(mValue)) { + context.listensTo(Utils.idFromNan(mValue), this); + } + } + + public static class Companion implements CompanionOperation { + private Companion() { + } + + @Override + public String name() { + return "TextData"; + } + + @Override + public int id() { + return Operations.TEXT_FROM_FLOAT; + } + + /** + * Writes out the operation to the buffer + * @param buffer + * @param textId + * @param value + * @param digitsBefore + * @param digitsAfter + * @param flags + */ + public void apply(WireBuffer buffer, int textId, + float value, short digitsBefore, + short digitsAfter, int flags) { + buffer.start(Operations.TEXT_FROM_FLOAT); + buffer.writeInt(textId); + buffer.writeFloat(value); + buffer.writeInt((digitsBefore << 16) | digitsAfter); + buffer.writeInt(flags); + + } + + @Override + public void read(WireBuffer buffer, List<Operation> operations) { + int textId = buffer.readInt(); + float value = buffer.readFloat(); + int tmp = buffer.readInt(); + short post = (short) (tmp & 0xFFFF); + short pre = (short) ((tmp >> 16) & 0xFFFF); + + int flags = buffer.readInt(); + operations.add(new TextFromFloat(textId, value, pre, post, flags)); + } + } + + @Override + public void apply(RemoteContext context) { + float v = mOutValue; + String s = StringUtils.floatToString(v, mDigitsBefore, + mDigitsAfter, mPre, mAfter); + context.loadText(mTextId, s); + } + + @Override + public String deepToString(String indent) { + return indent + toString(); + } +} diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/TextMerge.java b/core/java/com/android/internal/widget/remotecompose/core/operations/TextMerge.java new file mode 100644 index 000000000000..a0fc854f38f4 --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/TextMerge.java @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.widget.remotecompose.core.operations; + +import com.android.internal.widget.remotecompose.core.CompanionOperation; +import com.android.internal.widget.remotecompose.core.Operation; +import com.android.internal.widget.remotecompose.core.Operations; +import com.android.internal.widget.remotecompose.core.RemoteContext; +import com.android.internal.widget.remotecompose.core.WireBuffer; + +import java.util.List; + +/** + * Operation to deal with Text data + */ +public class TextMerge implements Operation { + public int mTextId; + public int mSrcId1; + public int mSrcId2; + public static final Companion COMPANION = new Companion(); + public static final int MAX_STRING_SIZE = 4000; + + public TextMerge(int textId, int srcId1, int srcId2) { + this.mTextId = textId; + this.mSrcId1 = srcId1; + this.mSrcId2 = srcId2; + } + + @Override + public void write(WireBuffer buffer) { + COMPANION.apply(buffer, mTextId, mSrcId1, mSrcId2); + } + + @Override + public String toString() { + return "TextMerge[" + mTextId + "] = [" + mSrcId1 + " ] + [ " + mSrcId2 + "]"; + } + + public static class Companion implements CompanionOperation { + private Companion() { + } + + @Override + public String name() { + return "TextData"; + } + + @Override + public int id() { + return Operations.TEXT_MERGE; + } + + /** + * Writes out the operation to the buffer + * @param buffer + * @param textId + * @param srcId1 + * @param srcId2 + */ + public void apply(WireBuffer buffer, int textId, int srcId1, int srcId2) { + buffer.start(Operations.TEXT_MERGE); + buffer.writeInt(textId); + buffer.writeInt(srcId1); + buffer.writeInt(srcId2); + } + + @Override + public void read(WireBuffer buffer, List<Operation> operations) { + int textId = buffer.readInt(); + int srcId1 = buffer.readInt(); + int srcId2 = buffer.readInt(); + + operations.add(new TextMerge(textId, srcId1, srcId2)); + } + } + + @Override + public void apply(RemoteContext context) { + String str1 = context.getText(mSrcId1); + String str2 = context.getText(mSrcId2); + context.loadText(mTextId, str1 + str2); + } + + @Override + public String deepToString(String indent) { + return indent + toString(); + } +} diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/Utils.java b/core/java/com/android/internal/widget/remotecompose/core/operations/Utils.java index 00e2f2058e89..fdc68601bf4d 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/Utils.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/Utils.java @@ -15,13 +15,16 @@ */ package com.android.internal.widget.remotecompose.core.operations; +/** + * Utilities to be used across all core operations + */ public class Utils { public static float asNan(int v) { return Float.intBitsToFloat(v | -0x800000); } public static int idFromNan(float value) { - int b = Float.floatToRawIntBits(value); + int b = Float.floatToRawIntBits(value); return b & 0xFFFFF; } @@ -29,13 +32,194 @@ public class Utils { return 0; } - String getFloatString(float value) { + /** + * trim a string to n characters if needing to trim + * end in "..." + * + * @param str + * @param n + * @return + */ + static String trimString(String str, int n) { + if (str.length() > n) { + str = str.substring(0, n - 3) + "..."; + } + return str; + } + + /** + * print the id and the value of a float + * @param idvalue + * @param value + * @return + */ + public static String floatToString(float idvalue, float value) { + if (Float.isNaN(idvalue)) { + return "[" + idFromNan(idvalue) + "]" + floatToString(value); + } + return floatToString(value); + } + + /** + * Convert float to string but render nan id in brackets [n] + * @param value + * @return + */ + public static String floatToString(float value) { if (Float.isNaN(value)) { - int id = idFromNan(value); - if (id > 0) { - return "NaN(" + id + ")"; - } + return "[" + idFromNan(value) + "]"; } - return "" + value; + return Float.toString(value); + } + + /** + * Debugging util to print a message and include the file/line it came from + * @param str + */ + public static void log(String str) { + StackTraceElement s = new Throwable().getStackTrace()[1]; + System.out.println("(" + s.getFileName() + ":" + s.getLineNumber() + ")." + str); + } + + /** + * Debugging util to print the stack + * @param str + * @param n + */ + public static void logStack(String str, int n) { + StackTraceElement[] st = new Throwable().getStackTrace(); + for (int i = 1; i < n + 1; i++) { + StackTraceElement s = st[i]; + String space = new String(new char[i]).replace('\0', ' '); + System.out.println(space + "(" + s.getFileName() + + ":" + s.getLineNumber() + ")." + str); + } + } + + /** + * Is a variable Allowed int calculation and references. + * + * @param v + * @return + */ + public static boolean isVariable(float v) { + if (Float.isNaN(v)) { + int id = idFromNan(v); + return id > 40 || id < 10; + } + return false; + } + + /** + * print a color in the familiar 0xAARRGGBB pattern + * + * @param color + * @return + */ + public static String colorInt(int color) { + String str = "000000000000" + Integer.toHexString(color); + return "0x" + str.substring(str.length() - 8); } + + /** + * Interpolate two colors. + * gamma corrected colors are interpolated in the form c1 * (1-t) + c2 * t + * + * @param c1 + * @param c2 + * @param t + * @return + */ + public static int interpolateColor(int c1, int c2, float t) { + if (Float.isNaN(t) || t == 0.0f) { + return c1; + } else if (t == 1.0f) { + return c2; + } + int a = 0xFF & (c1 >> 24); + int r = 0xFF & (c1 >> 16); + int g = 0xFF & (c1 >> 8); + int b = 0xFF & c1; + float f_r = (float) Math.pow(r / 255.0f, 2.2); + float f_g = (float) Math.pow(g / 255.0f, 2.2); + float f_b = (float) Math.pow(b / 255.0f, 2.2); + float c1fr = f_r; + float c1fg = f_g; + float c1fb = f_b; + float c1fa = a / 255f; + + a = 0xFF & (c2 >> 24); + r = 0xFF & (c2 >> 16); + g = 0xFF & (c2 >> 8); + b = 0xFF & c2; + f_r = (float) Math.pow(r / 255.0f, 2.2); + f_g = (float) Math.pow(g / 255.0f, 2.2); + f_b = (float) Math.pow(b / 255.0f, 2.2); + float c2fr = f_r; + float c2fg = f_g; + float c2fb = f_b; + float c2fa = a / 255f; + f_r = c1fr + t * (c2fr - c1fr); + f_g = c1fg + t * (c2fg - c1fg); + f_b = c1fb + t * (c2fb - c1fb); + float f_a = c1fa + t * (c2fa - c1fa); + + int outr = clamp((int) ((float) Math.pow(f_r, 1.0 / 2.2) * 255.0f)); + int outg = clamp((int) ((float) Math.pow(f_g, 1.0 / 2.2) * 255.0f)); + int outb = clamp((int) ((float) Math.pow(f_b, 1.0 / 2.2) * 255.0f)); + int outa = clamp((int) (f_a * 255.0f)); + + + return (outa << 24 | outr << 16 | outg << 8 | outb); + } + + /** + * Efficient clamping function + * + * @param c + * @return number between 0 and 255 + */ + public static int clamp(int c) { + int n = 255; + c &= ~(c >> 31); + c -= n; + c &= (c >> 31); + c += n; + return c; + } + + /** + * convert hue saturation and value to RGB + * + * @param hue 0..1 + * @param saturation 0..1 0=on the gray scale + * @param value 0..1 0=black + * @return + */ + public static int hsvToRgb(float hue, float saturation, float value) { + int h = (int) (hue * 6); + float f = hue * 6 - h; + int p = (int) (0.5f + 255 * value * (1 - saturation)); + int q = (int) (0.5f + 255 * value * (1 - f * saturation)); + int t = (int) (0.5f + 255 * value * (1 - (1 - f) * saturation)); + int v = (int) (0.5f + 255 * value); + switch (h) { + case 0: + return 0XFF000000 | (v << 16) + (t << 8) + p; + case 1: + return 0XFF000000 | (q << 16) + (v << 8) + p; + case 2: + return 0XFF000000 | (p << 16) + (v << 8) + t; + case 3: + return 0XFF000000 | (p << 16) + (q << 8) + v; + case 4: + return 0XFF000000 | (t << 16) + (p << 8) + v; + case 5: + return 0XFF000000 | (v << 16) + (p << 8) + q; + + } + return 0; + } + + } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintBundle.java b/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintBundle.java index 8abb0bfff338..a7d0ac6330f7 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintBundle.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintBundle.java @@ -15,43 +15,60 @@ */ package com.android.internal.widget.remotecompose.core.operations.paint; +import com.android.internal.widget.remotecompose.core.PaintContext; +import com.android.internal.widget.remotecompose.core.RemoteContext; +import com.android.internal.widget.remotecompose.core.VariableSupport; import com.android.internal.widget.remotecompose.core.WireBuffer; +import com.android.internal.widget.remotecompose.core.operations.Utils; import java.util.Arrays; +/** + * Paint Bundle represents a delta of changes to a paint object + */ public class PaintBundle { int[] mArray = new int[200]; + int[] mOutArray = null; int mPos = 0; - public void applyPaintChange(PaintChanges p) { + /** + * Apply changes to a PaintChanges interface + * @param paintContext + * @param p + */ + public void applyPaintChange(PaintContext paintContext, PaintChanges p) { int i = 0; int mask = 0; + if (mOutArray == null) { + mOutArray = mArray; + } while (i < mPos) { - int cmd = mArray[i++]; + int cmd = mOutArray[i++]; mask = mask | (1 << (cmd - 1)); switch (cmd & 0xFFFF) { case TEXT_SIZE: { - p.setTextSize(Float.intBitsToFloat(mArray[i++])); + p.setTextSize(Float.intBitsToFloat(mOutArray[i++])); break; } case TYPEFACE: int style = (cmd >> 16); int weight = style & 0x3ff; boolean italic = (style >> 10) > 0; - int font_type = mArray[i++]; + int font_type = mOutArray[i++]; p.setTypeFace(font_type, weight, italic); break; + case COLOR_ID: // mOutArray should have already decoded it case COLOR: { - p.setColor(mArray[i++]); + p.setColor(mOutArray[i++]); break; } case STROKE_WIDTH: { - p.setStrokeWidth(Float.intBitsToFloat(mArray[i++])); + p.setStrokeWidth(Float.intBitsToFloat(mOutArray[i++])); break; } case STROKE_MITER: { - p.setStrokeMiter(Float.intBitsToFloat(mArray[i++])); + p.setStrokeMiter(Float.intBitsToFloat(mOutArray[i++])); break; } case STROKE_CAP: { @@ -63,6 +80,7 @@ public class PaintBundle { break; } case SHADER: { + p.setShader(mOutArray[i++]); break; } case STROKE_JOIN: { @@ -81,17 +99,16 @@ public class PaintBundle { p.setFilterBitmap(!((cmd >> 16) == 0)); break; } - case GRADIENT: { - i = callSetGradient(cmd, mArray, i, p); + i = callSetGradient(cmd, mOutArray, i, p); break; } case COLOR_FILTER: { - p.setColorFilter(mArray[i++], cmd >> 16); + p.setColorFilter(mOutArray[i++], cmd >> 16); break; } case ALPHA: { - p.setAlpha(Float.intBitsToFloat(mArray[i++])); + p.setAlpha(Float.intBitsToFloat(mOutArray[i++])); break; } } @@ -106,7 +123,6 @@ public class PaintBundle { switch (id) { case TEXT_SIZE: return "TEXT_SIZE"; - case COLOR: return "COLOR"; case STROKE_WIDTH: @@ -133,7 +149,6 @@ public class PaintBundle { return "ALPHA"; case COLOR_FILTER: return "COLOR_FILTER"; - } return "????" + id + "????"; } @@ -154,6 +169,14 @@ public class PaintBundle { return str + "]"; } + private static String asFloatStr(int value) { + float fValue = Float.intBitsToFloat(value); + if (Float.isNaN(fValue)) { + return "[" + Utils.idFromNan(fValue) + "]"; + } + return Float.toString(fValue); + } + @Override public String toString() { StringBuilder ret = new StringBuilder("\n"); @@ -164,7 +187,8 @@ public class PaintBundle { switch (type) { case TEXT_SIZE: { - ret.append(" TextSize(" + Float.intBitsToFloat(mArray[i++])); + ret.append(" TextSize(" + + asFloatStr(mArray[i++])); } break; @@ -181,14 +205,18 @@ public class PaintBundle { ret.append(" Color(" + colorInt(mArray[i++])); } break; + case COLOR_ID: { + ret.append(" ColorId([" + mArray[i++] + "]"); + } + break; case STROKE_WIDTH: { ret.append(" StrokeWidth(" - + (Float.intBitsToFloat(mArray[i++]))); + + (asFloatStr(mArray[i++]))); } break; case STROKE_MITER: { ret.append(" StrokeMiter(" - + (Float.intBitsToFloat(mArray[i++]))); + + (asFloatStr(mArray[i++]))); } break; case STROKE_CAP: { @@ -207,11 +235,12 @@ public class PaintBundle { } break; case SHADER: { + ret.append(" Shader(" + mArray[i++]); } break; case ALPHA: { ret.append(" Alpha(" - + (Float.intBitsToFloat(mArray[i++]))); + + (asFloatStr(mArray[i++]))); } break; case IMAGE_FILTER_QUALITY: { @@ -244,7 +273,6 @@ public class PaintBundle { return ret.toString(); } - int callPrintGradient(int cmd, int[] array, int i, StringBuilder p) { int ret = i; int type = (cmd >> 16); @@ -258,26 +286,25 @@ public class PaintBundle { colors = new int[len]; for (int j = 0; j < colors.length; j++) { colors[j] = array[ret++]; - } } len = array[ret++]; - float[] stops = null; + String[] stops = null; if (len > 0) { - stops = new float[len]; + stops = new String[len]; for (int j = 0; j < stops.length; j++) { - stops[j] = Float.intBitsToFloat(array[ret++]); + stops[j] = asFloatStr(array[ret++]); } } p.append(" colors = " + colorInt(colors) + ",\n"); p.append(" stops = " + Arrays.toString(stops) + ",\n"); p.append(" start = "); - p.append("[" + Float.intBitsToFloat(array[ret++])); - p.append(", " + Float.intBitsToFloat(array[ret++]) + "],\n"); + p.append("[" + asFloatStr(array[ret++])); + p.append(", " + asFloatStr(array[ret++]) + "],\n"); p.append(" end = "); - p.append("[" + Float.intBitsToFloat(array[ret++])); - p.append(", " + Float.intBitsToFloat(array[ret++]) + "],\n"); + p.append("[" + asFloatStr(array[ret++])); + p.append(", " + asFloatStr(array[ret++]) + "],\n"); int tileMode = array[ret++]; p.append(" tileMode = " + tileMode + "\n "); } @@ -295,21 +322,21 @@ public class PaintBundle { } } len = array[ret++]; - float[] stops = null; + String[] stops = null; if (len > 0) { - stops = new float[len]; + stops = new String[len]; for (int j = 0; j < stops.length; j++) { - stops[j] = Float.intBitsToFloat(array[ret++]); + stops[j] = asFloatStr(array[ret++]); } } p.append(" colors = " + colorInt(colors) + ",\n"); p.append(" stops = " + Arrays.toString(stops) + ",\n"); p.append(" center = "); - p.append("[" + Float.intBitsToFloat(array[ret++])); - p.append(", " + Float.intBitsToFloat(array[ret++]) + "],\n"); + p.append("[" + asFloatStr(array[ret++])); + p.append(", " + asFloatStr(array[ret++]) + "],\n"); p.append(" radius ="); - p.append(" " + Float.intBitsToFloat(array[ret++]) + ",\n"); + p.append(" " + asFloatStr(array[ret++]) + ",\n"); int tileMode = array[ret++]; p.append(" tileMode = " + tileMode + "\n "); } @@ -327,20 +354,19 @@ public class PaintBundle { } } len = array[ret++]; - float[] stops = null; + String[] stops = null; if (len > 0) { - stops = new float[len]; + stops = new String[len]; for (int j = 0; j < stops.length; j++) { - stops[j] = Float.intBitsToFloat(array[ret++]); + stops[j] = asFloatStr(array[ret++]); } } - p.append(" colors = " + colorInt(colors) + ",\n"); p.append(" stops = " + Arrays.toString(stops) + ",\n"); p.append(" center = "); - p.append("[" + Float.intBitsToFloat(array[ret++])); - p.append(", " + Float.intBitsToFloat(array[ret++]) + "],\n "); - + p.append("[" + asFloatStr(array[ret++])); + p.append(", " + + asFloatStr(array[ret++]) + "],\n "); } break; default: { @@ -376,7 +402,6 @@ public class PaintBundle { return ret; } - switch (gradientType) { case LINEAR_GRADIENT: { @@ -433,7 +458,7 @@ public class PaintBundle { public static final int COLOR = 4; // int public static final int STROKE_WIDTH = 5; // float public static final int STROKE_MITER = 6; - public static final int STROKE_CAP = 7; // int + public static final int STROKE_CAP = 7; // int public static final int STYLE = 8; // int public static final int SHADER = 9; // int public static final int IMAGE_FILTER_QUALITY = 10; // int @@ -445,7 +470,7 @@ public class PaintBundle { public static final int TYPEFACE = 16; public static final int FILTER_BITMAP = 17; public static final int BLEND_MODE = 18; - + public static final int COLOR_ID = 19; // int public static final int BLEND_MODE_CLEAR = 0; public static final int BLEND_MODE_SRC = 1; @@ -634,8 +659,8 @@ public class PaintBundle { /** * @param fontType 0 = default 1 = sans serif 2 = serif 3 = monospace - * @param weight 100-1000 - * @param italic tur + * @param weight 100-1000 + * @param italic tur */ public void setTextStyle(int fontType, int weight, boolean italic) { int style = (weight & 0x3FF) | (italic ? 2048 : 0); // pack the weight and italic @@ -658,6 +683,10 @@ public class PaintBundle { mPos++; } + /** + * Set the Color based on Color + * @param color + */ public void setColor(int color) { mArray[mPos] = COLOR; mPos++; @@ -666,6 +695,18 @@ public class PaintBundle { } /** + * Set the Color based on ID + * @param color + */ + public void setColorId(int color) { + mArray[mPos] = COLOR_ID; + mPos++; + mArray[mPos] = color; + mPos++; + } + + + /** * Set the paint's Cap. * * @param cap set the paint's line cap style, used whenever the paint's @@ -676,16 +717,29 @@ public class PaintBundle { mPos++; } + /** + * Set the style STROKE and/or FILL + * @param style + */ public void setStyle(int style) { mArray[mPos] = STYLE | (style << 16); mPos++; } - public void setShader(int shader, String shaderString) { - mArray[mPos] = SHADER | (shader << 16); + /** + * Set the shader id to use + * @param shaderId + */ + public void setShader(int shaderId) { + mArray[mPos] = SHADER; + mPos++; + mArray[mPos] = shaderId; mPos++; } + /** + * Set the Alpha value + */ public void setAlpha(float alpha) { mArray[mPos] = ALPHA; mPos++; @@ -729,7 +783,6 @@ public class PaintBundle { * destination pixels * (content of the render target). * - * * @param blendmode The blend mode to be installed in the paint */ public void setBlendMode(int blendmode) { @@ -825,5 +878,216 @@ public class PaintBundle { return "null"; } -} + /** + * Check all the floats for Nan(id) floats and call listenTo + * @param context + * @param support + */ + public void registerVars(RemoteContext context, VariableSupport support) { + int i = 0; + while (i < mPos) { + int cmd = mArray[i++]; + int type = cmd & 0xFFFF; + switch (type) { + case STROKE_MITER: + case STROKE_WIDTH: + case ALPHA: + case TEXT_SIZE: + float v = Float.intBitsToFloat(mArray[i++]); + if (Float.isNaN(v)) { + context.listensTo(Utils.idFromNan(v), support); + } + break; + case COLOR_ID: + context.listensTo(mArray[i++], support); + break; + case COLOR: + + case TYPEFACE: + case SHADER: + case COLOR_FILTER: + i++; + break; + case STROKE_JOIN: + case FILTER_BITMAP: + case STROKE_CAP: + case STYLE: + case IMAGE_FILTER_QUALITY: + case BLEND_MODE: + case ANTI_ALIAS: + break; + + case GRADIENT: { + // TODO gradients should be handled correctly + i = callPrintGradient(cmd, mArray, i, new StringBuilder()); + } + } + } + } + + /** + * Update variables if any are float ids + * @param context + */ + public void updateVariables(RemoteContext context) { + if (mOutArray == null) { + mOutArray = Arrays.copyOf(mArray, mArray.length); + } else { + System.arraycopy(mArray, 0, mOutArray, 0, mArray.length); + } + int i = 0; + while (i < mPos) { + int cmd = mArray[i++]; + int type = cmd & 0xFFFF; + switch (type) { + case STROKE_MITER: + case STROKE_WIDTH: + case ALPHA: + case TEXT_SIZE: + mOutArray[i] = fixFloatVar(mArray[i], context); + i++; + break; + case COLOR_ID: + mOutArray[i] = fixColor(mArray[i], context); + i++; + break; + case COLOR: + case TYPEFACE: + case SHADER: + case COLOR_FILTER: + i++; + break; + case STROKE_JOIN: + case FILTER_BITMAP: + case STROKE_CAP: + case STYLE: + case IMAGE_FILTER_QUALITY: + case BLEND_MODE: + case ANTI_ALIAS: + break; + + case GRADIENT: { + // TODO gradients should be handled correctly + i = updateFloatsInGradient(cmd, mOutArray, mArray, i, context); + } + } + } + } + + private int fixFloatVar(int val, RemoteContext context) { + float v = Float.intBitsToFloat(val); + if (Float.isNaN(v)) { + int id = Utils.idFromNan(v); + return Float.floatToRawIntBits(context.getFloat(id)); + } + return val; + } + + private int fixColor(int colorId, RemoteContext context) { + int n = context.getColor(colorId); + return n; + } + + int updateFloatsInGradient(int cmd, int[] out, int[] array, + int i, + RemoteContext context) { + int ret = i; + int type = (cmd >> 16); + switch (type) { + case 0: { + int len = array[ret++]; + if (len > 0) { + for (int j = 0; j < len; j++) { + ret++; + } + } + len = array[ret++]; + + if (len > 0) { + for (int j = 0; j < len; j++) { + out[ret] = fixFloatVar(array[ret], context); + ret++; + } + } + + out[ret] = fixFloatVar(array[ret], context); + ret++; + out[ret] = fixFloatVar(array[ret], context); + ret++; + + // end + out[ret] = fixFloatVar(array[ret], context); + ret++; + out[ret] = fixFloatVar(array[ret], context); + ret++; + ret++; // tileMode + } + + break; + case 1: { + // RadialGradient + int len = array[ret++]; + if (len > 0) { + for (int j = 0; j < len; j++) { + ret++; + } + } + len = array[ret++]; + if (len > 0) { + for (int j = 0; j < len; j++) { + out[ret] = fixFloatVar(array[ret], context); + ret++; + } + } + + + // center + out[ret] = fixFloatVar(array[ret], context); + ret++; + out[ret] = fixFloatVar(array[ret], context); + ret++; + // radius + out[ret] = fixFloatVar(array[ret], context); + ret++; + ret++; // tileMode + + } + + break; + case 2: { + // SweepGradient + int len = array[ret++]; + int[] colors = null; + if (len > 0) { + colors = new int[len]; + for (int j = 0; j < colors.length; j++) { + colors[j] = array[ret++]; + + } + } + len = array[ret++]; + float[] stops = null; + if (len > 0) { + stops = new float[len]; + for (int j = 0; j < stops.length; j++) { + out[ret] = fixFloatVar(array[ret], context); + ret++; + } + } + + // center + out[ret] = fixFloatVar(array[ret], context); + ret++; + out[ret] = fixFloatVar(array[ret], context); + ret++; + } + break; + default: { + System.err.println("gradient type unknown"); + } + } + + return ret; + } +}
\ No newline at end of file diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintChangeAdapter.java b/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintChangeAdapter.java index 994bf6d7e327..28fe63a03c67 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintChangeAdapter.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintChangeAdapter.java @@ -27,7 +27,6 @@ public class PaintChangeAdapter implements PaintChanges { } - @Override public void setStrokeWidth(float width) { @@ -49,7 +48,7 @@ public class PaintChangeAdapter implements PaintChanges { } @Override - public void setShader(int shader, String shaderString) { + public void setShader(int shader) { } diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintChanges.java b/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintChanges.java index 87e58ac35930..d5dc3889add3 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintChanges.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/paint/PaintChanges.java @@ -15,9 +15,14 @@ */ package com.android.internal.widget.remotecompose.core.operations.paint; +/** + * Interface to a paint object + * For more details see Android Paint + */ public interface PaintChanges { - + // MASK to be set/cleared + int CLEAR_TEXT_SIZE = 1 << (PaintBundle.TEXT_SIZE - 1); int CLEAR_TEXT_STYLE = 1 << (PaintBundle.TYPEFACE - 1); int CLEAR_COLOR = 1 << (PaintBundle.COLOR - 1); int CLEAR_STROKE_WIDTH = 1 << (PaintBundle.STROKE_WIDTH - 1); @@ -32,21 +37,101 @@ public interface PaintChanges { int CLEAR_COLOR_FILTER = 1 << (PaintBundle.COLOR_FILTER - 1); int VALID_BITS = 0x1FFF; // only the first 13 bit are valid now - + /** + * Set the size of text + * @param size + */ void setTextSize(float size); + + /** + * Set the width of lines + * @param width + */ void setStrokeWidth(float width); + + /** + * Set the color to use + * @param color + */ void setColor(int color); + + /** + * Set the Stroke Cap + * @param cap + */ void setStrokeCap(int cap); + + /** + * Set the Stroke style FILL and/or STROKE + * @param style + */ void setStyle(int style); - void setShader(int shader, String shaderString); + + /** + * Set the id of the shader to use + * @param shader + */ + void setShader(int shader); + + /** + * Set the way image is interpolated + * @param quality + */ void setImageFilterQuality(int quality); + + /** + * Set the alpha to draw under + * @param a + */ void setAlpha(float a); + + /** + * Set the Stroke Miter + * @param miter + */ void setStrokeMiter(float miter); + + /** + * Set the Stroke Join + * @param join + */ void setStrokeJoin(int join); + + /** + * Should bitmaps be interpolated + * @param filter + */ void setFilterBitmap(boolean filter); + + /** + * Set the blend mode can be porterduff + others + * @param mode + */ void setBlendMode(int mode); + + /** + * Set the AntiAlias. Typically true + * Set to off when you need pixilated look (e.g. QR codes) + * @param aa + */ void setAntiAlias(boolean aa); + + /** + * Clear some sub set of the settings + * @param mask + */ void clear(long mask); + + /** + * Set a linear gradient fill + * @param colorsArray + * @param stopsArray + * @param startX + * @param startY + * @param endX + * @param endY + * @param tileMode + */ void setLinearGradient( int[] colorsArray, float[] stopsArray, @@ -57,6 +142,15 @@ public interface PaintChanges { int tileMode ); + /** + * Set a radial gradient fill + * @param colorsArray + * @param stopsArray + * @param centerX + * @param centerY + * @param radius + * @param tileMode + */ void setRadialGradient( int[] colorsArray, float[] stopsArray, @@ -66,6 +160,13 @@ public interface PaintChanges { int tileMode ); + /** + * Set a sweep gradient fill + * @param colorsArray + * @param stopsArray + * @param centerX + * @param centerY + */ void setSweepGradient( int[] colorsArray, float[] stopsArray, @@ -73,9 +174,19 @@ public interface PaintChanges { float centerY ); - + /** + * Set Color filter mod + * @param color + * @param mode + */ void setColorFilter(int color, int mode); + /** + * Set TypeFace 0,1,2 + * TODO above should point to a string to be decoded + * @param fontType + * @param weight + * @param italic + */ void setTypeFace(int fontType, int weight, boolean italic); -} - +}
\ No newline at end of file diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/AnimatedFloatExpression.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/AnimatedFloatExpression.java new file mode 100644 index 000000000000..616048d424ec --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/AnimatedFloatExpression.java @@ -0,0 +1,452 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.widget.remotecompose.core.operations.utilities; + +/** + * high performance floating point expression evaluator used in animation + */ +public class AnimatedFloatExpression { + static IntMap<String> sNames = new IntMap<>(); + public static final int OFFSET = 0x100; + public static final float ADD = asNan(OFFSET + 1); + public static final float SUB = asNan(OFFSET + 2); + public static final float MUL = asNan(OFFSET + 3); + public static final float DIV = asNan(OFFSET + 4); + public static final float MOD = asNan(OFFSET + 5); + public static final float MIN = asNan(OFFSET + 6); + public static final float MAX = asNan(OFFSET + 7); + public static final float POW = asNan(OFFSET + 8); + public static final float SQRT = asNan(OFFSET + 9); + public static final float ABS = asNan(OFFSET + 10); + public static final float SIGN = asNan(OFFSET + 11); + public static final float COPY_SIGN = asNan(OFFSET + 12); + public static final float EXP = asNan(OFFSET + 13); + public static final float FLOOR = asNan(OFFSET + 14); + public static final float LOG = asNan(OFFSET + 15); + public static final float LN = asNan(OFFSET + 16); + public static final float ROUND = asNan(OFFSET + 17); + public static final float SIN = asNan(OFFSET + 18); + public static final float COS = asNan(OFFSET + 19); + public static final float TAN = asNan(OFFSET + 20); + public static final float ASIN = asNan(OFFSET + 21); + public static final float ACOS = asNan(OFFSET + 22); + + public static final float ATAN = asNan(OFFSET + 23); + + public static final float ATAN2 = asNan(OFFSET + 24); + public static final float MAD = asNan(OFFSET + 25); + public static final float IFELSE = asNan(OFFSET + 26); + + public static final float CLAMP = asNan(OFFSET + 27); + public static final float CBRT = asNan(OFFSET + 28); + public static final float DEG = asNan(OFFSET + 29); + public static final float RAD = asNan(OFFSET + 30); + public static final float CEIL = asNan(OFFSET + 31); + + + public static final float LAST_OP = 31; + + + public static final float VAR1 = asNan(OFFSET + 27); + public static final float VAR2 = asNan(OFFSET + 28); + + // TODO CLAMP, CBRT, DEG, RAD, EXPM1, CEIL, FLOOR + private static final float FP_PI = (float) Math.PI; + private static final float FP_TO_RAD = 57.29577951f; // 180/PI + private static final float FP_TO_DEG = 0.01745329252f; // 180/PI + + float[] mStack; + float[] mLocalStack = new float[128]; + float[] mVar; + + /** + * is float a math operator + * @param v + * @return + */ + public static boolean isMathOperator(float v) { + if (Float.isNaN(v)) { + int pos = fromNaN(v); + return pos > OFFSET && pos <= OFFSET + LAST_OP; + } + return false; + } + + interface Op { + int eval(int sp); + } + + /** + * Evaluate a float expression + * @param exp + * @param var + * @return + */ + public float eval(float[] exp, float... var) { + mStack = exp; + mVar = var; + int sp = -1; + for (int i = 0; i < mStack.length; i++) { + float v = mStack[i]; + if (Float.isNaN(v)) { + sp = mOps[fromNaN(v) - OFFSET].eval(sp); + } else { + mStack[++sp] = v; + } + } + return mStack[sp]; + } + + /** + * Evaluate a float expression + * @param exp + * @param len + * @param var + * @return + */ + public float eval(float[] exp, int len, float... var) { + System.arraycopy(exp, 0, mLocalStack, 0, len); + mStack = mLocalStack; + mVar = var; + int sp = -1; + for (int i = 0; i < len; i++) { + float v = mStack[i]; + if (Float.isNaN(v)) { + sp = mOps[fromNaN(v) - OFFSET].eval(sp); + } else { + mStack[++sp] = v; + } + } + return mStack[sp]; + } + + /** + * Evaluate a float expression + * @param exp + * @param var + * @return + */ + public float evalDB(float[] exp, float... var) { + mStack = exp; + mVar = var; + int sp = -1; + for (float v : exp) { + if (Float.isNaN(v)) { + System.out.print(" " + sNames.get((fromNaN(v) - OFFSET))); + sp = mOps[fromNaN(v) - OFFSET].eval(sp); + } else { + System.out.print(" " + v); + mStack[++sp] = v; + } + } + return mStack[sp]; + } + + Op[] mOps = { + null, + (sp) -> { // ADD + mStack[sp - 1] = mStack[sp - 1] + mStack[sp]; + return sp - 1; + }, + (sp) -> { // SUB + mStack[sp - 1] = mStack[sp - 1] - mStack[sp]; + return sp - 1; + }, + (sp) -> { // MUL + mStack[sp - 1] = mStack[sp - 1] * mStack[sp]; + return sp - 1; + }, + (sp) -> { // DIV + mStack[sp - 1] = mStack[sp - 1] / mStack[sp]; + return sp - 1; + }, + (sp) -> { // MOD + mStack[sp - 1] = mStack[sp - 1] % mStack[sp]; + return sp - 1; + }, + (sp) -> { // MIN + mStack[sp - 1] = (float) Math.min(mStack[sp - 1], mStack[sp]); + return sp - 1; + }, + (sp) -> { // MAX + mStack[sp - 1] = (float) Math.max(mStack[sp - 1], mStack[sp]); + return sp - 1; + }, + (sp) -> { // POW + mStack[sp - 1] = (float) Math.pow(mStack[sp - 1], mStack[sp]); + return sp - 1; + }, + (sp) -> { // SQRT + mStack[sp] = (float) Math.sqrt(mStack[sp]); + return sp; + }, + (sp) -> { // ABS + mStack[sp] = (float) Math.abs(mStack[sp]); + return sp; + }, + (sp) -> { // SIGN + mStack[sp] = (float) Math.signum(mStack[sp]); + return sp; + }, + (sp) -> { // copySign + mStack[sp - 1] = (float) Math.copySign(mStack[sp - 1], mStack[sp]); + return sp - 1; + }, + (sp) -> { // EXP + mStack[sp] = (float) Math.exp(mStack[sp]); + return sp; + }, + (sp) -> { // FLOOR + mStack[sp] = (float) Math.floor(mStack[sp]); + return sp; + }, + (sp) -> { // LOG + mStack[sp] = (float) Math.log10(mStack[sp]); + return sp; + }, + (sp) -> { // LN + mStack[sp] = (float) Math.log(mStack[sp]); + return sp; + }, + (sp) -> { // ROUND + mStack[sp] = (float) Math.round(mStack[sp]); + return sp; + }, + (sp) -> { // SIN + mStack[sp] = (float) Math.sin(mStack[sp]); + return sp; + }, + (sp) -> { // COS + mStack[sp] = (float) Math.cos(mStack[sp]); + return sp; + }, + (sp) -> { // TAN + mStack[sp] = (float) Math.tan(mStack[sp]); + return sp; + }, + (sp) -> { // ASIN + mStack[sp] = (float) Math.asin(mStack[sp]); + return sp; + }, + (sp) -> { // ACOS + mStack[sp] = (float) Math.acos(mStack[sp]); + return sp; + }, + (sp) -> { // ATAN + mStack[sp] = (float) Math.atan(mStack[sp]); + return sp; + }, + (sp) -> { // ATAN2 + mStack[sp - 1] = (float) Math.atan2(mStack[sp - 1], mStack[sp]); + return sp - 1; + }, + (sp) -> { // MAD + mStack[sp - 2] = mStack[sp] + mStack[sp - 1] * mStack[sp - 2]; + return sp - 2; + }, + (sp) -> { // Ternary conditional + mStack[sp - 2] = (mStack[sp] > 0) + ? mStack[sp - 1] : mStack[sp - 2]; + return sp - 2; + }, + (sp) -> { // CLAMP(min,max, val) + mStack[sp - 2] = Math.min(Math.max(mStack[sp - 2], mStack[sp]), + mStack[sp - 1]); + return sp - 2; + }, + (sp) -> { // CBRT cuberoot + mStack[sp] = (float) Math.pow(mStack[sp], 1 / 3.); + return sp; + }, + (sp) -> { // DEG + mStack[sp] = mStack[sp] * FP_TO_RAD; + return sp; + }, + (sp) -> { // RAD + mStack[sp] = mStack[sp] * FP_TO_DEG; + return sp; + }, + (sp) -> { // CEIL + mStack[sp] = (float) Math.ceil(mStack[sp]); + return sp; + }, + (sp) -> { // first var = + mStack[sp] = mVar[0]; + return sp; + }, + (sp) -> { // second var y? + mStack[sp] = mVar[1]; + return sp; + }, + (sp) -> { // 3rd var z? + mStack[sp] = mVar[2]; + return sp; + }, + }; + + static { + int k = 0; + sNames.put(k++, "NOP"); + sNames.put(k++, "+"); + sNames.put(k++, "-"); + sNames.put(k++, "*"); + sNames.put(k++, "/"); + sNames.put(k++, "%"); + sNames.put(k++, "min"); + sNames.put(k++, "max"); + sNames.put(k++, "pow"); + sNames.put(k++, "sqrt"); + sNames.put(k++, "abs"); + sNames.put(k++, "sign"); + sNames.put(k++, "copySign"); + sNames.put(k++, "exp"); + sNames.put(k++, "floor"); + sNames.put(k++, "log"); + sNames.put(k++, "ln"); + sNames.put(k++, "round"); + sNames.put(k++, "sin"); + sNames.put(k++, "cos"); + sNames.put(k++, "tan"); + sNames.put(k++, "asin"); + sNames.put(k++, "acos"); + sNames.put(k++, "atan"); + sNames.put(k++, "atan2"); + sNames.put(k++, "mad"); + sNames.put(k++, "ifElse"); + sNames.put(k++, "clamp"); + sNames.put(k++, "cbrt"); + sNames.put(k++, "deg"); + sNames.put(k++, "rad"); + sNames.put(k++, "ceil"); + sNames.put(k++, "a[0]"); + sNames.put(k++, "a[1]"); + sNames.put(k++, "a[2]"); + } + + /** + * given a float command return its math name (e.g sin, cos etc.) + * @param f + * @return + */ + public static String toMathName(float f) { + int id = fromNaN(f) - OFFSET; + return sNames.get(id); + } + + /** + * Convert an expression encoded as an array of floats int ot a string + * @param exp + * @param labels + * @return + */ + public static String toString(float[] exp, String[] labels) { + StringBuilder s = new StringBuilder(); + for (int i = 0; i < exp.length; i++) { + float v = exp[i]; + if (Float.isNaN(v)) { + if (isMathOperator(v)) { + s.append(toMathName(v)); + } else { + s.append("["); + s.append(fromNaN(v)); + s.append("]"); + } + } else { + if (labels[i] != null) { + s.append(labels[i]); + } + s.append(v); + } + s.append(" "); + } + return s.toString(); + } + + static String toString(float[] exp, int sp) { + String[] str = new String[exp.length]; + if (Float.isNaN(exp[sp])) { + int id = fromNaN(exp[sp]) - OFFSET; + switch (NO_OF_OPS[id]) { + case -1: + return "nop"; + case 1: + return sNames.get(id) + "(" + toString(exp, sp + 1) + ") "; + case 2: + if (infix(id)) { + return "(" + toString(exp, sp + 1) + + sNames.get(id) + " " + + toString(exp, sp + 2) + ") "; + } else { + return sNames.get(id) + "(" + + toString(exp, sp + 1) + ", " + + toString(exp, sp + 2) + ")"; + } + case 3: + if (infix(id)) { + return "((" + toString(exp, sp + 1) + ") ? " + + toString(exp, sp + 2) + ":" + + toString(exp, sp + 3) + ")"; + } else { + return sNames.get(id) + + "(" + toString(exp, sp + 1) + + ", " + toString(exp, sp + 2) + + ", " + toString(exp, sp + 3) + ")"; + } + } + } + return Float.toString(exp[sp]); + } + + static final int[] NO_OF_OPS = { + -1, // no op + 2, 2, 2, 2, 2, // + - * / % + 2, 2, 2, // min max, power + 1, 1, 1, 1, 1, 1, 1, 1, //sqrt,abs,CopySign,exp,floor,log,ln + 1, 1, 1, 1, 1, 1, 1, 2, // round,sin,cos,tan,asin,acos,atan,atan2 + 3, 3, 3, 1, 1, 1, 1, + 0, 0, 0 // mad, ?:, + // a[0],a[1],a[2] + }; + + /** + * to be used by parser to determine if command is infix + * @param n + * @return + */ + static boolean infix(int n) { + return ((n < 6) || (n == 25) || (n == 26)); + } + + /** + * Convert an id into a NaN object + * @param v + * @return + */ + public static float asNan(int v) { + return Float.intBitsToFloat(v | -0x800000); + } + + /** + * Get ID from a NaN float + * @param v + * @return + */ + public static int fromNaN(float v) { + int b = Float.floatToRawIntBits(v); + return b & 0xFFFFF; + } + +} diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/ColorUtils.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/ColorUtils.java new file mode 100644 index 000000000000..0ea28a8bb900 --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/ColorUtils.java @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.widget.remotecompose.core.operations.utilities; + +/** + * These are tools to use long Color as variables + * long colors are stored a 0xXXXXXXXX XXXXXX?? + * in SRGB the colors are stored 0xAARRGGBB,00000000 + * SRGB color sapce is color space 0 + * Our Color will use color float with a + * Current android supports + * SRGB, LINEAR_SRGB, EXTENDED_SRGB, LINEAR_EXTENDED_SRGB, BT709, BT2020, + * DCI_P3, DISPLAY_P3, NTSC_1953, SMPTE_C, ADOBE_RGB, PRO_PHOTO_RGB, ACES, + * ACESCG, CIE_XYZ, CIE_LAB, BT2020_HLG, BT2020_PQ 0..17 respectively + * + * Our color space will be 62 (MAX_ID-1). (0x3E) + * Storing the default value in SRGB format and having the + * id of the color between the ARGB values and the 62 i.e. + * 0xAARRGGBB 00 00 00 3E + * + */ +public class ColorUtils { + public static int RC_COLOR = 62; + + long packRCColor(int defaultARGB, int id) { + long l = defaultARGB; + return (l << 32) | id << 8 | RC_COLOR; + } + + boolean isRCColor(long color) { + return ((color & 0x3F) == 62); + } + + int getID(long color) { + if (isRCColor(color)) { + return (int) ((color & 0xFFFFFF00) >> 8); + } + return -1; + } + + /** + * get default color from long color + * @param color + * @return + */ + public int getDefaultColor(long color) { + if (isRCColor(color)) { + return (int) (color >> 32); + } + if (((color & 0xFF) == 0)) { + return (int) (color >> 32); + } + return 0; + } +} diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntMap.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntMap.java index 8051ef1ab37c..0512fa6be710 100644 --- a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntMap.java +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/IntMap.java @@ -50,7 +50,6 @@ public class IntMap<T> { return insert(key, value); } - public T get(int key) { int index = findKey(key); if (index == -1) { diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/NanMap.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/NanMap.java new file mode 100644 index 000000000000..f4cd504d650f --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/NanMap.java @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.widget.remotecompose.core.operations.utilities; + +import com.android.internal.widget.remotecompose.core.operations.Utils; + +/** + * This defines the major id maps and ranges used by remote compose + * Generally ids ranging from 0 ... FFF (4095) are for ids + * 0x1000-0x1100 are used for path operations in PathData + * 0x1100-0x1200 are used for math operations in Animated float + * 0x + */ +public class NanMap { + + public static final int MOVE = 0x1000; + public static final int LINE = 0x1001; + public static final int QUADRATIC = 0x1002; + public static final int CONIC = 0x1003; + public static final int CUBIC = 0x1004; + public static final int CLOSE = 0x1005; + public static final int DONE = 0x1006; + public static final float MOVE_NAN = Utils.asNan(MOVE); + public static final float LINE_NAN = Utils.asNan(LINE); + public static final float QUADRATIC_NAN = Utils.asNan(QUADRATIC); + public static final float CONIC_NAN = Utils.asNan(CONIC); + public static final float CUBIC_NAN = Utils.asNan(CUBIC); + public static final float CLOSE_NAN = Utils.asNan(CLOSE); + public static final float DONE_NAN = Utils.asNan(DONE); + + /** + * + */ + public static final float ADD = asNan(0x1100); + public static final float SUB = asNan(0x1101); + public static final float MUL = asNan(0x1102); + public static final float DIV = asNan(0x1103); + public static final float MOD = asNan(0x1104); + public static final float MIN = asNan(0x1105); + public static final float MAX = asNan(0x1106); + public static final float POW = asNan(0x1107); + + + /** + * Get ID from Nan float + * @param v + * @return + */ + public static int fromNaN(float v) { + int b = Float.floatToRawIntBits(v); + return b & 0xFFFFF; + } + + /** + * Given id return as a Nan float + * @param v + * @return + */ + public static float asNan(int v) { + return Float.intBitsToFloat(v | 0xFF800000); + } +} diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/StringUtils.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/StringUtils.java new file mode 100644 index 000000000000..8dd5405ddf12 --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/StringUtils.java @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.widget.remotecompose.core.operations.utilities; + +import java.util.Arrays; + +/** + * Utilities for string manipulation + */ +public class StringUtils { + /** + * Converts a float into a string. + * Providing a defined number of characters before and after the + * decimal point. + * + * @param value The value to convert to string + * @param beforeDecimalPoint digits before the decimal point + * @param afterDecimalPoint digits after the decimal point + * @param pre character to pad width 0 = no pad typically ' ' or '0' + * @param post character to pad width 0 = no pad typically ' ' or '0' + * @return + */ + public static String floatToString(float value, + int beforeDecimalPoint, + int afterDecimalPoint, + char pre, char post) { + + int integerPart = (int) value; + float fractionalPart = value % 1; + + // Convert integer part to string and pad with spaces + String integerPartString = String.valueOf(integerPart); + int iLen = integerPartString.length(); + if (iLen < beforeDecimalPoint) { + int spacesToPad = beforeDecimalPoint - iLen; + if (pre != 0) { + char[] pad = new char[spacesToPad]; + Arrays.fill(pad, pre); + integerPartString = new String(pad) + integerPartString; + } + + + } else if (iLen > beforeDecimalPoint) { + integerPartString = integerPartString.substring(iLen - beforeDecimalPoint); + } + if (afterDecimalPoint == 0) { + return integerPartString; + } + // Convert fractional part to string and pad with zeros + + for (int i = 0; i < afterDecimalPoint; i++) { + fractionalPart *= 10; + } + + fractionalPart = Math.round(fractionalPart); + + for (int i = 0; i < afterDecimalPoint; i++) { + fractionalPart *= .1; + } + + String fact = Float.toString(fractionalPart); + fact = fact.substring(2, Math.min(fact.length(), afterDecimalPoint + 2)); + int trim = fact.length(); + for (int i = fact.length() - 1; i >= 0; i--) { + if (fact.charAt(i) != '0') { + break; + } + trim--; + } + if (trim != fact.length()) { + fact = fact.substring(0, trim); + } + int len = fact.length(); + if (post != 0 && len < afterDecimalPoint) { + char[] c = new char[afterDecimalPoint - len]; + Arrays.fill(c, post); + fact = fact + new String(c); + } + + return integerPartString + "." + fact; + } + +} diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/BounceCurve.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/BounceCurve.java new file mode 100644 index 000000000000..c3cd5ae9c79d --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/BounceCurve.java @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.widget.remotecompose.core.operations.utilities.easing; + +/** + * Provide a specific bouncing easing function + */ +public class BounceCurve extends Easing { + private static final float N1 = 7.5625f; + private static final float D1 = 2.75f; + + BounceCurve(int type) { + mType = type; + } + + @Override + public float get(float x) { + float t = x; + if (t < 0) { + return 0f; + } + if (t < 1 / D1) { + return 1 / (1 + 1 / D1) * (N1 * t * t + t); + } else if (t < 2 / D1) { + t -= 1.5f / D1; + return N1 * t * t + 0.75f; + } else if (t < 2.5 / D1) { + t -= 2.25f / D1; + return N1 * t * t + 0.9375f; + } else if (t <= 1) { + t -= 2.625f / D1; + return N1 * t * t + 0.984375f; + } + return 1f; + } + + @Override + public float getDiff(float x) { + if (x < 0) { + return 0f; + } + if (x < 1 / D1) { + return 2 * N1 * x / (1 + 1 / D1) + 1 / (1 + 1 / D1); + } else if (x < 2 / D1) { + return 2 * N1 * (x - 1.5f / D1); + } else if (x < 2.5 / D1) { + return 2 * N1 * (x - 2.25f / D1); + } else if (x <= 1) { + return 2 * N1 * (x - 2.625f / D1); + } + return 0f; + } + +} diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/CubicEasing.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/CubicEasing.java new file mode 100644 index 000000000000..fd1ee036e475 --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/CubicEasing.java @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.widget.remotecompose.core.operations.utilities.easing; + +class CubicEasing extends Easing { + float mType = 0; + float mX1 = 0f; + float mY1 = 0f; + float mX2 = 0f; + float mY2 = 0f; + + private static final float[] STANDARD = {0.4f, 0.0f, 0.2f, 1f}; + private static final float[] ACCELERATE = {0.4f, 0.05f, 0.8f, 0.7f}; + private static final float[] DECELERATE = {0.0f, 0.0f, 0.2f, 0.95f}; + private static final float[] LINEAR = {1f, 1f, 0f, 0f}; + private static final float[] ANTICIPATE = {0.36f, 0f, 0.66f, -0.56f}; + private static final float[] OVERSHOOT = {0.34f, 1.56f, 0.64f, 1f}; + + CubicEasing(int type) { + mType = type; + config(type); + } + + CubicEasing(float x1, float y1, float x2, float y2) { + setup(x1, y1, x2, y2); + } + + public void config(int type) { + + switch (type) { + case CUBIC_STANDARD: + setup(STANDARD); + break; + case CUBIC_ACCELERATE: + setup(ACCELERATE); + break; + case CUBIC_DECELERATE: + setup(DECELERATE); + break; + case CUBIC_LINEAR: + setup(LINEAR); + break; + case CUBIC_ANTICIPATE: + setup(ANTICIPATE); + break; + case CUBIC_OVERSHOOT: + setup(OVERSHOOT); + break; + } + mType = type; + } + + void setup(float[] values) { + setup(values[0], values[1], values[2], values[3]); + } + + void setup(float x1, float y1, float x2, float y2) { + mX1 = x1; + mY1 = y1; + mX2 = x2; + mY2 = y2; + } + + private float getX(float t) { + float t1 = 1 - t; + // no need for because start at 0,0 float f0 = (1 - t) * (1 - t) * (1 - t) + float f1 = 3 * t1 * t1 * t; + float f2 = 3 * t1 * t * t; + float f3 = t * t * t; + return mX1 * f1 + mX2 * f2 + f3; + } + + private float getY(float t) { + float t1 = 1 - t; + // no need for testing because start at 0,0 float f0 = (1 - t) * (1 - t) * (1 - t) + float f1 = 3 * t1 * t1 * t; + float f2 = 3 * t1 * t * t; + float f3 = t * t * t; + return mY1 * f1 + mY2 * f2 + f3; + } + + private float getDiffX(float t) { + float t1 = 1 - t; + return 3 * t1 * t1 * mX1 + 6 * t1 * t * (mX2 - mX1) + 3 * t * t * (1 - mX2); + } + + private float getDiffY(float t) { + float t1 = 1 - t; + return 3 * t1 * t1 * mY1 + 6 * t1 * t * (mY2 - mY1) + 3 * t * t * (1 - mY2); + } + + /** + * binary search for the region and linear interpolate the answer + */ + public float getDiff(float x) { + float t = 0.5f; + float range = 0.5f; + while (range > D_ERROR) { + float tx = getX(t); + range *= 0.5; + if (tx < x) { + t += range; + } else { + t -= range; + } + } + float x1 = getX(t - range); + float x2 = getX(t + range); + float y1 = getY(t - range); + float y2 = getY(t + range); + return (y2 - y1) / (x2 - x1); + } + + /** + * binary search for the region and linear interpolate the answer + */ + public float get(float x) { + if (x <= 0.0f) { + return 0f; + } + if (x >= 1.0f) { + return 1.0f; + } + float t = 0.5f; + float range = 0.5f; + while (range > ERROR) { + float tx = getX(t); + range *= 0.5f; + if (tx < x) { + t += range; + } else { + t -= range; + } + } + float x1 = getX(t - range); + float x2 = getX(t + range); + float y1 = getY(t - range); + float y2 = getY(t + range); + return (y2 - y1) * (x - x1) / (x2 - x1) + y1; + } + + private static final float ERROR = 0.01f; + private static final float D_ERROR = 0.0001f; +} diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/Easing.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/Easing.java new file mode 100644 index 000000000000..4ed955069d78 --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/Easing.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.widget.remotecompose.core.operations.utilities.easing; + +/** + * The standard interface to Easing functions + */ +public abstract class Easing { + int mType; + /** + * get the value at point x + */ + public abstract float get(float x); + + /** + * get the slope of the easing function at at x + */ + public abstract float getDiff(float x); + + public int getType() { + return mType; + } + + public static final int CUBIC_STANDARD = 1; + public static final int CUBIC_ACCELERATE = 2; + public static final int CUBIC_DECELERATE = 3; + public static final int CUBIC_LINEAR = 4; + public static final int CUBIC_ANTICIPATE = 5; + public static final int CUBIC_OVERSHOOT = 6; + public static final int CUBIC_CUSTOM = 11; + public static final int SPLINE_CUSTOM = 12; + public static final int EASE_OUT_BOUNCE = 13; + public static final int EASE_OUT_ELASTIC = 14; + +} diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/ElasticOutCurve.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/ElasticOutCurve.java new file mode 100644 index 000000000000..e26958302e3c --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/ElasticOutCurve.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.widget.remotecompose.core.operations.utilities.easing; + +/** + * Provide a bouncing Easing function + */ +public class ElasticOutCurve extends Easing { + private static final float F_PI = (float) Math.PI; + private static final float C4 = 2 * F_PI / 3; + private static final float TWENTY_PI = 20 * F_PI; + private static final float LOG_8 = (float) Math.log(8.0f); + + @Override + public float get(float x) { + if (x <= 0) { + return 0.0f; + } + if (x >= 1) { + return 1.0f; + } else + return (float) (Math.pow(2.0f, -10 * x) + * Math.sin((x * 10 - 0.75f) * C4) + 1); + } + + @Override + public float getDiff(float x) { + if (x < 0 || x > 1) { + return 0.0f; + } else + return (float) ((5 * Math.pow(2.0f, (1 - 10 * x)) + * (LOG_8 * Math.cos(TWENTY_PI * x / 3) + 2 + * F_PI * Math.sin(TWENTY_PI * x / 3)) + / 3)); + } +} diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/FloatAnimation.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/FloatAnimation.java new file mode 100644 index 000000000000..4f484de1045e --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/FloatAnimation.java @@ -0,0 +1,259 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.widget.remotecompose.core.operations.utilities.easing; + +/** + * Support Animation of the FloatExpression + */ +public class FloatAnimation extends Easing { + float[] mSpec; + // mSpec[0] = duration + // int(mSpec[1]) = num_of_param << 16 | type + // mSpec[2..1+num_of_param] params + // mSpec[2+num_of_param] starting Value + Easing mEasingCurve; + private int mType = CUBIC_STANDARD; + private float mDuration = 1; + private float mWrap = Float.NaN; + private float mInitialValue = Float.NaN; + private float mTargetValue = Float.NaN; + private float mScale = 1; + float mOffset = 0; + + @Override + public String toString() { + + String str = "type " + mType; + if (!Float.isNaN(mInitialValue)) { + str += " " + mInitialValue; + } + if (!Float.isNaN(mTargetValue)) { + str += " -> " + mTargetValue; + } + if (!Float.isNaN(mWrap)) { + str += " % " + mWrap; + } + + return str; + } + + public FloatAnimation() { + } + + public FloatAnimation(float... description) { + setAnimationDescription(description); + } + + public FloatAnimation(int type, + float duration, + float[] description, + float initialValue, + float wrap) { + setAnimationDescription(packToFloatArray(duration, + type, description, initialValue, wrap)); + } + + /** + * packs spec into a float array + * + * @param duration + * @param type + * @param spec + * @param initialValue + * @return + */ + public static float[] packToFloatArray(float duration, + int type, + float[] spec, + float initialValue, + float wrap) { + int count = 0; + + if (!Float.isNaN(initialValue)) { + count++; + } + if (spec != null) { + count++; + } + if (spec != null || type != CUBIC_STANDARD) { + count++; + count += (spec == null) ? 0 : spec.length; + } + if (duration != 1 || count > 0) { + count++; + } + if (!Float.isNaN(initialValue)) { + count++; + } + if (!Float.isNaN(wrap)) { + count++; + } + float[] ret = new float[count]; + int pos = 0; + int specLen = (spec == null) ? 0 : spec.length; + + if (ret.length > 0) { + ret[pos++] = duration; + + } + if (ret.length > 1) { + int wrapBit = (Float.isNaN(wrap)) ? 0 : 1; + int initBit = (Float.isNaN(initialValue)) ? 0 : 2; + int bits = type | ((wrapBit | initBit) << 8); + ret[pos++] = Float.intBitsToFloat(specLen << 16 | bits); + } + + if (specLen > 0) { + System.arraycopy(spec, 0, ret, pos, spec.length); + pos += spec.length; + } + if (!Float.isNaN(initialValue)) { + ret[pos++] = initialValue; + } + if (!Float.isNaN(wrap)) { + ret[pos] = wrap; + } + return ret; + } + + /** + * Create an animation based on a float encoding of the animation + * @param description + */ + public void setAnimationDescription(float[] description) { + mSpec = description; + mDuration = (mSpec.length == 0) ? 1 : mSpec[0]; + int len = 0; + if (mSpec.length > 1) { + int num_type = Float.floatToRawIntBits(mSpec[1]); + mType = num_type & 0xFF; + boolean wrap = ((num_type >> 8) & 0x1) > 0; + boolean init = ((num_type >> 8) & 0x2) > 0; + len = (num_type >> 16) & 0xFFFF; + int off = 2 + len; + if (init) { + mInitialValue = mSpec[off++]; + } + if (wrap) { + mWrap = mSpec[off]; + } + } + create(mType, description, 2, len); + } + + private void create(int type, float[] params, int offset, int len) { + switch (type) { + case CUBIC_STANDARD: + case CUBIC_ACCELERATE: + case CUBIC_DECELERATE: + case CUBIC_LINEAR: + case CUBIC_ANTICIPATE: + case CUBIC_OVERSHOOT: + mEasingCurve = new CubicEasing(type); + break; + case CUBIC_CUSTOM: + mEasingCurve = new CubicEasing(params[offset + 0], + params[offset + 1], + params[offset + 2], + params[offset + 3] + ); + break; + case EASE_OUT_BOUNCE: + mEasingCurve = new BounceCurve(type); + break; + case EASE_OUT_ELASTIC: + mEasingCurve = new ElasticOutCurve(); + break; + case SPLINE_CUSTOM: + mEasingCurve = new StepCurve(params, offset, len); + break; + } + } + + /** + * Get the duration the interpolate is to take + * @return duration in seconds + */ + public float getDuration() { + return mDuration; + } + + /** + * Set the initial Value + * @param value + */ + public void setInitialValue(float value) { + + if (Float.isNaN(mWrap)) { + mInitialValue = value; + } else { + mInitialValue = value % mWrap; + } + setScaleOffset(); + } + + /** + * Set the target value to interpolate to + * @param value + */ + public void setTargetValue(float value) { + if (Float.isNaN(mWrap)) { + mTargetValue = value; + } else { + if (Math.abs((value % mWrap) + mWrap - mInitialValue) + < Math.abs((value % mWrap) - mInitialValue)) { + mTargetValue = (value % mWrap) + mWrap; + + } else { + mTargetValue = value % mWrap; + } + } + setScaleOffset(); + } + + public float getTargetValue() { + return mTargetValue; + } + + private void setScaleOffset() { + if (!Float.isNaN(mInitialValue) && !Float.isNaN(mTargetValue)) { + mScale = (mTargetValue - mInitialValue); + mOffset = mInitialValue; + } else { + mScale = 1; + mOffset = 0; + } + } + + /** + * get the value at time t in seconds since start + */ + public float get(float t) { + return mEasingCurve.get(t / mDuration) + * (mTargetValue - mInitialValue) + mInitialValue; + } + + /** + * get the slope of the easing function at at x + */ + public float getDiff(float t) { + return mEasingCurve.getDiff(t / mDuration) * (mTargetValue - mInitialValue); + } + + public float getInitialValue() { + return mInitialValue; + } +} diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/GeneralEasing.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/GeneralEasing.java new file mode 100644 index 000000000000..693deafc5b00 --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/GeneralEasing.java @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.widget.remotecompose.core.operations.utilities.easing; + +/** + * Provides and interface to create easing functions + */ +public class GeneralEasing extends Easing{ + float[] mEasingData = new float[0]; + Easing mEasingCurve = new CubicEasing(CUBIC_STANDARD); + + /** + * Set the curve based on the float encoding of it + * @param data + */ + public void setCurveSpecification(float[] data) { + mEasingData = data; + createEngine(); + } + + public float[] getCurveSpecification() { + return mEasingData; + } + + void createEngine() { + int type = Float.floatToRawIntBits(mEasingData[0]); + switch (type) { + case CUBIC_STANDARD: + case CUBIC_ACCELERATE: + case CUBIC_DECELERATE: + case CUBIC_LINEAR: + case CUBIC_ANTICIPATE: + case CUBIC_OVERSHOOT: + mEasingCurve = new CubicEasing(type); + break; + case CUBIC_CUSTOM: + mEasingCurve = new CubicEasing(mEasingData[1], + mEasingData[2], + mEasingData[3], + mEasingData[5] + ); + break; + case EASE_OUT_BOUNCE: + mEasingCurve = new BounceCurve(type); + break; + } + } + + /** + * get the value at point x + */ + public float get(float x) { + return mEasingCurve.get(x); + } + + /** + * get the slope of the easing function at at x + */ + public float getDiff(float x) { + return mEasingCurve.getDiff(x); + } + + public int getType() { + return mEasingCurve.getType(); + } + + +} diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/MonotonicCurveFit.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/MonotonicCurveFit.java new file mode 100644 index 000000000000..23930b92a282 --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/MonotonicCurveFit.java @@ -0,0 +1,370 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.widget.remotecompose.core.operations.utilities.easing; + +import java.util.Arrays; + +/** + * This performs a spline interpolation in multiple dimensions + * + * + */ +public class MonotonicCurveFit { + private static final String TAG = "MonotonicCurveFit"; + private double[] mT; + private double[][] mY; + private double[][] mTangent; + private boolean mExtrapolate = true; + double[] mSlopeTemp; + + /** + * create a collection of curves + * @param time the point along the curve + * @param y the parameter at those points + */ + public MonotonicCurveFit(double[] time, double[][] y) { + final int n = time.length; + final int dim = y[0].length; + mSlopeTemp = new double[dim]; + double[][] slope = new double[n - 1][dim]; // could optimize this out + double[][] tangent = new double[n][dim]; + for (int j = 0; j < dim; j++) { + for (int i = 0; i < n - 1; i++) { + double dt = time[i + 1] - time[i]; + slope[i][j] = (y[i + 1][j] - y[i][j]) / dt; + if (i == 0) { + tangent[i][j] = slope[i][j]; + } else { + tangent[i][j] = (slope[i - 1][j] + slope[i][j]) * 0.5f; + } + } + tangent[n - 1][j] = slope[n - 2][j]; + } + + for (int i = 0; i < n - 1; i++) { + for (int j = 0; j < dim; j++) { + if (slope[i][j] == 0.) { + tangent[i][j] = 0.; + tangent[i + 1][j] = 0.; + } else { + double a = tangent[i][j] / slope[i][j]; + double b = tangent[i + 1][j] / slope[i][j]; + double h = Math.hypot(a, b); + if (h > 9.0) { + double t = 3. / h; + tangent[i][j] = t * a * slope[i][j]; + tangent[i + 1][j] = t * b * slope[i][j]; + } + } + } + } + mT = time; + mY = y; + mTangent = tangent; + } + + /** + * Get the position of all curves at time t + * @param t + * @param v + */ + public void getPos(double t, double[] v) { + final int n = mT.length; + final int dim = mY[0].length; + if (mExtrapolate) { + if (t <= mT[0]) { + getSlope(mT[0], mSlopeTemp); + for (int j = 0; j < dim; j++) { + v[j] = mY[0][j] + (t - mT[0]) * mSlopeTemp[j]; + } + return; + } + if (t >= mT[n - 1]) { + getSlope(mT[n - 1], mSlopeTemp); + for (int j = 0; j < dim; j++) { + v[j] = mY[n - 1][j] + (t - mT[n - 1]) * mSlopeTemp[j]; + } + return; + } + } else { + if (t <= mT[0]) { + for (int j = 0; j < dim; j++) { + v[j] = mY[0][j]; + } + return; + } + if (t >= mT[n - 1]) { + for (int j = 0; j < dim; j++) { + v[j] = mY[n - 1][j]; + } + return; + } + } + + for (int i = 0; i < n - 1; i++) { + if (t == mT[i]) { + for (int j = 0; j < dim; j++) { + v[j] = mY[i][j]; + } + } + if (t < mT[i + 1]) { + double h = mT[i + 1] - mT[i]; + double x = (t - mT[i]) / h; + for (int j = 0; j < dim; j++) { + double y1 = mY[i][j]; + double y2 = mY[i + 1][j]; + double t1 = mTangent[i][j]; + double t2 = mTangent[i + 1][j]; + v[j] = interpolate(h, x, y1, y2, t1, t2); + } + return; + } + } + } + + /** + * Get the position of all curves at time t + * @param t + * @param v + */ + public void getPos(double t, float[] v) { + final int n = mT.length; + final int dim = mY[0].length; + if (mExtrapolate) { + if (t <= mT[0]) { + getSlope(mT[0], mSlopeTemp); + for (int j = 0; j < dim; j++) { + v[j] = (float) (mY[0][j] + (t - mT[0]) * mSlopeTemp[j]); + } + return; + } + if (t >= mT[n - 1]) { + getSlope(mT[n - 1], mSlopeTemp); + for (int j = 0; j < dim; j++) { + v[j] = (float) (mY[n - 1][j] + (t - mT[n - 1]) * mSlopeTemp[j]); + } + return; + } + } else { + if (t <= mT[0]) { + for (int j = 0; j < dim; j++) { + v[j] = (float) mY[0][j]; + } + return; + } + if (t >= mT[n - 1]) { + for (int j = 0; j < dim; j++) { + v[j] = (float) mY[n - 1][j]; + } + return; + } + } + + for (int i = 0; i < n - 1; i++) { + if (t == mT[i]) { + for (int j = 0; j < dim; j++) { + v[j] = (float) mY[i][j]; + } + } + if (t < mT[i + 1]) { + double h = mT[i + 1] - mT[i]; + double x = (t - mT[i]) / h; + for (int j = 0; j < dim; j++) { + double y1 = mY[i][j]; + double y2 = mY[i + 1][j]; + double t1 = mTangent[i][j]; + double t2 = mTangent[i + 1][j]; + v[j] = (float) interpolate(h, x, y1, y2, t1, t2); + } + return; + } + } + } + + /** + * Get the position of the jth curve at time t + * @param t + * @param j + * @return + */ + public double getPos(double t, int j) { + final int n = mT.length; + if (mExtrapolate) { + if (t <= mT[0]) { + return mY[0][j] + (t - mT[0]) * getSlope(mT[0], j); + } + if (t >= mT[n - 1]) { + return mY[n - 1][j] + (t - mT[n - 1]) * getSlope(mT[n - 1], j); + } + } else { + if (t <= mT[0]) { + return mY[0][j]; + } + if (t >= mT[n - 1]) { + return mY[n - 1][j]; + } + } + + for (int i = 0; i < n - 1; i++) { + if (t == mT[i]) { + return mY[i][j]; + } + if (t < mT[i + 1]) { + double h = mT[i + 1] - mT[i]; + double x = (t - mT[i]) / h; + double y1 = mY[i][j]; + double y2 = mY[i + 1][j]; + double t1 = mTangent[i][j]; + double t2 = mTangent[i + 1][j]; + return interpolate(h, x, y1, y2, t1, t2); + + } + } + return 0; // should never reach here + } + + /** + * Get the slope of all the curves at position t + * @param t + * @param v + */ + public void getSlope(double t, double[] v) { + final int n = mT.length; + int dim = mY[0].length; + if (t <= mT[0]) { + t = mT[0]; + } else if (t >= mT[n - 1]) { + t = mT[n - 1]; + } + + for (int i = 0; i < n - 1; i++) { + if (t <= mT[i + 1]) { + double h = mT[i + 1] - mT[i]; + double x = (t - mT[i]) / h; + for (int j = 0; j < dim; j++) { + double y1 = mY[i][j]; + double y2 = mY[i + 1][j]; + double t1 = mTangent[i][j]; + double t2 = mTangent[i + 1][j]; + v[j] = diff(h, x, y1, y2, t1, t2) / h; + } + break; + } + } + return; + } + + /** + * Get the slope of the j curve at position t + * @param t + * @param j + * @return + */ + public double getSlope(double t, int j) { + final int n = mT.length; + + if (t < mT[0]) { + t = mT[0]; + } else if (t >= mT[n - 1]) { + t = mT[n - 1]; + } + for (int i = 0; i < n - 1; i++) { + if (t <= mT[i + 1]) { + double h = mT[i + 1] - mT[i]; + double x = (t - mT[i]) / h; + double y1 = mY[i][j]; + double y2 = mY[i + 1][j]; + double t1 = mTangent[i][j]; + double t2 = mTangent[i + 1][j]; + return diff(h, x, y1, y2, t1, t2) / h; + } + } + return 0; // should never reach here + } + + public double[] getTimePoints() { + return mT; + } + + /** + * Cubic Hermite spline + */ + private static double interpolate(double h, + double x, + double y1, + double y2, + double t1, + double t2) { + double x2 = x * x; + double x3 = x2 * x; + return -2 * x3 * y2 + 3 * x2 * y2 + 2 * x3 * y1 - 3 * x2 * y1 + y1 + + h * t2 * x3 + h * t1 * x3 - h * t2 * x2 - 2 * h * t1 * x2 + + h * t1 * x; + } + + /** + * Cubic Hermite spline slope differentiated + */ + private static double diff(double h, double x, double y1, double y2, double t1, double t2) { + double x2 = x * x; + return -6 * x2 * y2 + 6 * x * y2 + 6 * x2 * y1 - 6 * x * y1 + 3 * h * t2 * x2 + + 3 * h * t1 * x2 - 2 * h * t2 * x - 4 * h * t1 * x + h * t1; + } + + /** + * This builds a monotonic spline to be used as a wave function + */ + public static MonotonicCurveFit buildWave(String configString) { + // done this way for efficiency + String str = configString; + double[] values = new double[str.length() / 2]; + int start = configString.indexOf('(') + 1; + int off1 = configString.indexOf(',', start); + int count = 0; + while (off1 != -1) { + String tmp = configString.substring(start, off1).trim(); + values[count++] = Double.parseDouble(tmp); + off1 = configString.indexOf(',', start = off1 + 1); + } + off1 = configString.indexOf(')', start); + String tmp = configString.substring(start, off1).trim(); + values[count++] = Double.parseDouble(tmp); + + return buildWave(Arrays.copyOf(values, count)); + } + + private static MonotonicCurveFit buildWave(double[] values) { + int length = values.length * 3 - 2; + int len = values.length - 1; + double gap = 1.0 / len; + double[][] points = new double[length][1]; + double[] time = new double[length]; + for (int i = 0; i < values.length; i++) { + double v = values[i]; + points[i + len][0] = v; + time[i + len] = i * gap; + if (i > 0) { + points[i + len * 2][0] = v + 1; + time[i + len * 2] = i * gap + 1; + + points[i - 1][0] = v - 1 - gap; + time[i - 1] = i * gap + -1 - gap; + } + } + + return new MonotonicCurveFit(time, points); + } +} diff --git a/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/StepCurve.java b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/StepCurve.java new file mode 100644 index 000000000000..6ed6548405d2 --- /dev/null +++ b/core/java/com/android/internal/widget/remotecompose/core/operations/utilities/easing/StepCurve.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.internal.widget.remotecompose.core.operations.utilities.easing; + + +/** + * This class translates a series of floating point values into a continuous + * curve for use in an easing function including quantize functions + * it is used with the "spline(0,0.3,0.3,0.5,...0.9,1)" it should start at 0 and end at one 1 + */ +public class StepCurve extends Easing { + private static final boolean DEBUG = false; + MonotonicCurveFit mCurveFit; + + public StepCurve(float[] params, int offset, int len) { + mCurveFit = genSpline(params, offset, len); + } + + private static MonotonicCurveFit genSpline(float[] values, int off, int arrayLen) { + int length = arrayLen * 3 - 2; + int len = arrayLen - 1; + double gap = 1.0 / len; + double[][] points = new double[length][1]; + double[] time = new double[length]; + for (int i = 0; i < arrayLen; i++) { + double v = values[i + off]; + points[i + len][0] = v; + time[i + len] = i * gap; + if (i > 0) { + points[i + len * 2][0] = v + 1; + time[i + len * 2] = i * gap + 1; + + points[i - 1][0] = v - 1 - gap; + time[i - 1] = i * gap + -1 - gap; + } + } + + MonotonicCurveFit ms = new MonotonicCurveFit(time, points); + + return ms; + } + + @Override + public float getDiff(float x) { + return (float) mCurveFit.getSlope(x, 0); + } + + + @Override + public float get(float x) { + return (float) mCurveFit.getPos(x, 0); + } +} diff --git a/core/java/com/android/internal/widget/remotecompose/player/RemoteComposeDocument.java b/core/java/com/android/internal/widget/remotecompose/player/RemoteComposeDocument.java index bcda27a40f64..d1c4d46f2c22 100644 --- a/core/java/com/android/internal/widget/remotecompose/player/RemoteComposeDocument.java +++ b/core/java/com/android/internal/widget/remotecompose/player/RemoteComposeDocument.java @@ -79,6 +79,15 @@ public class RemoteComposeDocument { } /** + * The delay in milliseconds to next repaint -1 = not needed 0 = asap + * + * @return delay in milliseconds to next repaint or -1 + */ + public int needsRepaint() { + return mDocument.needsRepaint(); + } + + /** * Returns true if the document can be displayed given this version of the player * * @param majorVersion the max major version supported by the player @@ -89,5 +98,10 @@ public class RemoteComposeDocument { return mDocument.canBeDisplayed(majorVersion, minorVersion, capabilities); } + @Override + public String toString() { + return "Document{\n" + + mDocument + '}'; + } } diff --git a/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidPaintContext.java b/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidPaintContext.java index d0d6e6982a16..ecb68bb21fb3 100644 --- a/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidPaintContext.java +++ b/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidPaintContext.java @@ -26,6 +26,7 @@ import android.graphics.PorterDuffColorFilter; import android.graphics.RadialGradient; import android.graphics.Rect; import android.graphics.RectF; +import android.graphics.RuntimeShader; import android.graphics.Shader; import android.graphics.SweepGradient; import android.graphics.Typeface; @@ -33,6 +34,8 @@ import android.graphics.Typeface; import com.android.internal.widget.remotecompose.core.PaintContext; import com.android.internal.widget.remotecompose.core.RemoteContext; import com.android.internal.widget.remotecompose.core.operations.ClipPath; +import com.android.internal.widget.remotecompose.core.operations.ShaderData; +import com.android.internal.widget.remotecompose.core.operations.Utils; import com.android.internal.widget.remotecompose.core.operations.paint.PaintBundle; import com.android.internal.widget.remotecompose.core.operations.paint.PaintChanges; @@ -43,6 +46,7 @@ import com.android.internal.widget.remotecompose.core.operations.paint.PaintChan public class AndroidPaintContext extends PaintContext { Paint mPaint = new Paint(); Canvas mCanvas; + Rect mTmpRect = new Rect(); // use in calculation of bounds public AndroidPaintContext(RemoteContext context, Canvas canvas) { super(context); @@ -177,6 +181,22 @@ public class AndroidPaintContext extends PaintContext { } @Override + public void getTextBounds(int textId, int start, int end, boolean monospace, float[] bounds) { + String str = getText(textId); + if (end == -1) { + end = str.length(); + } + + mPaint.getTextBounds(str, start, end, mTmpRect); + + bounds[0] = mTmpRect.left; + bounds[1] = mTmpRect.top; + bounds[2] = monospace ? (mPaint.measureText(str, start, end) - mTmpRect.left) + : mTmpRect.right; + bounds[3] = mTmpRect.bottom; + } + + @Override public void drawTextRun(int textID, int start, int end, @@ -185,7 +205,16 @@ public class AndroidPaintContext extends PaintContext { float x, float y, boolean rtl) { - String textToPaint = getText(textID).substring(start, end); + + String textToPaint = getText(textID); + if (end == -1) { + if (start != 0) { + textToPaint = textToPaint.substring(start); + } + } else { + textToPaint = textToPaint.substring(start, end); + } + mCanvas.drawText(textToPaint, x, y, mPaint); } @@ -308,7 +337,7 @@ public class AndroidPaintContext extends PaintContext { @Override public void applyPaint(PaintBundle mPaintData) { - mPaintData.applyPaintChange(new PaintChanges() { + mPaintData.applyPaintChange((PaintContext) this, new PaintChanges() { @Override public void setTextSize(float size) { mPaint.setTextSize(size); @@ -361,10 +390,8 @@ public class AndroidPaintContext extends PaintContext { } } - } - @Override public void setStrokeWidth(float width) { mPaint.setStrokeWidth(width); @@ -386,13 +413,37 @@ public class AndroidPaintContext extends PaintContext { } @Override - public void setShader(int shader, String shaderString) { - + public void setShader(int shaderId) { + // TODO this stuff should check the shader creation + if (shaderId == 0) { + mPaint.setShader(null); + return; + } + ShaderData data = getShaderData(shaderId); + RuntimeShader shader = new RuntimeShader(getText(data.getShaderTextId())); + String[] names = data.getUniformFloatNames(); + for (int i = 0; i < names.length; i++) { + String name = names[i]; + float[] val = data.getUniformFloats(name); + shader.setFloatUniform(name, val); + } + names = data.getUniformIntegerNames(); + for (int i = 0; i < names.length; i++) { + String name = names[i]; + int[] val = data.getUniformInts(name); + shader.setIntUniform(name, val); + } + names = data.getUniformBitmapNames(); + for (int i = 0; i < names.length; i++) { + String name = names[i]; + int val = data.getUniformBitmapId(name); + } + mPaint.setShader(shader); } @Override public void setImageFilterQuality(int quality) { - System.out.println(">>>>>>>>>>>> "); + Utils.log(" quality =" + quality); } @Override @@ -420,7 +471,6 @@ public class AndroidPaintContext extends PaintContext { mPaint.setFilterBitmap(filter); } - @Override public void setAntiAlias(boolean aa) { mPaint.setAntiAlias(aa); @@ -437,7 +487,6 @@ public class AndroidPaintContext extends PaintContext { case PaintBundle.COLOR_FILTER: mPaint.setColorFilter(null); - System.out.println(">>>>>>>>>>>>> CLEAR!!!!"); break; } } @@ -446,12 +495,11 @@ public class AndroidPaintContext extends PaintContext { } } - Shader.TileMode[] mTilesModes = new Shader.TileMode[]{ + Shader.TileMode[] mTileModes = new Shader.TileMode[]{ Shader.TileMode.CLAMP, Shader.TileMode.REPEAT, Shader.TileMode.MIRROR}; - @Override public void setLinearGradient(int[] colors, float[] stops, @@ -463,7 +511,7 @@ public class AndroidPaintContext extends PaintContext { mPaint.setShader(new LinearGradient(startX, startY, endX, - endY, colors, stops, mTilesModes[tileMode])); + endY, colors, stops, mTileModes[tileMode])); } @@ -475,7 +523,7 @@ public class AndroidPaintContext extends PaintContext { float radius, int tileMode) { mPaint.setShader(new RadialGradient(centerX, centerY, radius, - colors, stops, mTilesModes[tileMode])); + colors, stops, mTileModes[tileMode])); } @Override @@ -490,7 +538,6 @@ public class AndroidPaintContext extends PaintContext { @Override public void setColorFilter(int color, int mode) { PorterDuff.Mode pmode = origamiToPorterDuffMode(mode); - System.out.println("setting color filter to " + pmode.name()); if (pmode != null) { mPaint.setColorFilter( new PorterDuffColorFilter(color, pmode)); @@ -500,10 +547,10 @@ public class AndroidPaintContext extends PaintContext { } @Override - public void mtrixScale(float scaleX, - float scaleY, - float centerX, - float centerY) { + public void matrixScale(float scaleX, + float scaleY, + float centerX, + float centerY) { if (Float.isNaN(centerX)) { mCanvas.scale(scaleX, scaleY); } else { @@ -556,6 +603,11 @@ public class AndroidPaintContext extends PaintContext { } } + @Override + public void reset() { + mPaint.reset(); + } + private Path getPath(int path1Id, int path2Id, float tween, @@ -599,5 +651,9 @@ public class AndroidPaintContext extends PaintContext { private String getText(int id) { return (String) mContext.mRemoteComposeState.getFromId(id); } + + private ShaderData getShaderData(int id) { + return (ShaderData) mContext.mRemoteComposeState.getFromId(id); + } } diff --git a/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidRemoteContext.java b/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidRemoteContext.java index 270e96f11942..6e4893bc0ee6 100644 --- a/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidRemoteContext.java +++ b/core/java/com/android/internal/widget/remotecompose/player/platform/AndroidRemoteContext.java @@ -20,10 +20,15 @@ import android.graphics.BitmapFactory; import android.graphics.Canvas; import com.android.internal.widget.remotecompose.core.RemoteContext; +import com.android.internal.widget.remotecompose.core.VariableSupport; +import com.android.internal.widget.remotecompose.core.operations.FloatExpression; +import com.android.internal.widget.remotecompose.core.operations.ShaderData; + +import java.util.HashMap; /** * An implementation of Context for Android. - * + * <p> * This is used to play the RemoteCompose operations on Android. */ class AndroidRemoteContext extends RemoteContext { @@ -33,6 +38,7 @@ class AndroidRemoteContext extends RemoteContext { mPaintContext = new AndroidPaintContext(this, canvas); } else { // need to make sure to update the canvas for the current one + mPaintContext.reset(); ((AndroidPaintContext) mPaintContext).setCanvas(canvas); } mWidth = canvas.getWidth(); @@ -50,13 +56,32 @@ class AndroidRemoteContext extends RemoteContext { } } + static class VarName { + String mName; + int mId; + int mType; + + VarName(String name, int id, int type) { + mName = name; + mId = id; + mType = type; + } + } + + HashMap<String, VarName> mVarNameHashMap = new HashMap<>(); + + @Override + public void loadVariableName(String varName, int varId, int varType) { + mVarNameHashMap.put(varName, new VarName(varName, varId, varType)); + } + /** * Decode a byte array into an image and cache it using the given imageId * - * @oaram imageId the id of the image - * @param width with of image to be loaded + * @param width with of image to be loaded * @param height height of image to be loaded * @param bitmap a byte array containing the image information + * @oaram imageId the id of the image */ @Override public void loadBitmap(int imageId, int width, int height, byte[] bitmap) { @@ -70,14 +95,66 @@ class AndroidRemoteContext extends RemoteContext { public void loadText(int id, String text) { if (!mRemoteComposeState.containsId(id)) { mRemoteComposeState.cache(id, text); + } else { + mRemoteComposeState.update(id, text); } } + @Override + public String getText(int id) { + return (String) mRemoteComposeState.getFromId(id); + } + + @Override + public void loadFloat(int id, float value) { + mRemoteComposeState.updateFloat(id, value); + } + + + @Override + public void loadColor(int id, int color) { + mRemoteComposeState.updateColor(id, color); + } + + @Override + public void loadAnimatedFloat(int id, FloatExpression animatedFloat) { + mRemoteComposeState.cache(id, animatedFloat); + } + + @Override + public void loadShader(int id, ShaderData value) { + mRemoteComposeState.cache(id, value); + } + + @Override + public float getFloat(int id) { + return (float) mRemoteComposeState.getFloat(id); + } + + @Override + public int getColor(int id) { + return mRemoteComposeState.getColor(id); + } + + @Override + public void listensTo(int id, VariableSupport variableSupport) { + mRemoteComposeState.listenToVar(id, variableSupport); + } + + @Override + public int updateOps() { + return mRemoteComposeState.getOpsToUpdate(this); + } + + @Override + public ShaderData getShader(int id) { + return (ShaderData) mRemoteComposeState.getFromId(id); + } + /////////////////////////////////////////////////////////////////////////////////////////////// // Click handling /////////////////////////////////////////////////////////////////////////////////////////////// - @Override public void addClickArea(int id, int contentDescriptionId, @@ -87,7 +164,7 @@ class AndroidRemoteContext extends RemoteContext { float bottom, int metadataId) { String contentDescription = (String) mRemoteComposeState.getFromId(contentDescriptionId); - String metadata = (String) mRemoteComposeState.getFromId(metadataId); + String metadata = (String) mRemoteComposeState.getFromId(metadataId); mDocument.addClickArea(id, contentDescription, left, top, right, bottom, metadata); } } diff --git a/core/java/com/android/internal/widget/remotecompose/player/platform/ClickAreaView.java b/core/java/com/android/internal/widget/remotecompose/player/platform/ClickAreaView.java index 672dae32d8e0..329178abe8b5 100644 --- a/core/java/com/android/internal/widget/remotecompose/player/platform/ClickAreaView.java +++ b/core/java/com/android/internal/widget/remotecompose/player/platform/ClickAreaView.java @@ -20,7 +20,6 @@ import android.graphics.Canvas; import android.graphics.Paint; import android.view.View; - /** * Implementation for the click handling */ @@ -40,7 +39,6 @@ class ClickAreaView extends View { setContentDescription(contentDescription); } - public void setDebug(boolean value) { if (mDebug != value) { mDebug = value; diff --git a/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java b/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java index a3bb73e22c59..97d23c84b5bd 100644 --- a/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java +++ b/core/java/com/android/internal/widget/remotecompose/player/platform/RemoteComposeCanvas.java @@ -85,6 +85,7 @@ public class RemoteComposeCanvas extends FrameLayout implements View.OnAttachSta mDocument.initializeContext(mARContext); setContentDescription(mDocument.getDocument().getContentDescription()); requestLayout(); + invalidate(); } AndroidRemoteContext mARContext = new AndroidRemoteContext(); @@ -119,8 +120,7 @@ public class RemoteComposeCanvas extends FrameLayout implements View.OnAttachSta removeAllViews(); } - - public interface ClickCallbacks { + public interface ClickCallbacks { void click(int id, String metadata); } @@ -213,6 +213,9 @@ public class RemoteComposeCanvas extends FrameLayout implements View.OnAttachSta setMeasuredDimension(w, h); } + private int mCount; + private long mTime = System.nanoTime(); + @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); @@ -224,6 +227,17 @@ public class RemoteComposeCanvas extends FrameLayout implements View.OnAttachSta mARContext.mWidth = getWidth(); mARContext.mHeight = getHeight(); mDocument.paint(mARContext, mTheme); + if (mDebug) { + mCount++; + if (System.nanoTime() - mTime > 1000000000L) { + System.out.println(" count " + mCount + " fps"); + mCount = 0; + mTime = System.nanoTime(); + } + } + if (mDocument.needsRepaint() > 0) { + invalidate(); + } } } diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp index e831a7d229d6..536583840d8f 100644 --- a/core/jni/android_view_SurfaceControl.cpp +++ b/core/jni/android_view_SurfaceControl.cpp @@ -358,7 +358,8 @@ public: jobject stats = env->NewObject(gTransactionStatsClassInfo.clazz, gTransactionStatsClassInfo.ctor, - latchTime, presentFence.get()); + latchTime, + static_cast<jlong>(reinterpret_cast<uintptr_t>(presentFence.get()))); env->CallVoidMethod(mTransactionCompletedListenerObject, gConsumerClassInfo.accept, stats); env->DeleteLocalRef(stats); DieIfException(env, "Uncaught exception in TransactionCompletedListener."); diff --git a/core/proto/android/providers/settings/system.proto b/core/proto/android/providers/settings/system.proto index 123306924c2b..6a0ec1dcb3f2 100644 --- a/core/proto/android/providers/settings/system.proto +++ b/core/proto/android/providers/settings/system.proto @@ -126,6 +126,7 @@ message SystemSettingsProto { option (android.msg_privacy).dest = DEST_EXPLICIT; optional SettingProto pointer_fill_style = 1 [ (android.privacy).dest = DEST_AUTOMATIC ]; + optional SettingProto pointer_scale = 3 [ (android.privacy).dest = DEST_AUTOMATIC ]; } optional Pointer pointer = 37; optional SettingProto pointer_speed = 18 [ (android.privacy).dest = DEST_AUTOMATIC ]; diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 09ffdf3d4e4c..c71f9bde6bf1 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -3249,16 +3249,20 @@ <permission android:name="android.permission.INTERACT_ACROSS_PROFILES" android:protectionLevel="signature|appop" /> - <!-- Allows applications to access profiles with ACCESS_HIDDEN_PROFILES user property - <p>Protection level: normal - @FlaggedApi("android.multiuser.enable_permission_to_access_hidden_profiles") --> + <!-- Allows applications to access profiles with + {@code android.content.pm.UserProperties#PROFILE_API_VISIBILITY_HIDDEN} user property, e.g. + {@link android.os.UserManager#USER_TYPE_PROFILE_PRIVATE}. + <p>Protection level: normal + @FlaggedApi("android.multiuser.enable_permission_to_access_hidden_profiles") --> <permission android:name="android.permission.ACCESS_HIDDEN_PROFILES" android:label="@string/permlab_accessHiddenProfile" android:description="@string/permdesc_accessHiddenProfile" android:protectionLevel="normal" /> - <!-- @SystemApi @hide Allows privileged applications to get details about hidden profile - users. + <!-- @SystemApi @hide Allows privileged applications to get details about profiles with + {@code android.content.pm.UserProperties#PROFILE_API_VISIBILITY_HIDDEN} user property, e.g. + {@link android.os.UserManager#USER_TYPE_PROFILE_PRIVATE}. Removes extra requirements such + as having {@link android.app.role.RoleManager#ROLE_HOME} role for LauncherApps APIs. @FlaggedApi("android.multiuser.enable_permission_to_access_hidden_profiles") --> <permission android:name="android.permission.ACCESS_HIDDEN_PROFILES_FULL" @@ -3322,13 +3326,18 @@ <!-- Allows an application to manage device policy relating to time. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call - APIs protected by this permission on users different to the calling user.--> + APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. + --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_TIME" android:protectionLevel="internal|role" /> <!-- Allows an application to set the grant state of runtime permissions on packages. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS" android:protectionLevel="internal|role" /> @@ -3336,6 +3345,8 @@ <!-- Allows an application to manage the identity of the managing organization. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY" android:protectionLevel="internal|role" /> @@ -3344,6 +3355,8 @@ active policy. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_SUPPORT_MESSAGE" android:protectionLevel="internal|role" /> @@ -3351,6 +3364,8 @@ <!-- Allows an application to manage backup service policy. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_BACKUP_SERVICE" android:protectionLevel="internal|role" /> @@ -3358,6 +3373,8 @@ <!-- Allows an application to manage lock task policy. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_LOCK_TASK" android:protectionLevel="internal|role" /> @@ -3365,6 +3382,8 @@ <!-- Allows an application to manage policy regarding modifying applications. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_APPS_CONTROL" android:protectionLevel="internal|role" /> @@ -3372,6 +3391,8 @@ <!-- Allows an application to manage installing from unknown sources policy. <p>MANAGE_SECURITY_CRITICAL_DEVICE_POLICY_ACROSS_USERS is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_INSTALL_UNKNOWN_SOURCES" android:protectionLevel="internal|role" /> @@ -3379,6 +3400,8 @@ <!-- Allows an application to manage application restrictions. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_APP_RESTRICTIONS" android:protectionLevel="internal|role" /> @@ -3386,6 +3409,8 @@ <!-- Allows an application to manage calling policy. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_CALLS" android:protectionLevel="internal|role" /> @@ -3393,6 +3418,8 @@ <!-- Allows an application to manage debugging features policy. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_DEBUGGING_FEATURES" android:protectionLevel="internal|role" /> @@ -3400,6 +3427,8 @@ <!-- Allows an application to manage policy preventing users from modifying users. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_MODIFY_USERS" android:protectionLevel="internal|role" /> @@ -3407,6 +3436,8 @@ <!-- Allows an application to manage safe boot policy. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_SAFE_BOOT" android:protectionLevel="internal|role" /> @@ -3415,6 +3446,8 @@ enable and disable the microphone. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_MICROPHONE" android:protectionLevel="internal|role" /> @@ -3423,6 +3456,8 @@ enable and disable the camera. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_CAMERA" android:protectionLevel="internal|role" /> @@ -3430,6 +3465,8 @@ <!-- Allows an application to manage policy related to keyguard. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_KEYGUARD" android:protectionLevel="internal|role" /> @@ -3437,6 +3474,8 @@ <!-- Allows an application to set policy related to account management. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_ACCOUNT_MANAGEMENT" android:protectionLevel="internal|role" /> @@ -3444,6 +3483,8 @@ <!-- Allows an application to set policy related to hiding and suspending packages. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_PACKAGE_STATE" android:protectionLevel="internal|role" /> @@ -3452,17 +3493,24 @@ challenge on current user. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_RESET_PASSWORD" android:protectionLevel="internal|role" /> - <!-- Allows an application to set policy related to the status bar.--> + <!-- Allows an application to set policy related to the status bar. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. + --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_STATUS_BAR" android:protectionLevel="internal|role" /> <!-- Allows an application to set policy related to bluetooth. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_BLUETOOTH" android:protectionLevel="internal|role" /> @@ -3470,6 +3518,8 @@ <!-- Allows an application to set policy related to fun. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_FUN" android:protectionLevel="internal|role" /> @@ -3477,6 +3527,8 @@ <!-- Allows an application to set policy related to airplane mode. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_AIRPLANE_MODE" android:protectionLevel="internal|role" /> @@ -3484,6 +3536,8 @@ <!-- Allows an application to set policy related to mobile networks. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_MOBILE_NETWORK" android:protectionLevel="internal|role" /> @@ -3491,6 +3545,8 @@ <!-- Allows an application to set policy related to physical media. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_PHYSICAL_MEDIA" android:protectionLevel="internal|role" /> @@ -3498,6 +3554,8 @@ <!-- Allows an application to set policy related to sms. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_SMS" android:protectionLevel="internal|role" /> @@ -3505,6 +3563,8 @@ <!-- Allows an application to set policy related to usb file transfers. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_USB_FILE_TRANSFER" android:protectionLevel="internal|role" /> @@ -3512,6 +3572,8 @@ <!-- Allows an application to set policy related to lock credentials. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS" android:protectionLevel="internal|role" /> @@ -3519,6 +3581,8 @@ <!-- Allows an application to set policy related to Wifi. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_WIFI" android:protectionLevel="internal|role" /> @@ -3526,6 +3590,8 @@ <!-- Allows an application to set policy related to screen capture. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_SCREEN_CAPTURE" android:protectionLevel="internal|role" /> @@ -3533,6 +3599,8 @@ <!-- Allows an application to set policy related to input methods. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_INPUT_METHODS" android:protectionLevel="internal|role" /> @@ -3541,6 +3609,8 @@ private DNS. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_RESTRICT_PRIVATE_DNS" android:protectionLevel="internal|role" /> @@ -3548,6 +3618,8 @@ <!-- Allows an application to set policy related to the default sms application. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_DEFAULT_SMS" android:protectionLevel="internal|role" /> @@ -3555,6 +3627,8 @@ <!-- Allows an application to set policy related to profiles. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_PROFILES" android:protectionLevel="internal|role" /> @@ -3563,6 +3637,8 @@ cross-profile copy and paste). <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_PROFILE_INTERACTION" android:protectionLevel="internal|role" /> @@ -3570,6 +3646,8 @@ <!-- Allows an application to set policy related to VPNs. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_VPN" android:protectionLevel="internal|role" /> @@ -3577,6 +3655,8 @@ <!-- Allows an application to set policy related to audio output. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_AUDIO_OUTPUT" android:protectionLevel="internal|role" /> @@ -3584,6 +3664,8 @@ <!-- Allows an application to set policy related to the display. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_DISPLAY" android:protectionLevel="internal|role" /> @@ -3591,6 +3673,8 @@ <!-- Allows an application to set policy related to location. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_LOCATION" android:protectionLevel="internal|role" /> @@ -3598,6 +3682,8 @@ <!-- Allows an application to set policy related to factory reset. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_FACTORY_RESET" android:protectionLevel="internal|role" /> @@ -3605,6 +3691,8 @@ <!-- Allows an application to set policy related to the wallpaper. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_WALLPAPER" android:protectionLevel="internal|role" /> @@ -3612,6 +3700,8 @@ <!-- Allows an application to set policy related to the usage of the contents of the screen. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_SCREEN_CONTENT" android:protectionLevel="internal|role" /> @@ -3619,6 +3709,8 @@ <!-- Allows an application to set policy related to system dialogs. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_SYSTEM_DIALOGS" android:protectionLevel="internal|role" /> @@ -3626,6 +3718,8 @@ <!-- Allows an application to set policy related to users running in the background. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_RUN_IN_BACKGROUND" android:protectionLevel="internal|role" /> @@ -3633,6 +3727,8 @@ <!-- Allows an application to set policy related to printing. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_PRINTING" android:protectionLevel="internal|role" /> @@ -3641,12 +3737,16 @@ nearby streaming). <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_NEARBY_COMMUNICATION" android:protectionLevel="internal|role" /> <!-- Allows an application to set policy related to <a href="https://www.threadgroup.org">Thread</a> network. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. @FlaggedApi("com.android.net.thread.platform.flags.thread_user_restriction_enabled") --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_THREAD_NETWORK" @@ -3654,6 +3754,8 @@ <!-- Allows an application to set policy related to sending assist content to a privileged app such as the Assistant app. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. @FlaggedApi("android.app.admin.flags.assist_content_user_restriction_enabled") --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_ASSIST_CONTENT" @@ -3662,6 +3764,8 @@ <!-- Allows an application to set policy related to windows. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_WINDOWS" android:protectionLevel="internal|role" /> @@ -3669,6 +3773,8 @@ <!-- Allows an application to set policy related to locale. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_LOCALE" android:protectionLevel="internal|role" /> @@ -3676,6 +3782,8 @@ <!-- Allows an application to set policy related to autofill. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_AUTOFILL" android:protectionLevel="internal|role" /> @@ -3683,6 +3791,8 @@ <!-- Allows an application to set policy related to users. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_USERS" android:protectionLevel="internal|role" /> @@ -3690,6 +3800,8 @@ <!-- Allows an application to set policy related to certificates. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_CERTIFICATES" android:protectionLevel="internal|role" /> @@ -3697,6 +3809,8 @@ <!-- Allows an application to set policy related to override APNs. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_OVERRIDE_APN" android:protectionLevel="internal|role" /> @@ -3704,6 +3818,8 @@ <!-- Allows an application to set policy related to security logging. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_SECURITY_LOGGING" android:protectionLevel="internal|role" /> @@ -3719,6 +3835,8 @@ <!-- Allows an application to set policy related to system updates. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_SYSTEM_UPDATES" android:protectionLevel="internal|role" /> @@ -3726,6 +3844,8 @@ <!-- Allows an application query system updates. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_QUERY_SYSTEM_UPDATES" android:protectionLevel="internal|role" /> @@ -3733,6 +3853,8 @@ <!-- Allows an application to set policy related to private DNS. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_PRIVATE_DNS" android:protectionLevel="internal|role" /> @@ -3740,6 +3862,8 @@ <!-- Allows an application to set policy related to settings. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_SETTINGS" android:protectionLevel="internal|role" /> @@ -3747,17 +3871,24 @@ <!-- Allows an application to set policy related to network logging. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_NETWORK_LOGGING" android:protectionLevel="internal|role" /> - <!-- Allows an application to set policy related to usb data signalling.--> + <!-- Allows an application to set policy related to usb data signalling. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. + --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_USB_DATA_SIGNALLING" android:protectionLevel="internal|role" /> <!-- Allows an application to set policy related to suspending personal apps. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_SUSPEND_PERSONAL_APPS" android:protectionLevel="internal|role" /> @@ -3765,13 +3896,17 @@ <!-- Allows an application to set policy related to keeping uninstalled packages. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_KEEP_UNINSTALLED_PACKAGES" android:protectionLevel="internal|role" /> <!-- Allows an application to manage policy related to accessibility. - <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call - APIs protected by this permission on users different to the calling user. + <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to + call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_ACCESSIBILITY" android:protectionLevel="internal|role" /> @@ -3779,6 +3914,8 @@ <!-- Allows an application to manage policy related to common criteria mode. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_COMMON_CRITERIA_MODE" android:protectionLevel="internal|role" /> @@ -3786,6 +3923,8 @@ <!-- Allows an application to manage policy related to metered data. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_METERED_DATA" android:protectionLevel="internal|role" /> @@ -3793,6 +3932,8 @@ <!-- Allows an application to set a network-independent global HTTP proxy. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_PROXY" android:protectionLevel="internal|role" /> @@ -3800,6 +3941,8 @@ <!-- Allows an application to request bugreports with user consent. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_BUGREPORT" android:protectionLevel="internal|role" /> @@ -3807,6 +3950,8 @@ <!-- Allows an application to manage policy related to application user data. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_APP_USER_DATA" android:protectionLevel="internal|role" /> @@ -3815,6 +3960,8 @@ permission. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_LOCK" android:protectionLevel="internal|role" /> @@ -3830,6 +3977,8 @@ <!-- Allows an application to manage policy related to system apps. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_SYSTEM_APPS" android:protectionLevel="internal|role" /> @@ -3837,16 +3986,23 @@ <!-- Allows an application to manage policy related to wiping data. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS} is required to call APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_WIPE_DATA" android:protectionLevel="internal|role" /> <!-- Allows an application to manage policy related to the Memory Tagging Extension (MTE). + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_MTE" android:protectionLevel="internal|role" /> - <!-- Allows an application to manage policy related to device identifiers. --> + <!-- Allows an application to manage policy related to device identifiers. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. + --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_DEVICE_IDENTIFIERS" android:protectionLevel="internal|role" /> @@ -3859,24 +4015,33 @@ <!-- Allows an application to set policy related to subscriptions downloaded by an admin. <p>{@link Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL} is required to call - APIs protected by this permission on users different to the calling user. - @FlaggedApi("android.app.admin.flags.esim_management_enabled") --> + APIs protected by this permission on users different to the calling user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. + @FlaggedApi("android.app.admin.flags.esim_management_enabled") + --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_MANAGED_SUBSCRIPTIONS" android:protectionLevel="internal|role" /> <!-- Allows an application to manage policy related to block package uninstallation. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. @FlaggedApi("android.app.admin.flags.dedicated_device_control_api_enabled") --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_BLOCK_UNINSTALL" android:protectionLevel="internal|role" /> <!-- Allows an application to manage policy related to camera toggle. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. @FlaggedApi("android.app.admin.flags.dedicated_device_control_api_enabled") --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_CAMERA_TOGGLE" android:protectionLevel="internal|role" /> <!-- Allows an application to manage policy related to microphone toggle. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. @FlaggedApi("android.app.admin.flags.dedicated_device_control_api_enabled") --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_MICROPHONE_TOGGLE" @@ -3885,16 +4050,21 @@ <!-- Allows an application to set device policies outside the current user that are critical for securing data within the current user. <p>Holding this permission allows the use of other held MANAGE_DEVICE_POLICY_* - permissions across all users on the device provided they are required for securing data - within the current user.--> + permissions across all users on the device provided they are required for securing data + within the current user. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. + --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL" android:protectionLevel="internal|role" /> <!-- Allows an application to set device policies outside the current user that are required for securing device ownership without accessing user data. <p>Holding this permission allows the use of other held MANAGE_DEVICE_POLICY_* - permissions across all users on the device provided they do not grant access to user - data. --> + permissions across all users on the device provided they do not grant access to user data. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. + --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_ACROSS_USERS" android:protectionLevel="internal|role" /> @@ -3902,7 +4072,10 @@ <p>Fuller form of {@link android.Manifest.permission#MANAGE_DEVICE_POLICY_ACROSS_USERS} that removes the restriction on accessing user data. <p>Holding this permission allows the use of any other held MANAGE_DEVICE_POLICY_* - permissions across all users on the device.--> + permissions across all users on the device. + <p>Protection level: internal|role + <p>Intended for use by the DEVICE_POLICY_MANAGEMENT role only. + --> <permission android:name="android.permission.MANAGE_DEVICE_POLICY_ACROSS_USERS_FULL" android:protectionLevel="internal|role" /> @@ -8194,6 +8367,17 @@ <permission android:name="android.permission.SETUP_FSVERITY" android:protectionLevel="signature|privileged"/> + <!-- + @TestApi + Signature permission reserved for testing. This should never be used to + gate any actual functionality. + <p> + Protection level: signature + @hide + --> + <permission android:name="android.permission.RESERVED_FOR_TESTING_SIGNATURE" + android:protectionLevel="signature"/> + <!-- Attribution for Geofencing service. --> <attribution android:tag="GeofencingService" android:label="@string/geofencing_service"/> <!-- Attribution for Country Detector. --> diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml index 704d44278a78..18425fe0163c 100644 --- a/core/res/res/values-af/strings.xml +++ b/core/res/res/values-af/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privaat ruimte"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Kloon"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Gemeenskaplik"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Privaat ruimte"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Sensitiewe kennisgewinginhoud is versteek"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Appinhoud is weens sekuriteit van skermdeling verberg"</string> diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml index d1e18dacd5b4..965160e4b3a2 100644 --- a/core/res/res/values-am/strings.xml +++ b/core/res/res/values-am/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"የግል ቦታ"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"አባዛ"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"የጋራ"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"የግል ቦታ"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"አደገኛ የማሳወቂያ ይዘት ተደብቋል"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"ለደኅንነት ሲባል የመተግበሪያ ይዘት ከማያ ገጽ ማጋራት ተደብቋል"</string> diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml index 5ef18af8129a..3e396e0ce86f 100644 --- a/core/res/res/values-ar/strings.xml +++ b/core/res/res/values-ar/strings.xml @@ -2411,8 +2411,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"المساحة الخاصة"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"نسخة طبق الأصل"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"ملف شخصي مشترك"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"المساحة الخاصّة"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"تم إخفاء المحتوى الحساس في الإشعار"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"تم إخفاء محتوى التطبيق بعد تفعيل ميزة \"مشاركة الشاشة\" للحفاظ على أمانك"</string> diff --git a/core/res/res/values-as/strings.xml b/core/res/res/values-as/strings.xml index 5443f12881b4..5f9f16a61945 100644 --- a/core/res/res/values-as/strings.xml +++ b/core/res/res/values-as/strings.xml @@ -1865,7 +1865,7 @@ <string name="mediasize_japanese_you4" msgid="5552111912684384833">"You4"</string> <string name="mediasize_japanese_l" msgid="1326765321473431817">"L"</string> <string name="mediasize_unknown_portrait" msgid="3817016220446495613">"অজ্ঞাত প\'ৰ্ট্ৰেইট"</string> - <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"অজ্ঞাত লেণ্ডস্কেইপ"</string> + <string name="mediasize_unknown_landscape" msgid="1584741567225095325">"অজ্ঞাত লেণ্ডস্কে’প"</string> <string name="write_fail_reason_cancelled" msgid="2344081488493969190">"বাতিল কৰা হ’ল"</string> <string name="write_fail_reason_cannot_write" msgid="432118118378451508">"সমল লিখাত আসোঁৱাহ"</string> <string name="reason_unknown" msgid="5599739807581133337">"অজ্ঞাত"</string> @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"প্ৰাইভেট স্পে’চ"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"ক্ল’ন"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"সম্প্ৰদায়ৰ সৈতে জড়িত"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"প্ৰাইভেট স্পে’চ"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"সংবেদনশীল জাননী লুকুওৱা হৈছে"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"সুৰক্ষাৰ বাবে এপৰ সমল স্ক্ৰীণ শ্বেয়াৰ কৰাৰ পৰা লুকুৱাই ৰখা হৈছে"</string> diff --git a/core/res/res/values-az/strings.xml b/core/res/res/values-az/strings.xml index b9ec7428a4e6..c4327e79a45f 100644 --- a/core/res/res/values-az/strings.xml +++ b/core/res/res/values-az/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Məxfi sahə"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Kommunal"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Şəxsi sahə"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Həssas bildiriş kontenti gizlədildi"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Güvənlik üçün tətbiq kontenti ekran paylaşımından gizlədildi"</string> diff --git a/core/res/res/values-b+sr+Latn/strings.xml b/core/res/res/values-b+sr+Latn/strings.xml index 858075ba4893..2b81e1cdce7c 100644 --- a/core/res/res/values-b+sr+Latn/strings.xml +++ b/core/res/res/values-b+sr+Latn/strings.xml @@ -2408,8 +2408,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privatan prostor"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klonirano"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Zajedničko"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Privatan prostor"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Osetljiv sadržaj obaveštenja je skriven"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Sadržaj aplikacije je skriven za deljenje sadržaja ekrana zbog bezbednosti"</string> diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml index 2739cb212353..7d1f6bee5816 100644 --- a/core/res/res/values-be/strings.xml +++ b/core/res/res/values-be/strings.xml @@ -2409,8 +2409,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Прыватная прастора"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Клон"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Супольны"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Прыватная прастора"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Канфідэнцыяльнае змесціва ў апавяшчэннях схавана"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Змесціва праграмы выключана з абагульвання экрана ў мэтах бяспекі"</string> diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml index 0d4cadcae10b..a2f03646bb66 100644 --- a/core/res/res/values-bg/strings.xml +++ b/core/res/res/values-bg/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Частно пространство"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Клониране"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Общи"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Частно пространство"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Деликатното съдържание в известието е скрито"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Съдържанието на приложението е скрито от функцията за споделяне на екрана от съображения за сигурност"</string> diff --git a/core/res/res/values-bn/strings.xml b/core/res/res/values-bn/strings.xml index 03fba4b39d94..e21b5c05965f 100644 --- a/core/res/res/values-bn/strings.xml +++ b/core/res/res/values-bn/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"প্রাইভেট স্পেস"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"ক্লোন"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"কমিউনাল"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"প্রাইভেট স্পেস"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"সংবেদনশীল বিজ্ঞপ্তির কন্টেন্ট লুকানো আছে"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"নিরাপত্তার জন্য স্ক্রিন শেয়ার করা থেকে লুকানো অ্যাপের কন্টেন্ট"</string> diff --git a/core/res/res/values-bs/strings.xml b/core/res/res/values-bs/strings.xml index 6ce10c0c8907..2f285f13f4e5 100644 --- a/core/res/res/values-bs/strings.xml +++ b/core/res/res/values-bs/strings.xml @@ -2408,8 +2408,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privatni prostor"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Opće"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Privatni prostor"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Sakriven je osjetljiv sadržaj obavještenja"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Sadržaj aplikacije je sakriven od dijeljenja ekrana radi sigurnosti"</string> diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml index 4b1af3444446..48ba090c65c3 100644 --- a/core/res/res/values-ca/strings.xml +++ b/core/res/res/values-ca/strings.xml @@ -2408,8 +2408,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Espai privat"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clon"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Comunitari"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Espai privat"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"S\'ha amagat contingut sensible de les notificacions"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Contingut de l\'aplicació amagat de la compartició de pantalla per motius de seguretat"</string> diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml index cc4bd3cfeb9a..64ccaf540411 100644 --- a/core/res/res/values-cs/strings.xml +++ b/core/res/res/values-cs/strings.xml @@ -2409,8 +2409,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Soukromý prostor"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Komunální"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Soukromý prostor"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Obsah citlivých oznámení je skrytý"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Obsah aplikace je z bezpečnostních důvodů při sdílení obrazovky skryt"</string> diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml index 665ea1750b14..a499afc01987 100644 --- a/core/res/res/values-da/strings.xml +++ b/core/res/res/values-da/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privat område"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Fælles"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Privat område"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Følsomt indhold i notifikationen er skjult"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Af sikkerhedsmæssige årsager vises appindhold ikke ved skærmdeling"</string> diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml index cd637e12b70a..464a5373bcf1 100644 --- a/core/res/res/values-de/strings.xml +++ b/core/res/res/values-de/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Vertrauliches Profil"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Gemeinsam genutzt"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Vertrauliches Profil"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Vertrauliche Benachrichtigungsinhalte ausgeblendet"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"App-Inhalte werden aus Sicherheitsgründen bei der Bildschirmfreigabe ausgeblendet"</string> diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml index 09ecc2c5f22f..98ce03c16c69 100644 --- a/core/res/res/values-el/strings.xml +++ b/core/res/res/values-el/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Ιδιωτικός χώρος"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Κλώνος"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Κοινόχρηστο"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Ιδιωτικός χώρος"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Έγινε απόκρυψη της ειδοποίησης ευαίσθητου περιεχομένου"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Για λόγους ασφάλειας, έγινε απόκρυψη του περιεχομένου της εφαρμογής από την κοινή χρήση οθόνης"</string> diff --git a/core/res/res/values-en-rAU/strings.xml b/core/res/res/values-en-rAU/strings.xml index 20e391d173eb..1d2fc4d5e89c 100644 --- a/core/res/res/values-en-rAU/strings.xml +++ b/core/res/res/values-en-rAU/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Private space"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Communal"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Private space"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Sensitive notification content hidden"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"App content hidden from screen share for security"</string> diff --git a/core/res/res/values-en-rCA/strings.xml b/core/res/res/values-en-rCA/strings.xml index 7f33f6a83c21..c03bb3cb53e3 100644 --- a/core/res/res/values-en-rCA/strings.xml +++ b/core/res/res/values-en-rCA/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Private space"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Communal"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Private space"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Sensitive notification content hidden"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"App content hidden from screen share for security"</string> diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml index b7fedbe45d78..436d7aeafea4 100644 --- a/core/res/res/values-en-rGB/strings.xml +++ b/core/res/res/values-en-rGB/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Private space"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Communal"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Private space"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Sensitive notification content hidden"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"App content hidden from screen share for security"</string> diff --git a/core/res/res/values-en-rIN/strings.xml b/core/res/res/values-en-rIN/strings.xml index b83a7cfb877b..d34ed3f6e01e 100644 --- a/core/res/res/values-en-rIN/strings.xml +++ b/core/res/res/values-en-rIN/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Private space"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Communal"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Private space"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Sensitive notification content hidden"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"App content hidden from screen share for security"</string> diff --git a/core/res/res/values-en-rXC/strings.xml b/core/res/res/values-en-rXC/strings.xml index d358e5e37481..c2c107c65b5f 100644 --- a/core/res/res/values-en-rXC/strings.xml +++ b/core/res/res/values-en-rXC/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Private space"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Communal"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Private space"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Sensitive notification content hidden"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"App content hidden from screen share for security"</string> diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml index 45ea7ee341ae..df7deacdf10d 100644 --- a/core/res/res/values-es-rUS/strings.xml +++ b/core/res/res/values-es-rUS/strings.xml @@ -2408,8 +2408,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Espacio privado"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clon"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Compartido"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Espacio privado"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Se ocultó contenido sensible de la notificación"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Se ocultó el contenido de la app durante el uso compartido de la pantalla por motivos de seguridad"</string> diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml index 19c5f842acc7..e22430ff072d 100644 --- a/core/res/res/values-es/strings.xml +++ b/core/res/res/values-es/strings.xml @@ -717,7 +717,7 @@ <string name="face_acquired_too_right" msgid="6245286514593540859">"Mueve el teléfono hacia la izquierda"</string> <string name="face_acquired_too_left" msgid="9201762240918405486">"Mueve el teléfono hacia la derecha"</string> <string name="face_acquired_poor_gaze" msgid="4427153558773628020">"Mira de forma más directa al dispositivo."</string> - <string name="face_acquired_not_detected" msgid="1057966913397548150">"No se puede detectar tu cara. Sujeta el teléfono a la altura de los ojos."</string> + <string name="face_acquired_not_detected" msgid="1057966913397548150">"No se detecta tu cara. Sujeta el teléfono a la altura de los ojos."</string> <string name="face_acquired_too_much_motion" msgid="8199691445085189528">"El teléfono se mueve demasiado. Mantenlo quieto."</string> <string name="face_acquired_recalibrate" msgid="8724013080976469746">"Vuelve a registrar tu cara."</string> <string name="face_acquired_too_different" msgid="4505278456634706967">"Cara no reconocida. Inténtalo de nuevo."</string> @@ -2408,8 +2408,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Espacio privado"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clon"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Común"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Espacio privado"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Contenido sensible de la notificación oculto"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Contenido de la aplicación oculto en pantalla compartida por motivos de seguridad"</string> diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml index 46c7341f2725..979079d05762 100644 --- a/core/res/res/values-et/strings.xml +++ b/core/res/res/values-et/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privaatne ruum"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Kloon"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Ühine"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Privaatne ruum"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Märguande delikaatne sisu peideti"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Rakenduse sisu on ekraani jagamises turvalisuse huvides peidetud"</string> diff --git a/core/res/res/values-eu/strings.xml b/core/res/res/values-eu/strings.xml index 3c47f68bad97..06855f5539ee 100644 --- a/core/res/res/values-eu/strings.xml +++ b/core/res/res/values-eu/strings.xml @@ -190,7 +190,7 @@ <string name="contentServiceTooManyDeletesNotificationDesc" msgid="4562226280528716090">"<xliff:g id="CONTENT_TYPE">%s</xliff:g> gehiegi ezabatzen saiatu zara."</string> <string name="low_memory" product="tablet" msgid="5557552311566179924">"Tabletaren memoria beteta dago. Tokia egiteko, ezabatu fitxategi batzuk."</string> <string name="low_memory" product="watch" msgid="3479447988234030194">"Erlojuaren memoria beteta dago. Tokia egiteko, ezabatu fitxategi batzuk."</string> - <string name="low_memory" product="tv" msgid="6663680413790323318">"Android TV gailuaren memoria beteta dago. Tokia egiteko, ezabatu fitxategi batzuk."</string> + <string name="low_memory" product="tv" msgid="6663680413790323318">"Android TV gailuko biltegia beteta dago. Tokia egiteko, ezabatu fitxategi batzuk."</string> <string name="low_memory" product="default" msgid="2539532364144025569">"Telefonoaren memoria beteta dago. Tokia egiteko, ezabatu fitxategi batzuk."</string> <string name="ssl_ca_cert_warning" msgid="7233573909730048571">"{count,plural, =1{Autoritate ziurtagiri-emaile bat dago instalatuta}other{Autoritate ziurtagiri-emaile bat baino gehiago daude instalatuta}}"</string> <string name="ssl_ca_cert_noti_by_unknown" msgid="4961102218216815242">"Hirugarren alderdi ezezagun baten arabera"</string> @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Eremu pribatua"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klona"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Partekatua"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Eremu pribatua"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Jakinarazpenaren kontuzko edukia ezkutatu da"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Aplikazioko edukia ezkutatu egin da pantaila partekatzeko eginbidetik, segurtasuna bermatzeko"</string> diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml index 3c50b070f9d3..b0e6e6ecd0a5 100644 --- a/core/res/res/values-fa/strings.xml +++ b/core/res/res/values-fa/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"فضای خصوصی"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"همسانهسازی"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"همگانی"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"فضای خصوصی"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"محتوای اعلان حساس پنهان شده است"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"بهدلایل امنیتی، محتوای برنامه از دید همرسانی صفحهنمایش پنهان شد"</string> diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml index 0e159f2a83a6..7e2fa9c7b14b 100644 --- a/core/res/res/values-fi/strings.xml +++ b/core/res/res/values-fi/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Yksityinen tila"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klooni"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Yhteinen"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Yksityinen tila"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Arkaluontoisen ilmoituksen sisältö piilotettu"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Sovelluksen sisältö piilotettu näytön jakamiselta turvallisuussyistä"</string> diff --git a/core/res/res/values-fr-rCA/strings.xml b/core/res/res/values-fr-rCA/strings.xml index 7fca5e67a7bd..a21aefb2169e 100644 --- a/core/res/res/values-fr-rCA/strings.xml +++ b/core/res/res/values-fr-rCA/strings.xml @@ -2408,8 +2408,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Espace privé"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Commun"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Espace privé"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Le contenu confidentiel de la notification est masqué"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Le contenu de l\'application est masqué du Partage d\'écran par mesure de sécurité"</string> diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml index e08f4269eebd..486124e310b4 100644 --- a/core/res/res/values-fr/strings.xml +++ b/core/res/res/values-fr/strings.xml @@ -2408,8 +2408,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Espace privé"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Commun"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Espace privé"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Le contenu sensible de la notification a été masqué"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Le contenu de l\'appli est masqué lors du partage d\'écran pour des raisons de sécurité"</string> diff --git a/core/res/res/values-gl/strings.xml b/core/res/res/values-gl/strings.xml index 1eec828b8ad1..9994a3e74a50 100644 --- a/core/res/res/values-gl/strings.xml +++ b/core/res/res/values-gl/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Espazo privado"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clonado"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Compartido"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Espazo privado"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Contido confidencial da notificación oculto"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Por motivos de seguranza, ocultouse o contido da aplicación para que no se mostre na pantalla compartida"</string> diff --git a/core/res/res/values-gu/strings.xml b/core/res/res/values-gu/strings.xml index 303f00125ee1..3206242c0e47 100644 --- a/core/res/res/values-gu/strings.xml +++ b/core/res/res/values-gu/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"ખાનગી સ્પેસ"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"ક્લોન"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"કૉમ્યુનલ"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"ખાનગી સ્પેસ"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"સંવેદનશીલ માહિતીવાળા નોટિફિકેશનનું કન્ટેન્ટ છુપાવ્યું"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"સુરક્ષા માટે સ્ક્રીન શેર કરતી વખતે ઍપનું કન્ટેન્ટ છુપાવેલું છે"</string> diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml index 14d2bf7086c9..f7ae13e66984 100644 --- a/core/res/res/values-hi/strings.xml +++ b/core/res/res/values-hi/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"प्राइवेट स्पेस"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"क्लोन"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"कम्यूनिटी"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"प्राइवेट स्पेस"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"संवेदनशील जानकारी वाली सूचना का कॉन्टेंट छिपा है"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"स्क्रीन शेयर करने के दौरान सुरक्षा के लिए, ऐप्लिकेशन का कॉन्टेंट छिपाया गया"</string> diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml index e69a59d969cf..f6f7e8b27ba3 100644 --- a/core/res/res/values-hr/strings.xml +++ b/core/res/res/values-hr/strings.xml @@ -2408,8 +2408,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privatni prostor"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Zajedničko"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Privatni prostor"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Skriven je osjetljiv sadržaj obavijesti"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Sadržaj aplikacije sakriven je od dijeljenja zaslona radi sigurnosti"</string> diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml index f1ff23613ab6..51626166db1b 100644 --- a/core/res/res/values-hu/strings.xml +++ b/core/res/res/values-hu/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privát terület"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klón"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Közös"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Privát terület"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Bizalmas értesítéstartalom elrejtve"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"A biztonság érdekében a képernyőmegosztástól elrejtett alkalmazástartalom"</string> diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml index 40812068752a..f2b6932b1300 100644 --- a/core/res/res/values-hy/strings.xml +++ b/core/res/res/values-hy/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Մասնավոր տարածք"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Կլոն"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Ընդհանուր"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Մասնավոր տարածք"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Ծանուցման զգայուն բովանդակությունը թաքցված է"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Անվտանգության նկատառումներից ելնելով՝ հավելվածի բովանդակությունը թաքցվել է էկրանի ցուցադրումից"</string> diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml index 866e9468ae3b..d1a20f34c6d7 100644 --- a/core/res/res/values-in/strings.xml +++ b/core/res/res/values-in/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Ruang privasi"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Umum"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Ruang privasi"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Konten notifikasi sensitif disembunyikan"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Konten aplikasi disembunyikan dari berbagi layar untuk alasan keamanan"</string> diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml index 76ba64bc9d4f..c8da94810664 100644 --- a/core/res/res/values-is/strings.xml +++ b/core/res/res/values-is/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Leynirými"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Afrit"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Sameiginlegt"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Leynirými"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Viðkvæmt tilkynningaefni falið"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Efni forrits falið í skjádeilingu af öryggisástæðum"</string> diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml index 5e87dcec0736..403c522e24ca 100644 --- a/core/res/res/values-it/strings.xml +++ b/core/res/res/values-it/strings.xml @@ -2408,8 +2408,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Spazio privato"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Condiviso"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Spazio privato"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Contenuti sensibili della notifica nascosti"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Contenuti dell\'app nascosti dalla condivisione schermo per sicurezza"</string> diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml index 439565d5951c..21e9293a7e4f 100644 --- a/core/res/res/values-iw/strings.xml +++ b/core/res/res/values-iw/strings.xml @@ -2039,7 +2039,7 @@ <string name="pin_specific_target" msgid="7824671240625957415">"הצמדה של <xliff:g id="LABEL">%1$s</xliff:g>"</string> <string name="unpin_target" msgid="3963318576590204447">"ביטול הצמדה"</string> <string name="unpin_specific_target" msgid="3859828252160908146">"ביטול ההצמדה של <xliff:g id="LABEL">%1$s</xliff:g>"</string> - <string name="app_info" msgid="6113278084877079851">"פרטי אפליקציה"</string> + <string name="app_info" msgid="6113278084877079851">"פרטי האפליקציה"</string> <string name="negative_duration" msgid="1938335096972945232">"−<xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="demo_starting_message" msgid="6577581216125805905">"תהליך ההדגמה מתחיל…"</string> <string name="demo_restarting_message" msgid="1160053183701746766">"מתבצע איפוס של המכשיר…"</string> @@ -2408,8 +2408,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"המרחב הפרטי"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"שכפול"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"שיתופי"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"המרחב הפרטי"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"יש תוכן רגיש בהתראה שהוסתר"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"תוכן האפליקציה מוסתר משיתוף המסך מטעמי אבטחה"</string> diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml index fcaef68c6ebf..149f3cd02f54 100644 --- a/core/res/res/values-ja/strings.xml +++ b/core/res/res/values-ja/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"プライベート スペース"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"複製"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"共用"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"プライベート スペース"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"プライベートな通知内容は表示されません"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"セキュリティ上、画面共有ではアプリの内容は非表示となります"</string> diff --git a/core/res/res/values-ka/strings.xml b/core/res/res/values-ka/strings.xml index 2b98c58ff272..ee1e89d8a40f 100644 --- a/core/res/res/values-ka/strings.xml +++ b/core/res/res/values-ka/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"კერძო სივრცე"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"კლონის შექმნა"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"საერთო"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"კერძო სივრცე"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"სენსიტიური შეტყობინების კონტენტი დამალულია"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"ეკრანის გაზიარებიდან აპის კონტენტი დამალულია უსაფრთხოების მიზნით"</string> diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml index 6e9eb1220b50..63457e000bce 100644 --- a/core/res/res/values-kk/strings.xml +++ b/core/res/res/values-kk/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Құпия кеңістік"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Клон"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Жалпы"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Құпия кеңістік"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Хабарландырудың құпия контенті жасырылған."</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Қауіпсіздік мақсатында қолданба контенті экранды көрсету кезінде жасырылды."</string> diff --git a/core/res/res/values-km/strings.xml b/core/res/res/values-km/strings.xml index 70f59c1c9235..7294d4b0f3fd 100644 --- a/core/res/res/values-km/strings.xml +++ b/core/res/res/values-km/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"លំហឯកជន"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"ក្លូន"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"ទូទៅ"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"លំហឯកជន"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"បានលាក់ខ្លឹមសារជូនដំណឹងដែលមានលក្ខណៈរសើប"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"បានលាក់ខ្លឹមសារកម្មវិធីពីការបង្ហាញអេក្រង់ដើម្បីសុវត្ថិភាព"</string> diff --git a/core/res/res/values-kn/strings.xml b/core/res/res/values-kn/strings.xml index f22c43a4dd03..3d7c7823f4d6 100644 --- a/core/res/res/values-kn/strings.xml +++ b/core/res/res/values-kn/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"ಪ್ರೈವೆಟ್ ಸ್ಪೇಸ್"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"ಕ್ಲೋನ್"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"ಸಮುದಾಯ"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"ಪ್ರೈವೆಟ್ ಸ್ಪೇಸ್"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"ಸೂಕ್ಷ್ಮ ನೋಟಿಫಿಕೇಶನ್ ಕಂಟೆಂಟ್ ಅನ್ನು ಮರೆಮಾಡಲಾಗಿದೆ"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"ಭದ್ರತೆಗಾಗಿ ಸ್ಕ್ರೀನ್ ಹಂಚಿಕೊಳ್ಳುವಿಕೆಯಲ್ಲಿ ಆ್ಯಪ್ ಕಂಟೆಂಟ್ ಅನ್ನು ಮರೆಮಾಡಲಾಗಿದೆ"</string> diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml index 99b871579c1c..d815fe8f82bd 100644 --- a/core/res/res/values-ko/strings.xml +++ b/core/res/res/values-ko/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"비공개 스페이스"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"클론"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"공동"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"비공개 스페이스"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"민감한 알림 콘텐츠 숨김"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"보안을 위해 화면 공유에서 앱 콘텐츠가 숨겨집니다."</string> diff --git a/core/res/res/values-ky/strings.xml b/core/res/res/values-ky/strings.xml index 427010b0e356..3cdf3cad862a 100644 --- a/core/res/res/values-ky/strings.xml +++ b/core/res/res/values-ky/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Жеке мейкиндик"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Клон"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Жалпы"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Жеке мейкиндик"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Купуя билдирменин мазмуну жашырылган"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Коопсуздук үчүн колдонмодогу контент бөлүшүлгөн экрандан жашырылды"</string> diff --git a/core/res/res/values-lo/strings.xml b/core/res/res/values-lo/strings.xml index 4682bb0f1a93..80e09a6b4a1e 100644 --- a/core/res/res/values-lo/strings.xml +++ b/core/res/res/values-lo/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"ພື້ນທີ່ສ່ວນບຸກຄົນ"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"ໂຄລນ"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"ສ່ວນກາງ"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"ພື້ນທີ່ສ່ວນບຸກຄົນ"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"ເນື້ອຫາການແຈ້ງເຕືອນທີ່ລະອຽດອ່ອນເຊື່ອງຢູ່"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"ເນື້ອຫາແອັບຖືກເຊື່ອງໄວ້ຈາກການແບ່ງປັນໜ້າຈໍເພື່ອຄວາມປອດໄພ"</string> diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml index 6fbb9b829164..b2372414614a 100644 --- a/core/res/res/values-lt/strings.xml +++ b/core/res/res/values-lt/strings.xml @@ -2409,8 +2409,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privati erdvė"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klonuoti"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Bendruomenės"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Privati erdvė"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Neskelbtinos informacijos pranešimo turinys paslėptas"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Programos turinys paslėptas bendrinant ekraną saugumo sumetimais"</string> diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml index 0bea4ff64fbf..cc902cd9ab40 100644 --- a/core/res/res/values-lv/strings.xml +++ b/core/res/res/values-lv/strings.xml @@ -2408,8 +2408,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privātā telpa"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klons"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Kopīgs"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Privātā telpa"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Sensitīvs paziņojuma saturs ir paslēpts"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Drošības nolūkos lietotnes saturs kopīgotajā ekrānā ir paslēpts"</string> diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml index d7a6dd2902cc..b61b817dcf9c 100644 --- a/core/res/res/values-mk/strings.xml +++ b/core/res/res/values-mk/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Приватен простор"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Клониран профил"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Профил на заедницата"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Приватен простор"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Содржината на чувствителните известувања е скриена"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Од безбедносни причини, содржините на апликацијата се скриени од споделувањето екран"</string> diff --git a/core/res/res/values-ml/strings.xml b/core/res/res/values-ml/strings.xml index 402333699754..9906470edbcf 100644 --- a/core/res/res/values-ml/strings.xml +++ b/core/res/res/values-ml/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"സ്വകാര്യ സ്പേസ്"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"ക്ലോൺ ചെയ്യുക"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"കമ്മ്യൂണൽ"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"സ്വകാര്യ സ്പേസ്"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"സൂക്ഷ്മമായി കൈകാര്യം ചെയ്യേണ്ട അറിയിപ്പ് ഉള്ളടക്കം മറച്ചിരിക്കുന്നു"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"ആപ്പ് ഉള്ളടക്കം, അതിന്റെ സുരക്ഷയ്ക്കായി സ്ക്രീൻ പങ്കിടലിൽ നിന്ന് മറച്ചിരിക്കുന്നു"</string> diff --git a/core/res/res/values-mn/strings.xml b/core/res/res/values-mn/strings.xml index 9aa18e2d4b82..22f8f15ae8da 100644 --- a/core/res/res/values-mn/strings.xml +++ b/core/res/res/values-mn/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Хаалттай орон зай"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Клон"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Нийтийн"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Хаалттай орон зай"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Эмзэг мэдэгдлийн контентыг нуусан"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Аюулгүй байдлын улмаас аппын контентыг дэлгэц хуваалцахаас нуусан"</string> diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml index a115e2c61724..36d0bba7e17f 100644 --- a/core/res/res/values-mr/strings.xml +++ b/core/res/res/values-mr/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"खाजगी स्पेस"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"क्लोन"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"सामुदायिक"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"खाजगी स्पेस"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"संवेदनशील नोटिफिकेशनचा आशय लपवलेला आहे"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"स्क्रीन शेअर करताना सुरक्षेसाठी अॅपमधील आशय लपवला आहे"</string> diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml index 2fa571ceb8a0..c215f62e7aa1 100644 --- a/core/res/res/values-ms/strings.xml +++ b/core/res/res/values-ms/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Ruang persendirian"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Umum"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Ruang privasi"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Kandungan pemberitahuan yang sensitif disembunyikan"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Kandungan apl disembunyikan daripada perkongsian skrin untuk keselamatan"</string> diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml index 364a7b169c50..f030ef2ad024 100644 --- a/core/res/res/values-my/strings.xml +++ b/core/res/res/values-my/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"သီးသန့်နေရာ"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"ပုံတူပွားရန်"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"အများသုံး"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"သီးသန့်နေရာ"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"သတိထားရမည့် အကြောင်းကြားချက်ပါ အချက်အလက်ကို ဖျောက်ထားသည်"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"အက်ပ်အကြောင်းအရာသည် လုံခြုံရေးအတွက် မျက်နှာပြင် မျှဝေခြင်းမှ ဖျောက်ထားသည်"</string> diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml index e2de428b1e93..116c586cb628 100644 --- a/core/res/res/values-nb/strings.xml +++ b/core/res/res/values-nb/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privat område"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Felles"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Privat område"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Sensitivt varselinnhold er skjult"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Av sikkerhetsgrunner er appinnholdet skjult for skjermdelingen"</string> diff --git a/core/res/res/values-ne/strings.xml b/core/res/res/values-ne/strings.xml index 8d5f560d521a..a202d4c4d81c 100644 --- a/core/res/res/values-ne/strings.xml +++ b/core/res/res/values-ne/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"निजी स्पेस"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"क्लोन"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"सामुदायिक"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"निजी स्पेस"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"संवेदनशील सूचनासम्बन्धी सामग्री लुकाइएको छ"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"स्क्रिन सेयर गर्दा सुरक्षाका लागि एपमा भएको सामग्री लुकाइएको छ"</string> diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml index 5bc6c4421339..51d895987fea 100644 --- a/core/res/res/values-nl/strings.xml +++ b/core/res/res/values-nl/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privégedeelte"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Kloon"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Gemeenschappelijk"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Privégedeelte"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Content van gevoelige meldingen verborgen"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"App-content verborgen voor scherm delen vanwege beveiligingsrisico\'s"</string> diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml index 4c7868d03914..90ab620fb710 100644 --- a/core/res/res/values-or/strings.xml +++ b/core/res/res/values-or/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"ପ୍ରାଇଭେଟ ସ୍ପେସ"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"କ୍ଲୋନ"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"କମ୍ୟୁନାଲ"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"ପ୍ରାଇଭେଟ ସ୍ପେସ"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"ସମ୍ୱେଦନଶୀଳ ବିଜ୍ଞପ୍ତି ବିଷୟବସ୍ତୁକୁ ଲୁଚାଯାଇଛି"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"ସୁରକ୍ଷା ପାଇଁ ସ୍କ୍ରିନ ସେୟାରରୁ ଆପ ବିଷୟବସ୍ତୁକୁ ଲୁଚାଯାଇଛି"</string> diff --git a/core/res/res/values-pa/strings.xml b/core/res/res/values-pa/strings.xml index 38780b610f18..de91ea7ec24a 100644 --- a/core/res/res/values-pa/strings.xml +++ b/core/res/res/values-pa/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"ਪ੍ਰਾਈਵੇਟ ਸਪੇਸ"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"ਕਲੋਨ"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"ਭਾਈਚਾਰਕ"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"ਪ੍ਰਾਈਵੇਟ ਸਪੇਸ"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"ਲੁਕੀ ਹੋਈ ਸੰਵੇਦਨਸ਼ੀਲ ਸੂਚਨਾ ਸਮੱਗਰੀ"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"ਐਪ ਸਮੱਗਰੀ ਨੂੰ ਸੁਰੱਖਿਆ ਲਈ ਸਕ੍ਰੀਨ ਸਾਂਝਾਕਰਨ ਤੋਂ ਲੁਕਾਇਆ ਗਿਆ ਹੈ"</string> diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml index 95e1a30a6dd4..d9a95b898356 100644 --- a/core/res/res/values-pl/strings.xml +++ b/core/res/res/values-pl/strings.xml @@ -2409,8 +2409,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Przestrzeń prywatna"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Wspólny"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Przestrzeń prywatna"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Treść poufnego powiadomienia została ukryta"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Ze względów bezpieczeństwa zawartość aplikacji jest niewidoczna podczas udostępniania ekranu"</string> diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml index c9d36b149fdf..cd3a7c55bed8 100644 --- a/core/res/res/values-pt-rBR/strings.xml +++ b/core/res/res/values-pt-rBR/strings.xml @@ -2408,8 +2408,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Espaço privado"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Público"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Espaço privado"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Conteúdo de notificação sensível oculto"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Conteúdo do app oculto no compartilhamento de tela por motivos de segurança"</string> diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml index f1b2bd07acca..d8fe4fe8c2e1 100644 --- a/core/res/res/values-pt-rPT/strings.xml +++ b/core/res/res/values-pt-rPT/strings.xml @@ -733,7 +733,7 @@ <skip /> <string name="face_acquired_recalibrate_alt" msgid="5702674220280332115">"Não é possível criar o seu modelo de rosto. Tente novamente."</string> <string name="face_acquired_dark_glasses_detected_alt" msgid="4052123776406041972">"Óculos escuros detetados. O seu rosto tem de estar completamente visível."</string> - <string name="face_acquired_mouth_covering_detected_alt" msgid="1122294982850589766">"Máscara detetada. Todo o rosto tem de estar visível"</string> + <string name="face_acquired_mouth_covering_detected_alt" msgid="1122294982850589766">"Cobertura facial detetada. O seu rosto tem de estar completamente visível."</string> <string-array name="face_acquired_vendor"> </string-array> <string name="face_error_hw_not_available" msgid="5085202213036026288">"Não pode validar o rosto. Hardware não disponível."</string> @@ -1292,7 +1292,7 @@ <string name="android_upgrading_complete" msgid="409800058018374746">"A concluir o arranque."</string> <string name="fp_power_button_enrollment_message" msgid="5648173517663246140">"Premiu o botão ligar/desligar. Geralmente, esta ação desliga o ecrã.\n\nExperimente tocar levemente ao configurar a sua impressão digital."</string> <string name="fp_power_button_enrollment_title" msgid="6976841690455338563">"Para terminar, desligue o ecrã"</string> - <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"Desligar"</string> + <string name="fp_power_button_enrollment_button_text" msgid="3199783266386029200">"Desativar"</string> <string name="fp_power_button_bp_title" msgid="5585506104526820067">"Continuar a validar a impressão digital?"</string> <string name="fp_power_button_bp_message" msgid="2983163038168903393">"Premiu o botão ligar/desligar. Geralmente, esta ação desliga o ecrã.\n\nExperimente tocar levemente para validar a sua impressão digital."</string> <string name="fp_power_button_bp_positive_button" msgid="728945472408552251">"Desligar ecrã"</string> @@ -1438,7 +1438,7 @@ <string name="alert_windows_notification_channel_name" msgid="3437528564303192620">"A app <xliff:g id="NAME">%s</xliff:g> sobrepõe-se a outras aplicações"</string> <string name="alert_windows_notification_title" msgid="6331662751095228536">"O <xliff:g id="NAME">%s</xliff:g> sobrepõe-se a outras app"</string> <string name="alert_windows_notification_message" msgid="6538171456970725333">"Se não quer que a app <xliff:g id="NAME">%s</xliff:g> utilize esta funcionalidade, toque para abrir as definições e desative-a."</string> - <string name="alert_windows_notification_turn_off_action" msgid="7805857234839123780">"Desligar"</string> + <string name="alert_windows_notification_turn_off_action" msgid="7805857234839123780">"Desativar"</string> <string name="ext_media_checking_notification_title" msgid="8299199995416510094">"A verificar o <xliff:g id="NAME">%s</xliff:g>…"</string> <string name="ext_media_checking_notification_message" msgid="2231566971425375542">"A rever o conteúdo atual…"</string> <string name="ext_media_checking_notification_message" product="tv" msgid="7986154434946021415">"A analisar o armazenamento de multimédia"</string> @@ -2408,8 +2408,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Espaço privado"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Comum"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Espaço privado"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Conteúdo das notificações sensíveis ocultado"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Conteúdo da app ocultado da partilha de ecrã por motivos de segurança"</string> diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml index c9d36b149fdf..cd3a7c55bed8 100644 --- a/core/res/res/values-pt/strings.xml +++ b/core/res/res/values-pt/strings.xml @@ -2408,8 +2408,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Espaço privado"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Público"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Espaço privado"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Conteúdo de notificação sensível oculto"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Conteúdo do app oculto no compartilhamento de tela por motivos de segurança"</string> diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml index e14aec706cb7..d43556d47f86 100644 --- a/core/res/res/values-ro/strings.xml +++ b/core/res/res/values-ro/strings.xml @@ -2408,8 +2408,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Spațiu privat"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clonă"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Comun"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Spațiu privat"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Conținutul sensibil din notificări a fost ascuns"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Conținutul aplicației este ascuns de permiterea accesului la ecran din motive de securitate"</string> diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml index 79b0daa445fa..16ddae82f144 100644 --- a/core/res/res/values-ru/strings.xml +++ b/core/res/res/values-ru/strings.xml @@ -2409,8 +2409,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Частное пространство"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Клонированный"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Совместный"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Частное пространство"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Конфиденциальная информация в уведомлении скрыта"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Содержимое приложения исключено из демонстрации экрана в целях безопасности."</string> diff --git a/core/res/res/values-si/strings.xml b/core/res/res/values-si/strings.xml index 1e69e0bc9911..4f6e756d569e 100644 --- a/core/res/res/values-si/strings.xml +++ b/core/res/res/values-si/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"රහසිගත අවකාශය"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"ක්ලෝන කරන්න"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"වාර්ගික"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"රහසිගත අවකාශය"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"සංවේදී දැනුම්දීම් අන්තර්ගතය සැඟවී ඇත"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"ආරක්ෂාව සඳහා යෙදුම් අන්තර්ගතය තිරය බෙදා ගැනීමෙන් සඟවා ඇත"</string> diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml index c7ca9f471942..aeea7b688dbb 100644 --- a/core/res/res/values-sk/strings.xml +++ b/core/res/res/values-sk/strings.xml @@ -2040,7 +2040,7 @@ <string name="pin_specific_target" msgid="7824671240625957415">"Pripnúť <xliff:g id="LABEL">%1$s</xliff:g>"</string> <string name="unpin_target" msgid="3963318576590204447">"Uvoľniť"</string> <string name="unpin_specific_target" msgid="3859828252160908146">"Odopnúť <xliff:g id="LABEL">%1$s</xliff:g>"</string> - <string name="app_info" msgid="6113278084877079851">"Info o aplikácii"</string> + <string name="app_info" msgid="6113278084877079851">"Informácie o aplikácii"</string> <string name="negative_duration" msgid="1938335096972945232">"-<xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="demo_starting_message" msgid="6577581216125805905">"Spúšťa sa ukážka…"</string> <string name="demo_restarting_message" msgid="1160053183701746766">"Resetuje sa zariadenie…"</string> @@ -2409,8 +2409,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Súkromný priestor"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Spoločný"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Súkromný priestor"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Obsah citlivého upozornenia je skrytý"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Obsah aplikácie bol na účely zabezpečenia skrytý v zdieľaní obrazovky"</string> diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml index 4f23a546376d..d3ce57cdb28b 100644 --- a/core/res/res/values-sl/strings.xml +++ b/core/res/res/values-sl/strings.xml @@ -2409,8 +2409,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Zasebni prostor"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Skupno"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Zasebni prostor"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Občutljiva vsebina obvestila je bila skrita"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Pri deljenju zaslona je vsebina aplikacije skrita zaradi varnosti"</string> diff --git a/core/res/res/values-sq/strings.xml b/core/res/res/values-sq/strings.xml index c49402bc2cc5..de0210013732 100644 --- a/core/res/res/values-sq/strings.xml +++ b/core/res/res/values-sq/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Hapësira private"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"I përbashkët"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Hapësira private"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Përmbajtjet delikate të njoftimeve janë fshehur"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Përmbajtja e aplikacionit është fshehur nga ndarja e ekranit për arsye sigurie"</string> diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml index 2b3f75a60555..f200ac16c798 100644 --- a/core/res/res/values-sr/strings.xml +++ b/core/res/res/values-sr/strings.xml @@ -2408,8 +2408,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Приватан простор"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Клонирано"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Заједничко"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Приватан простор"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Осетљив садржај обавештења је скривен"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Садржај апликације је скривен за дељење садржаја екрана због безбедности"</string> diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml index 28333815e1d0..589414da6291 100644 --- a/core/res/res/values-sv/strings.xml +++ b/core/res/res/values-sv/strings.xml @@ -2038,7 +2038,7 @@ <string name="pin_specific_target" msgid="7824671240625957415">"Fäst <xliff:g id="LABEL">%1$s</xliff:g>"</string> <string name="unpin_target" msgid="3963318576590204447">"Lossa"</string> <string name="unpin_specific_target" msgid="3859828252160908146">"Lossa <xliff:g id="LABEL">%1$s</xliff:g>"</string> - <string name="app_info" msgid="6113278084877079851">"Info om appen"</string> + <string name="app_info" msgid="6113278084877079851">"Appinformation"</string> <string name="negative_duration" msgid="1938335096972945232">"-<xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="demo_starting_message" msgid="6577581216125805905">"Demo startas …"</string> <string name="demo_restarting_message" msgid="1160053183701746766">"Enheten återställs …"</string> @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Privat område"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klona"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Allmän"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Privat område"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Känsligt aviseringsinnehåll dolt"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Av säkerhetsskäl döljs appinnehållet vid skärmdelning"</string> diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml index d3af99f7798f..4b50cb7366ef 100644 --- a/core/res/res/values-sw/strings.xml +++ b/core/res/res/values-sw/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Sehemu ya faragha"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Nakala"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Wasifu wa pamoja"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Sehemu ya faragha"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Maudhui nyeti kwenye arifa yamefichwa"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Maudhui ya programu yamefichwa ili yasionekane kwenye skrini ya pamoja kwa sababu za kiusalama"</string> diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml index 37abcef212cf..6ad8f59e0903 100644 --- a/core/res/res/values-ta/strings.xml +++ b/core/res/res/values-ta/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"ரகசிய இடம்"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"குளோன்"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"பொது"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"ரகசிய இடம்"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"உணர்வுபூர்வமான அறிவிப்பு உள்ளடக்கம் மறைக்கப்பட்டது"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"பாதுகாப்பிற்காக, திரைப் பகிர்வில் இருந்து ஆப்ஸ் உள்ளடக்கம் மறைக்கப்பட்டுள்ளது"</string> diff --git a/core/res/res/values-te/strings.xml b/core/res/res/values-te/strings.xml index 2766248dabbd..91f9ef200a5c 100644 --- a/core/res/res/values-te/strings.xml +++ b/core/res/res/values-te/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"ప్రైవేట్ స్పేస్"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"క్లోన్"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"కమ్యూనల్"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"ప్రైవేట్ స్పేస్"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"గోప్యమైన నోటిఫికేషన్ కంటెంట్ దాచబడింది"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"సెక్యూరిటీ కోసం స్క్రీన్ షేర్ నుండి యాప్ కంటెంట్ దాచబడింది"</string> diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml index 1f2a0911ff5f..eb102fc79966 100644 --- a/core/res/res/values-th/strings.xml +++ b/core/res/res/values-th/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"พื้นที่ส่วนตัว"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"โคลน"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"ส่วนกลาง"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"พื้นที่ส่วนตัว"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"เนื้อหาการแจ้งเตือนที่ละเอียดอ่อนซ่อนอยู่"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"ซ่อนเนื้อหาแอปจากการแชร์หน้าจอเพื่อความปลอดภัย"</string> diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml index 7bce4b78c437..de499d346524 100644 --- a/core/res/res/values-tl/strings.xml +++ b/core/res/res/values-tl/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Pribadong space"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Clone"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Communal"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Pribadong space"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Nakatago ang content ng sensitibong notification"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Nakatago ang content ng app mula sa pagbabahagi ng screen para sa seguridad"</string> diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml index cbfb77037e73..7dab2360da04 100644 --- a/core/res/res/values-tr/strings.xml +++ b/core/res/res/values-tr/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Özel alan"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Klon"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Paylaşılan"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Özel alan"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Hassas bildirim içerikleri gizlendi"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Uygulama içerikleri, güvenlik nedeniyle ekran paylaşımında gizlendi"</string> diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml index 5266acde1ecb..22c72727537b 100644 --- a/core/res/res/values-uk/strings.xml +++ b/core/res/res/values-uk/strings.xml @@ -2409,8 +2409,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Приватний простір"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Копія профілю"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Спільний профіль"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Приватний простір"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Чутливий вміст сповіщення приховано"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"З міркувань безпеки вміст додатка приховано під час показу екрана"</string> diff --git a/core/res/res/values-ur/strings.xml b/core/res/res/values-ur/strings.xml index 2fe52f83d09a..0d878c9d1346 100644 --- a/core/res/res/values-ur/strings.xml +++ b/core/res/res/values-ur/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"پرائیویٹ اسپیس"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"کلون"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"کمیونل"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"پرائیویٹ اسپیس"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"حساس اطلاعی مواد چھپا ہوا ہے"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"سیکیورٹی کے مد نظر ایپ کا مواد اسکرین کے اشتراک سے چھپا ہوا ہے"</string> diff --git a/core/res/res/values-uz/strings.xml b/core/res/res/values-uz/strings.xml index d40f38338138..fc5d8ee32520 100644 --- a/core/res/res/values-uz/strings.xml +++ b/core/res/res/values-uz/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Maxfiy makon"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Nusxalash"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Umumiy"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Maxfiy makon"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Bildirishnomadagi maxfiy axborot berkitildi"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Ekran namoyishida xavfsizlik maqsadida ilova kontenti berkitildi"</string> diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml index 1051cdab6599..4619be37f2ad 100644 --- a/core/res/res/values-vi/strings.xml +++ b/core/res/res/values-vi/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Không gian riêng tư"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Nhân bản"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Dùng chung"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Không gian riêng tư"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Đã ẩn nội dung thông báo nhạy cảm"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Nội dung ứng dụng bị ẩn khỏi tính năng chia sẻ màn hình vì lý do bảo mật"</string> diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml index d16a353e9bfc..a6c62dc826ce 100644 --- a/core/res/res/values-zh-rCN/strings.xml +++ b/core/res/res/values-zh-rCN/strings.xml @@ -581,7 +581,7 @@ <string name="permlab_changeTetherState" msgid="9079611809931863861">"更改网络共享连接"</string> <string name="permdesc_changeTetherState" msgid="3025129606422533085">"允许应用更改绑定网络连接的状态。"</string> <string name="permlab_accessWifiState" msgid="5552488500317911052">"查看WLAN连接"</string> - <string name="permdesc_accessWifiState" msgid="6913641669259483363">"允许该应用查看WLAN网络的相关信息,例如是否启用了WLAN以及连接的WLAN设备的名称。"</string> + <string name="permdesc_accessWifiState" msgid="6913641669259483363">"允许该应用查看 WLAN 网络的相关信息,例如是否启用了 WLAN 以及连接的 WLAN 设备的名称。"</string> <string name="permlab_changeWifiState" msgid="7947824109713181554">"连接WLAN网络和断开连接"</string> <string name="permdesc_changeWifiState" msgid="7170350070554505384">"允许该应用与WLAN接入点建立和断开连接,以及更改WLAN网络的设备配置。"</string> <string name="permlab_changeWifiMulticastState" msgid="285626875870754696">"允许接收WLAN多播"</string> @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"私密空间"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"克隆"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"共用"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"私密空间"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"已隐藏敏感通知内容"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"为安全起见而在屏幕共享画面中处于隐藏状态的应用内容"</string> diff --git a/core/res/res/values-zh-rHK/strings.xml b/core/res/res/values-zh-rHK/strings.xml index 672a638e6932..f9b10a2dac52 100644 --- a/core/res/res/values-zh-rHK/strings.xml +++ b/core/res/res/values-zh-rHK/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"私人空間"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"複製"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"共用"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"私人空間"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"已隱藏敏感通知內容"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"為安全起見,應用程式內容已從分享螢幕畫面隱藏"</string> diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml index 8a224ed24675..4dce59ada110 100644 --- a/core/res/res/values-zh-rTW/strings.xml +++ b/core/res/res/values-zh-rTW/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"私人空間"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"複製"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"共通"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"私人空間"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"系統已隱藏含有私密資訊的通知內容"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"為安全起見,分享螢幕畫面未顯示應用程式內容"</string> diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml index 27bd3c949c14..60cb31b4db5c 100644 --- a/core/res/res/values-zu/strings.xml +++ b/core/res/res/values-zu/strings.xml @@ -2407,8 +2407,7 @@ <string name="accessibility_label_private_profile" msgid="1436459319135548969">"Indawo engasese"</string> <string name="accessibility_label_clone_profile" msgid="7579118375042398784">"Yenza i-Clone"</string> <string name="accessibility_label_communal_profile" msgid="1437173163111334791">"Okomphakathi"</string> - <!-- no translation found for private_space_biometric_prompt_title (5777592909271728154) --> - <skip /> + <string name="private_space_biometric_prompt_title" msgid="5777592909271728154">"Indawo engasese"</string> <string name="redacted_notification_message" msgid="1520587845842228816">"Okuqukethwe kwesaziso esizwelayo kufihliwe"</string> <string name="redacted_notification_action_title" msgid="6942924973335920935"></string> <string name="screen_not_shared_sensitive_content" msgid="7058419185079565001">"Okuqukethwe kwe-app kufihliwe kusuka ekwabelaneni kwesikrini ngokuvikelwa"</string> diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsTest.java index 5f78b2a8a5f0..42501c1bf461 100644 --- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsTest.java +++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsTest.java @@ -27,6 +27,7 @@ import android.hardware.broadcastradio.ConfigFlag; import android.hardware.broadcastradio.DabTableEntry; import android.hardware.broadcastradio.IdentifierType; import android.hardware.broadcastradio.Metadata; +import android.hardware.broadcastradio.ProgramFilter; import android.hardware.broadcastradio.ProgramIdentifier; import android.hardware.broadcastradio.ProgramInfo; import android.hardware.broadcastradio.Properties; @@ -41,6 +42,7 @@ import android.hardware.radio.RadioMetadata; import android.hardware.radio.UniqueProgramIdentifier; import android.os.ServiceSpecificException; import android.platform.test.flag.junit.SetFlagsRule; +import android.util.ArraySet; import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder; import com.android.server.broadcastradio.ExtendedRadioMockitoTestCase; @@ -93,6 +95,11 @@ public final class ConversionUtilsTest extends ExtendedRadioMockitoTestCase { private static final long TEST_HD_LOCATION_VALUE = 0x4E647007665CF6L; private static final long TEST_VENDOR_ID_VALUE = 9_901; + private static final ProgramSelector.Identifier TEST_INVALID_ID = + new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_INVALID, 1); + private static final ProgramIdentifier TEST_HAL_INVALID_ID = + AidlTestUtils.makeHalIdentifier(IdentifierType.INVALID, 1); + private static final ProgramSelector.Identifier TEST_DAB_SID_EXT_ID = new ProgramSelector.Identifier( ProgramSelector.IDENTIFIER_TYPE_DAB_DMB_SID_EXT, TEST_DAB_DMB_SID_EXT_VALUE); @@ -139,7 +146,7 @@ public final class ConversionUtilsTest extends ExtendedRadioMockitoTestCase { private static final int TEST_ANNOUNCEMENT_FREQUENCY = FM_LOWER_LIMIT + FM_SPACING; private static final RadioManager.ModuleProperties MODULE_PROPERTIES = - convertToModuleProperties(); + createModuleProperties(); private static final Announcement ANNOUNCEMENT = ConversionUtils.announcementFromHalAnnouncement( AidlTestUtils.makeAnnouncement(TEST_ENABLED_TYPE, TEST_ANNOUNCEMENT_FREQUENCY)); @@ -291,6 +298,37 @@ public final class ConversionUtilsTest extends ExtendedRadioMockitoTestCase { } @Test + public void propertiesFromHalProperties_withoutAmFmAndDabConfigs() { + RadioManager.ModuleProperties properties = createModuleProperties(/* amFmConfig= */ null, + new DabTableEntry[]{}); + + expect.withMessage("Empty AM/FM config") + .that(properties.getBands()).asList().isEmpty(); + expect.withMessage("Empty DAB config") + .that(properties.getDabFrequencyTable()).isNull(); + } + + @Test + public void propertiesFromHalProperties_withInvalidBand() { + AmFmRegionConfig amFmRegionConfig = new AmFmRegionConfig(); + amFmRegionConfig.ranges = new AmFmBandRange[]{createAmFmBandRange(/* lowerBound= */ 50000, + /* upperBound= */ 60000, /* spacing= */ 10), + createAmFmBandRange(FM_LOWER_LIMIT, FM_UPPER_LIMIT, FM_SPACING)}; + + RadioManager.ModuleProperties properties = createModuleProperties(amFmRegionConfig, + new DabTableEntry[]{}); + + RadioManager.BandDescriptor[] bands = properties.getBands(); + expect.withMessage("Band descriptors").that(bands).hasLength(1); + expect.withMessage("FM band frequency lower limit") + .that(bands[0].getLowerLimit()).isEqualTo(FM_LOWER_LIMIT); + expect.withMessage("FM band frequency upper limit") + .that(bands[0].getUpperLimit()).isEqualTo(FM_UPPER_LIMIT); + expect.withMessage("FM band frequency spacing") + .that(bands[0].getSpacing()).isEqualTo(FM_SPACING); + } + + @Test public void identifierToHalProgramIdentifier_withDabId() { ProgramIdentifier halDabId = ConversionUtils.identifierToHalProgramIdentifier(TEST_DAB_SID_EXT_ID); @@ -358,6 +396,13 @@ public final class ConversionUtilsTest extends ExtendedRadioMockitoTestCase { } @Test + public void identifierFromHalProgramIdentifier_withInvalidIdentifier() { + expect.withMessage("Identifier converted from invalid HAL identifier") + .that(ConversionUtils.identifierFromHalProgramIdentifier(TEST_HAL_INVALID_ID)) + .isNull(); + } + + @Test public void programSelectorToHalProgramSelector_withValidSelector() { android.hardware.broadcastradio.ProgramSelector halDabSelector = ConversionUtils.programSelectorToHalProgramSelector(TEST_DAB_SELECTOR); @@ -370,6 +415,23 @@ public final class ConversionUtilsTest extends ExtendedRadioMockitoTestCase { } @Test + public void programSelectorToHalProgramSelector_withInvalidSecondaryId() { + ProgramSelector dabSelector = new ProgramSelector(ProgramSelector.PROGRAM_TYPE_DAB, + TEST_DAB_SID_EXT_ID, new ProgramSelector.Identifier[]{TEST_INVALID_ID, + TEST_DAB_FREQUENCY_ID, TEST_DAB_ENSEMBLE_ID}, /* vendorIds= */ null); + + android.hardware.broadcastradio.ProgramSelector halDabSelector = + ConversionUtils.programSelectorToHalProgramSelector(dabSelector); + + expect.withMessage("Primary identifier of converted HAL DAB selector with invalid " + + "secondary id").that(halDabSelector.primaryId) + .isEqualTo(TEST_HAL_DAB_SID_EXT_ID); + expect.withMessage("Secondary identifiers of converted HAL DAB selector with " + + "invalid secondary id").that(halDabSelector.secondaryIds).asList() + .containsExactly(TEST_HAL_DAB_FREQUENCY_ID, TEST_HAL_DAB_ENSEMBLE_ID); + } + + @Test public void programSelectorFromHalProgramSelector_withValidSelector() { android.hardware.broadcastradio.ProgramSelector halDabSelector = AidlTestUtils.makeHalSelector(TEST_HAL_DAB_SID_EXT_ID, new ProgramIdentifier[]{ @@ -386,6 +448,33 @@ public final class ConversionUtilsTest extends ExtendedRadioMockitoTestCase { } @Test + public void programSelectorFromHalProgramSelector_withInvalidSelector() { + android.hardware.broadcastradio.ProgramSelector invalidSelector = + AidlTestUtils.makeHalSelector(TEST_HAL_INVALID_ID, new ProgramIdentifier[]{}); + + expect.withMessage("Selector converted from invalid HAL selector") + .that(ConversionUtils.programSelectorFromHalProgramSelector(invalidSelector)) + .isNull(); + } + + @Test + public void programSelectorFromHalProgramSelector_withInvalidSecondaryId() { + android.hardware.broadcastradio.ProgramSelector halDabSelector = + AidlTestUtils.makeHalSelector(TEST_HAL_DAB_SID_EXT_ID, new ProgramIdentifier[]{ + TEST_HAL_INVALID_ID, TEST_HAL_DAB_ENSEMBLE_ID, TEST_HAL_DAB_FREQUENCY_ID}); + + ProgramSelector dabSelector = + ConversionUtils.programSelectorFromHalProgramSelector(halDabSelector); + + expect.withMessage("Primary identifier of converted DAB selector with invalid " + + "secondary id").that(dabSelector.getPrimaryId()) + .isEqualTo(TEST_DAB_SID_EXT_ID); + expect.withMessage("Secondary identifiers of converted DAB selector with invalid " + + "secondary id").that(dabSelector.getSecondaryIds()).asList() + .containsExactly(TEST_DAB_FREQUENCY_ID, TEST_DAB_ENSEMBLE_ID); + } + + @Test public void programInfoFromHalProgramInfo_withValidProgramInfo() { android.hardware.broadcastradio.ProgramSelector halDabSelector = AidlTestUtils.makeHalSelector(TEST_HAL_DAB_SID_EXT_ID, new ProgramIdentifier[]{ @@ -628,11 +717,41 @@ public final class ConversionUtilsTest extends ExtendedRadioMockitoTestCase { .that(Utils.getBand(/* freq= */ 110000)).isEqualTo(Utils.FrequencyBand.UNKNOWN); } - private static RadioManager.ModuleProperties convertToModuleProperties() { + @Test + public void filterToHalProgramFilter_withNullFilter() { + ProgramFilter filter = ConversionUtils.filterToHalProgramFilter(null); + + expect.withMessage("Filter identifier types").that(filter.identifierTypes) + .asList().isEmpty(); + expect.withMessage("Filter identifiers").that(filter.identifiers).asList() + .isEmpty(); + } + + @Test + public void filterToHalProgramFilter_withInvalidIdentifier() { + Set<ProgramSelector.Identifier> identifiers = + new ArraySet<ProgramSelector.Identifier>(2); + identifiers.add(TEST_INVALID_ID); + identifiers.add(TEST_DAB_SID_EXT_ID); + ProgramList.Filter filter = new ProgramList.Filter(/* identifierTypes */ new ArraySet<>(), + identifiers, /* includeCategories= */ true, /* excludeModifications= */ false); + ProgramFilter halFilter = ConversionUtils.filterToHalProgramFilter(filter); + + expect.withMessage("Filter identifiers with invalid ones removed") + .that(halFilter.identifiers).asList().containsExactly( + ConversionUtils.identifierToHalProgramIdentifier(TEST_DAB_SID_EXT_ID)); + } + + private static RadioManager.ModuleProperties createModuleProperties() { AmFmRegionConfig amFmConfig = createAmFmRegionConfig(); DabTableEntry[] dabTableEntries = new DabTableEntry[]{ createDabTableEntry(DAB_ENTRY_LABEL_1, DAB_ENTRY_FREQUENCY_1), createDabTableEntry(DAB_ENTRY_LABEL_2, DAB_ENTRY_FREQUENCY_2)}; + return createModuleProperties(amFmConfig, dabTableEntries); + } + + private static RadioManager.ModuleProperties createModuleProperties( + AmFmRegionConfig amFmConfig, DabTableEntry[] dabTableEntries) { Properties properties = createHalProperties(); return ConversionUtils.propertiesFromHalProperties(TEST_ID, TEST_SERVICE_NAME, properties, diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/RadioModuleTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/RadioModuleTest.java index 10ac05d66c62..a952bde956d8 100644 --- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/RadioModuleTest.java +++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/RadioModuleTest.java @@ -16,13 +16,12 @@ package com.android.server.broadcastradio.aidl; -import static com.google.common.truth.Truth.assertWithMessage; - import static org.junit.Assert.assertThrows; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -32,9 +31,13 @@ import android.hardware.radio.Announcement; import android.hardware.radio.IAnnouncementListener; import android.hardware.radio.ICloseHandle; import android.hardware.radio.RadioManager; +import android.os.ParcelableException; import android.os.RemoteException; +import com.google.common.truth.Expect; + import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; @@ -50,6 +53,9 @@ public final class RadioModuleTest { private static final RadioManager.ModuleProperties TEST_MODULE_PROPERTIES = AidlTestUtils.makeDefaultModuleProperties(); + @Rule + public final Expect mExpect = Expect.create(); + // Mocks @Mock private IBroadcastRadio mBroadcastRadioMock; @@ -77,13 +83,13 @@ public final class RadioModuleTest { @Test public void getService() { - assertWithMessage("Service of radio module") + mExpect.withMessage("Service of radio module") .that(mRadioModule.getService()).isEqualTo(mBroadcastRadioMock); } @Test public void getProperties() { - assertWithMessage("Module properties of radio module") + mExpect.withMessage("Module properties of radio module") .that(mRadioModule.getProperties()).isEqualTo(TEST_MODULE_PROPERTIES); } @@ -93,7 +99,7 @@ public final class RadioModuleTest { Bitmap imageTest = mRadioModule.getImage(imageId); - assertWithMessage("Image from radio module").that(imageTest).isNull(); + mExpect.withMessage("Image from radio module").that(imageTest).isNull(); } @Test @@ -104,7 +110,7 @@ public final class RadioModuleTest { mRadioModule.getImage(invalidImageId); }); - assertWithMessage("Exception for getting image with invalid ID") + mExpect.withMessage("Exception for getting image with invalid ID") .that(thrown).hasMessageThat().contains("Image ID is missing"); } @@ -117,6 +123,18 @@ public final class RadioModuleTest { } @Test + public void addAnnouncementListener_whenHalThrowsRemoteException() throws Exception { + doThrow(new RuntimeException("HAL service died")).when(mBroadcastRadioMock) + .registerAnnouncementListener(any(), any()); + + ParcelableException thrown = assertThrows(ParcelableException.class, () -> + mRadioModule.addAnnouncementListener(mListenerMock, new int[]{TEST_ENABLED_TYPE})); + + mExpect.withMessage("Exception for adding announcement listener when HAL service died") + .that(thrown).hasMessageThat().contains("unknown error"); + } + + @Test public void onListUpdate_forAnnouncementListener() throws Exception { android.hardware.broadcastradio.Announcement halAnnouncement = AidlTestUtils.makeAnnouncement(TEST_ENABLED_TYPE, /* selectorFreq= */ 96300); diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java index 755bcdb7df20..4ded91d03579 100644 --- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java +++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java @@ -421,6 +421,19 @@ public final class TunerSessionTest extends ExtendedRadioMockitoTestCase { } @Test + public void tune_withClosedTuner_fails() throws Exception { + openAidlClients(/* numClients= */ 1); + ProgramSelector sel = AidlTestUtils.makeFmSelector(AM_FM_FREQUENCY_LIST[1]); + mTunerSessions[0].close(); + + IllegalStateException thrown = assertThrows(IllegalStateException.class, + () -> mTunerSessions[0].tune(sel)); + + expect.withMessage("Exception for tuning on closed tuner").that(thrown).hasMessageThat() + .contains("Tuner is closed"); + } + + @Test public void step_withDirectionUp() throws Exception { long initFreq = AM_FM_FREQUENCY_LIST[1]; ProgramSelector initialSel = AidlTestUtils.makeFmSelector(initFreq); @@ -1149,6 +1162,20 @@ public final class TunerSessionTest extends ExtendedRadioMockitoTestCase { } @Test + public void onCurrentProgramInfoChanged_withLowerSdkVersion_doesNotInvokesCallback() + throws Exception { + doReturn(false).when(() -> CompatChanges.isChangeEnabled( + eq(ConversionUtils.RADIO_U_VERSION_REQUIRED), anyInt())); + openAidlClients(/* numClients= */ 1); + + mHalTunerCallback.onCurrentProgramInfoChanged( + AidlTestUtils.programInfoToHalProgramInfo(TEST_DAB_INFO)); + + verify(mAidlTunerCallbackMocks[0], after(CALLBACK_TIMEOUT_MS).never()) + .onCurrentProgramInfoChanged(any()); + } + + @Test public void onTuneFailed_forTunerCallback() throws Exception { int numSessions = 3; openAidlClients(numSessions); @@ -1165,6 +1192,20 @@ public final class TunerSessionTest extends ExtendedRadioMockitoTestCase { } @Test + public void onTuneFailed_withLowerSdkVersion_doesNotInvokesCallback() + throws Exception { + doReturn(false).when(() -> CompatChanges.isChangeEnabled( + eq(ConversionUtils.RADIO_U_VERSION_REQUIRED), anyInt())); + openAidlClients(/* numClients= */ 1); + + mHalTunerCallback.onTuneFailed(Result.CANCELED, + ConversionUtils.programSelectorToHalProgramSelector(TEST_DAB_SELECTOR)); + + verify(mAidlTunerCallbackMocks[0], after(CALLBACK_TIMEOUT_MS).never()) + .onTuneFailed(anyInt(), any()); + } + + @Test public void onAntennaStateChange_forTunerCallback() throws Exception { int numSessions = 3; openAidlClients(numSessions); @@ -1231,6 +1272,36 @@ public final class TunerSessionTest extends ExtendedRadioMockitoTestCase { } } + @Test + public void openSession_withNonNullAntennaState() throws Exception { + boolean antennaConnected = false; + android.hardware.radio.ITunerCallback callback = + mock(android.hardware.radio.ITunerCallback.class); + openAidlClients(/* numClients= */ 1); + mHalTunerCallback.onAntennaStateChange(antennaConnected); + verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onAntennaState(antennaConnected); + + mRadioModule.openSession(callback); + + verify(callback, CALLBACK_TIMEOUT).onAntennaState(antennaConnected); + } + + @Test + public void openSession_withNonNullCurrentProgramInfo() throws Exception { + openAidlClients(/* numClients= */ 1); + ProgramSelector initialSel = AidlTestUtils.makeFmSelector(AM_FM_FREQUENCY_LIST[1]); + RadioManager.ProgramInfo tuneInfo = AidlTestUtils.makeProgramInfo(initialSel, + SIGNAL_QUALITY); + mTunerSessions[0].tune(initialSel); + verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onCurrentProgramInfoChanged(tuneInfo); + android.hardware.radio.ITunerCallback callback = + mock(android.hardware.radio.ITunerCallback.class); + + mRadioModule.openSession(callback); + + verify(callback, CALLBACK_TIMEOUT).onCurrentProgramInfoChanged(tuneInfo); + } + private void openAidlClients(int numClients) throws Exception { mAidlTunerCallbackMocks = new android.hardware.radio.ITunerCallback[numClients]; mTunerSessions = new TunerSession[numClients]; diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/ConvertTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/ConvertTest.java index 123e02c0a054..4cb012ca27db 100644 --- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/ConvertTest.java +++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/ConvertTest.java @@ -21,7 +21,6 @@ import android.hardware.broadcastradio.V2_0.AmFmRegionConfig; import android.hardware.broadcastradio.V2_0.DabTableEntry; import android.hardware.broadcastradio.V2_0.IdentifierType; import android.hardware.broadcastradio.V2_0.Properties; -import android.hardware.broadcastradio.V2_0.VendorKeyValue; import android.hardware.radio.Announcement; import android.hardware.radio.ProgramSelector; import android.hardware.radio.RadioManager; @@ -149,6 +148,26 @@ public final class ConvertTest { } @Test + public void propertiesFromHalProperties_withInvalidBand() { + AmFmRegionConfig amFmRegionConfig = new AmFmRegionConfig(); + amFmRegionConfig.ranges = new ArrayList<>(Arrays.asList(createAmFmBandRange( + /* lowerBound= */ 50000, /* upperBound= */ 60000, /* spacing= */ 10), + createAmFmBandRange(FM_LOWER_LIMIT, FM_UPPER_LIMIT, FM_SPACING))); + + RadioManager.ModuleProperties properties = convertToModuleProperties(amFmRegionConfig, + new ArrayList<>()); + + RadioManager.BandDescriptor[] bands = properties.getBands(); + expect.withMessage("Band descriptors").that(bands).hasLength(1); + expect.withMessage("FM band frequency lower limit") + .that(bands[0].getLowerLimit()).isEqualTo(FM_LOWER_LIMIT); + expect.withMessage("FM band frequency upper limit") + .that(bands[0].getUpperLimit()).isEqualTo(FM_UPPER_LIMIT); + expect.withMessage("FM band frequency spacing") + .that(bands[0].getSpacing()).isEqualTo(FM_SPACING); + } + + @Test public void announcementFromHalAnnouncement_typesMatch() { expect.withMessage("Announcement type") .that(ANNOUNCEMENT.getType()).isEqualTo(TEST_ENABLED_TYPE); @@ -184,15 +203,20 @@ public final class ConvertTest { List<DabTableEntry> dabTableEntries = Arrays.asList( createDabTableEntry(DAB_ENTRY_LABEL_1, DAB_ENTRY_FREQUENCY_1), createDabTableEntry(DAB_ENTRY_LABEL_2, DAB_ENTRY_FREQUENCY_2)); - Properties properties = createHalProperties(); + return convertToModuleProperties(amFmConfig, dabTableEntries); + } + + private static RadioManager.ModuleProperties convertToModuleProperties( + AmFmRegionConfig amFmConfig, List<DabTableEntry> dabTableEntries) { + Properties properties = createHalProperties(); return Convert.propertiesFromHal(TEST_ID, TEST_SERVICE_NAME, properties, amFmConfig, dabTableEntries); } private static AmFmRegionConfig createAmFmRegionConfig() { AmFmRegionConfig amFmRegionConfig = new AmFmRegionConfig(); - amFmRegionConfig.ranges = new ArrayList<AmFmBandRange>(Arrays.asList( + amFmRegionConfig.ranges = new ArrayList<>(Arrays.asList( createAmFmBandRange(FM_LOWER_LIMIT, FM_UPPER_LIMIT, FM_SPACING), createAmFmBandRange(AM_LOWER_LIMIT, AM_UPPER_LIMIT, AM_SPACING))); return amFmRegionConfig; @@ -222,7 +246,7 @@ public final class ConvertTest { halProperties.product = TEST_PRODUCT; halProperties.version = TEST_VERSION; halProperties.serial = TEST_SERIAL; - halProperties.vendorInfo = new ArrayList<VendorKeyValue>(Arrays.asList( + halProperties.vendorInfo = new ArrayList<>(Arrays.asList( TestUtils.makeVendorKeyValue(VENDOR_INFO_KEY_1, VENDOR_INFO_VALUE_1), TestUtils.makeVendorKeyValue(VENDOR_INFO_KEY_2, VENDOR_INFO_VALUE_2))); return halProperties; diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TunerSessionHidlTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TunerSessionHidlTest.java index 6edfa0294fdd..898ef577629c 100644 --- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TunerSessionHidlTest.java +++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TunerSessionHidlTest.java @@ -29,8 +29,6 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.never; import static com.android.dx.mockito.inline.extended.ExtendedMockito.timeout; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; -import static com.google.common.truth.Truth.assertWithMessage; - import static org.junit.Assert.assertThrows; import android.graphics.Bitmap; @@ -57,8 +55,11 @@ import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder; import com.android.server.broadcastradio.ExtendedRadioMockitoTestCase; import com.android.server.broadcastradio.RadioServiceUserController; +import com.google.common.truth.Expect; + import org.junit.After; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; @@ -98,6 +99,9 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { private ProgramInfo mHalCurrentInfo; private TunerSession[] mTunerSessions; + @Rule + public final Expect mExpect = Expect.create(); + @Mock private UserHandle mUserHandleMock; @Mock @@ -206,7 +210,7 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { openAidlClients(numSessions); for (int index = 0; index < numSessions; index++) { - assertWithMessage("Session of index %s close state", index) + mExpect.withMessage("Session of index %s close state", index) .that(mTunerSessions[index].isClosed()).isFalse(); } } @@ -238,7 +242,7 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { RadioManager.BandConfig config = mTunerSessions[0].getConfiguration(); - assertWithMessage("Session configuration").that(config) + mExpect.withMessage("Session configuration").that(config) .isEqualTo(FM_BAND_CONFIG); } @@ -248,7 +252,7 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { mTunerSessions[0].setMuted(/* mute= */ false); - assertWithMessage("Session mute state after setting unmuted") + mExpect.withMessage("Session mute state after setting unmuted") .that(mTunerSessions[0].isMuted()).isFalse(); } @@ -258,7 +262,7 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { mTunerSessions[0].setMuted(/* mute= */ true); - assertWithMessage("Session mute state after setting muted") + mExpect.withMessage("Session mute state after setting muted") .that(mTunerSessions[0].isMuted()).isTrue(); } @@ -268,7 +272,7 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { mTunerSessions[0].close(); - assertWithMessage("Close state of broadcast radio service session") + mExpect.withMessage("Close state of broadcast radio service session") .that(mTunerSessions[0].isClosed()).isTrue(); } @@ -282,11 +286,11 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { for (int index = 0; index < numSessions; index++) { if (index == closeIdx) { - assertWithMessage( + mExpect.withMessage( "Close state of broadcast radio service session of index %s", index) .that(mTunerSessions[index].isClosed()).isTrue(); } else { - assertWithMessage( + mExpect.withMessage( "Close state of broadcast radio service session of index %s", index) .that(mTunerSessions[index].isClosed()).isFalse(); } @@ -301,7 +305,21 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { mTunerSessions[0].close(errorCode); verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onError(errorCode); - assertWithMessage("Close state of broadcast radio service session") + mExpect.withMessage("Close state of broadcast radio service session") + .that(mTunerSessions[0].isClosed()).isTrue(); + } + + @Test + public void close_forMultipleTimes() throws Exception { + openAidlClients(/* numClients= */ 1); + int errorCode = RadioTuner.ERROR_SERVER_DIED; + mTunerSessions[0].close(errorCode); + verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onError(errorCode); + + mTunerSessions[0].close(errorCode); + + verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onError(errorCode); + mExpect.withMessage("State of closing broadcast radio service session twice") .that(mTunerSessions[0].isClosed()).isTrue(); } @@ -315,7 +333,7 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { for (int index = 0; index < numSessions; index++) { verify(mAidlTunerCallbackMocks[index], CALLBACK_TIMEOUT).onError(errorCode); - assertWithMessage("Close state of broadcast radio service session of index %s", index) + mExpect.withMessage("Close state of broadcast radio service session of index %s", index) .that(mTunerSessions[index].isClosed()).isTrue(); } } @@ -365,7 +383,7 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { UnsupportedOperationException thrown = assertThrows(UnsupportedOperationException.class, () -> mTunerSessions[0].tune(unsupportedSelector)); - assertWithMessage("Exception for tuning on unsupported program selector") + mExpect.withMessage("Exception for tuning on unsupported program selector") .that(thrown).hasMessageThat().contains("tune: NOT_SUPPORTED"); } @@ -393,11 +411,24 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { mTunerSessions[0].tune(sel); }); - assertWithMessage("Unknown error HAL exception when tuning") + mExpect.withMessage("Unknown error HAL exception when tuning") .that(thrown).hasMessageThat().contains(Result.toString(Result.UNKNOWN_ERROR)); } @Test + public void tune_withClosedTuner_fails() throws Exception { + openAidlClients(/* numClients= */ 1); + ProgramSelector sel = TestUtils.makeFmSelector(AM_FM_FREQUENCY_LIST[1]); + mTunerSessions[0].close(); + + IllegalStateException thrown = assertThrows(IllegalStateException.class, + () -> mTunerSessions[0].tune(sel)); + + mExpect.withMessage("Exception for tuning on closed tuner").that(thrown).hasMessageThat() + .contains("Tuner is closed"); + } + + @Test public void step_withDirectionUp() throws Exception { long initFreq = AM_FM_FREQUENCY_LIST[1]; ProgramSelector initialSel = TestUtils.makeFmSelector(initFreq); @@ -454,7 +485,7 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { mTunerSessions[0].step(/* directionDown= */ true, /* skipSubChannel= */ false); }); - assertWithMessage("Exception for stepping when HAL is in invalid state") + mExpect.withMessage("Exception for stepping when HAL is in invalid state") .that(thrown).hasMessageThat().contains(Result.toString(Result.INVALID_STATE)); } @@ -533,7 +564,7 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { mTunerSessions[0].seek(/* directionDown= */ true, /* skipSubChannel= */ false); }); - assertWithMessage("Internal error HAL exception when seeking") + mExpect.withMessage("Internal error HAL exception when seeking") .that(thrown).hasMessageThat().contains(Result.toString(Result.INTERNAL_ERROR)); } @@ -566,7 +597,7 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { mTunerSessions[0].cancel(); }); - assertWithMessage("Exception for canceling when HAL throws remote exception") + mExpect.withMessage("Exception for canceling when HAL throws remote exception") .that(thrown).hasMessageThat().contains(exceptionMessage); } @@ -579,7 +610,7 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { mTunerSessions[0].getImage(imageId); }); - assertWithMessage("Get image exception") + mExpect.withMessage("Get image exception") .that(thrown).hasMessageThat().contains("Image ID is missing"); } @@ -590,7 +621,7 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { Bitmap imageTest = mTunerSessions[0].getImage(imageId); - assertWithMessage("Null image").that(imageTest).isEqualTo(null); + mExpect.withMessage("Null image").that(imageTest).isEqualTo(null); } @Test @@ -603,7 +634,7 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { mTunerSessions[0].getImage(/* id= */ 1); }); - assertWithMessage("Exception for getting image when HAL throws remote exception") + mExpect.withMessage("Exception for getting image when HAL throws remote exception") .that(thrown).hasMessageThat().contains(exceptionMessage); } @@ -649,7 +680,7 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { mTunerSessions[0].startProgramListUpdates(/* filter= */ null); }); - assertWithMessage("Unknown error HAL exception when updating program list") + mExpect.withMessage("Unknown error HAL exception when updating program list") .that(thrown).hasMessageThat().contains(Result.toString(Result.UNKNOWN_ERROR)); } @@ -686,7 +717,7 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { boolean isSupported = mTunerSessions[0].isConfigFlagSupported(flag); verify(mHalTunerSessionMock).isConfigFlagSet(eq(flag), any()); - assertWithMessage("Config flag %s is supported", flag).that(isSupported).isFalse(); + mExpect.withMessage("Config flag %s is supported", flag).that(isSupported).isFalse(); } @Test @@ -697,7 +728,7 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { boolean isSupported = mTunerSessions[0].isConfigFlagSupported(flag); verify(mHalTunerSessionMock).isConfigFlagSet(eq(flag), any()); - assertWithMessage("Config flag %s is supported", flag).that(isSupported).isTrue(); + mExpect.withMessage("Config flag %s is supported", flag).that(isSupported).isTrue(); } @Test @@ -709,7 +740,7 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { mTunerSessions[0].setConfigFlag(flag, /* value= */ true); }); - assertWithMessage("Exception for setting unsupported flag %s", flag) + mExpect.withMessage("Exception for setting unsupported flag %s", flag) .that(thrown).hasMessageThat().contains("setConfigFlag: NOT_SUPPORTED"); } @@ -755,7 +786,7 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { mTunerSessions[0].isConfigFlagSet(flag); }); - assertWithMessage("Exception for checking if unsupported flag %s is set", flag) + mExpect.withMessage("Exception for checking if unsupported flag %s is set", flag) .that(thrown).hasMessageThat().contains("isConfigFlagSet: NOT_SUPPORTED"); } @@ -768,7 +799,7 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { boolean isSet = mTunerSessions[0].isConfigFlagSet(flag); - assertWithMessage("Config flag %s is set", flag) + mExpect.withMessage("Config flag %s is set", flag) .that(isSet).isEqualTo(expectedConfigFlagValue); } @@ -782,7 +813,7 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { mTunerSessions[0].isConfigFlagSet(flag); }); - assertWithMessage("Exception for checking config flag when HAL throws remote exception") + mExpect.withMessage("Exception for checking config flag when HAL throws remote exception") .that(thrown).hasMessageThat().contains("Failed to check flag"); } @@ -822,7 +853,7 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { mTunerSessions[0].setParameters(parametersSet); }); - assertWithMessage("Exception for setting parameters when HAL throws remote exception") + mExpect.withMessage("Exception for setting parameters when HAL throws remote exception") .that(thrown).hasMessageThat().contains(exceptionMessage); } @@ -848,7 +879,7 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { mTunerSessions[0].getParameters(parameterKeys); }); - assertWithMessage("Exception for getting parameters when HAL throws remote exception") + mExpect.withMessage("Exception for getting parameters when HAL throws remote exception") .that(thrown).hasMessageThat().contains(exceptionMessage); } @@ -894,6 +925,36 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase { } } + @Test + public void openSession_withNonNullAntennaState() throws Exception { + boolean antennaConnected = false; + android.hardware.radio.ITunerCallback callback = + mock(android.hardware.radio.ITunerCallback.class); + openAidlClients(/* numClients= */ 1); + mHalTunerCallback.onAntennaStateChange(antennaConnected); + verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onAntennaState(antennaConnected); + + mRadioModule.openSession(callback); + + verify(callback, CALLBACK_TIMEOUT).onAntennaState(antennaConnected); + } + + @Test + public void openSession_withNonNullCurrentProgramInfo() throws Exception { + openAidlClients(/* numClients= */ 1); + ProgramSelector initialSel = TestUtils.makeFmSelector(AM_FM_FREQUENCY_LIST[1]); + RadioManager.ProgramInfo tuneInfo = TestUtils.makeProgramInfo(initialSel, + SIGNAL_QUALITY); + mTunerSessions[0].tune(initialSel); + verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onCurrentProgramInfoChanged(tuneInfo); + android.hardware.radio.ITunerCallback callback = + mock(android.hardware.radio.ITunerCallback.class); + + mRadioModule.openSession(callback); + + verify(callback, CALLBACK_TIMEOUT).onCurrentProgramInfoChanged(tuneInfo); + } + private void openAidlClients(int numClients) throws Exception { mAidlTunerCallbackMocks = new android.hardware.radio.ITunerCallback[numClients]; mTunerSessions = new TunerSession[numClients]; diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java index 16c77d0c3c81..ecf47209a802 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/WindowExtensionsImpl.java @@ -24,6 +24,7 @@ import android.app.Application; import android.app.compat.CompatChanges; import android.content.Context; import android.hardware.devicestate.DeviceStateManager; +import android.os.SystemProperties; import android.util.Log; import androidx.annotation.NonNull; @@ -50,6 +51,11 @@ class WindowExtensionsImpl implements WindowExtensions { private static final String TAG = "WindowExtensionsImpl"; /** + * The value of the system property that indicates no override is set. + */ + private static final int NO_LEVEL_OVERRIDE = -1; + + /** * The min version of the WM Extensions that must be supported in the current platform version. */ @VisibleForTesting @@ -66,14 +72,30 @@ class WindowExtensionsImpl implements WindowExtensions { WindowExtensionsImpl() { mIsActivityEmbeddingEnabled = isActivityEmbeddingEnabled(); - Log.i(TAG, "Initializing Window Extensions, vendor API level=" + mVersion - + ", activity embedding enabled=" + mIsActivityEmbeddingEnabled); + + Log.i(TAG, generateLogMessage()); + } + + private String generateLogMessage() { + final StringBuilder logBuilder = new StringBuilder("Initializing Window Extensions, " + + "vendor API level=" + mVersion); + final int levelOverride = getLevelOverride(); + if (levelOverride != NO_LEVEL_OVERRIDE) { + logBuilder.append(", override to ").append(levelOverride); + } + logBuilder.append(", activity embedding enabled=").append(mIsActivityEmbeddingEnabled); + return logBuilder.toString(); } // TODO(b/241126279) Introduce constants to better version functionality @Override public int getVendorApiLevel() { - return mVersion; + final int levelOverride = getLevelOverride(); + return (levelOverride != NO_LEVEL_OVERRIDE) ? levelOverride : mVersion; + } + + private int getLevelOverride() { + return SystemProperties.getInt("persist.wm.debug.ext_version_override", NO_LEVEL_OVERRIDE); } @NonNull diff --git a/libs/WindowManager/Shell/res/values-cs/strings.xml b/libs/WindowManager/Shell/res/values-cs/strings.xml index aafb2e16b703..150a6e642529 100644 --- a/libs/WindowManager/Shell/res/values-cs/strings.xml +++ b/libs/WindowManager/Shell/res/values-cs/strings.xml @@ -84,7 +84,7 @@ <string name="notification_bubble_title" msgid="6082910224488253378">"Bublina"</string> <string name="manage_bubbles_text" msgid="7730624269650594419">"Spravovat"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Bublina byla zavřena."</string> - <string name="restart_button_description" msgid="4564728020654658478">"Klepnutím tuto aplikaci kvůli lepšímu zobrazení restartujete"</string> + <string name="restart_button_description" msgid="4564728020654658478">"Klepnutím tuto aplikaci restartujete kvůli lepšímu zobrazení"</string> <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Změnit v Nastavení poměr stran této aplikace"</string> <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Změnit poměr stran"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Problémy s fotoaparátem?\nKlepnutím vyřešíte"</string> diff --git a/libs/WindowManager/Shell/res/values-nb/strings.xml b/libs/WindowManager/Shell/res/values-nb/strings.xml index 9f03d8b5b178..6005be4fb5c9 100644 --- a/libs/WindowManager/Shell/res/values-nb/strings.xml +++ b/libs/WindowManager/Shell/res/values-nb/strings.xml @@ -85,7 +85,7 @@ <string name="manage_bubbles_text" msgid="7730624269650594419">"Administrer"</string> <string name="accessibility_bubble_dismissed" msgid="8367471990421247357">"Boblen er avvist."</string> <string name="restart_button_description" msgid="4564728020654658478">"Trykk for å starte denne appen på nytt og få en bedre visning"</string> - <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Endre høyde/bredde-forholdet for denne appen i innstillingene"</string> + <string name="user_aspect_ratio_settings_button_hint" msgid="734835849600713016">"Endre høyde/bredde-forholdet for denne appen i Innstillinger"</string> <string name="user_aspect_ratio_settings_button_description" msgid="4315566801697411684">"Endre høyde/bredde-forholdet"</string> <string name="camera_compat_treatment_suggested_button_description" msgid="8103916969024076767">"Har du kameraproblemer?\nTrykk for å tilpasse"</string> <string name="camera_compat_treatment_applied_button_description" msgid="2944157113330703897">"Ble ikke problemet løst?\nTrykk for å gå tilbake"</string> diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java index a7da07d013c1..972dce51e02b 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarExpandedView.java @@ -209,7 +209,7 @@ public class BubbleBarExpandedView extends FrameLayout implements BubbleTaskView @Override public void onDismissBubble(Bubble bubble) { - mManager.dismissBubble(bubble, Bubbles.DISMISS_USER_REMOVED); + mManager.dismissBubble(bubble, Bubbles.DISMISS_USER_GESTURE); } }); mHandleView.setOnClickListener(view -> { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java index 57e95d6fcfd6..f4ac5f260fcd 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayImeController.java @@ -538,8 +538,10 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged @Override public void onAnimationStart(Animator animation) { + ValueAnimator valueAnimator = (ValueAnimator) animation; + float value = (float) valueAnimator.getAnimatedValue(); SurfaceControl.Transaction t = mTransactionPool.acquire(); - t.setPosition(mImeSourceControl.getLeash(), x, startY); + t.setPosition(mImeSourceControl.getLeash(), x, value); if (DEBUG) { Slog.d(TAG, "onAnimationStart d:" + mDisplayId + " top:" + imeTop(hiddenY) + "->" + imeTop(shownY) @@ -549,7 +551,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged imeTop(shownY), mAnimationDirection == DIRECTION_SHOW, isFloating, t); mAnimateAlpha = (flags & ImePositionProcessor.IME_ANIMATION_NO_ALPHA) == 0; final float alpha = (mAnimateAlpha || isFloating) - ? (startY - hiddenY) / (shownY - hiddenY) + ? (value - hiddenY) / (shownY - hiddenY) : 1.f; t.setAlpha(mImeSourceControl.getLeash(), alpha); if (mAnimationDirection == DIRECTION_SHOW) { @@ -560,7 +562,7 @@ public class DisplayImeController implements DisplayController.OnDisplaysChanged if (DEBUG_IME_VISIBILITY) { EventLog.writeEvent(IMF_IME_REMOTE_ANIM_START, mStatsToken != null ? mStatsToken.getTag() : ImeTracker.TOKEN_NONE, - mDisplayId, mAnimationDirection, alpha, startY , endY, + mDisplayId, mAnimationDirection, alpha, value, endY, Objects.toString(mImeSourceControl.getLeash()), Objects.toString(mImeSourceControl.getInsetsHint()), Objects.toString(mImeSourceControl.getSurfacePosition()), diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java index 991fbafed296..609e5af5c5b0 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java @@ -87,6 +87,7 @@ import com.android.wm.shell.performance.PerfHintController; import com.android.wm.shell.recents.RecentTasks; import com.android.wm.shell.recents.RecentTasksController; import com.android.wm.shell.recents.RecentsTransitionHandler; +import com.android.wm.shell.recents.TaskStackTransitionObserver; import com.android.wm.shell.shared.DesktopModeStatus; import com.android.wm.shell.shared.ShellTransitions; import com.android.wm.shell.shared.annotations.ShellAnimationThread; @@ -619,12 +620,13 @@ public abstract class WMShellBaseModule { TaskStackListenerImpl taskStackListener, ActivityTaskManager activityTaskManager, Optional<DesktopModeTaskRepository> desktopModeTaskRepository, + TaskStackTransitionObserver taskStackTransitionObserver, @ShellMainThread ShellExecutor mainExecutor ) { return Optional.ofNullable( RecentTasksController.create(context, shellInit, shellController, shellCommandHandler, taskStackListener, activityTaskManager, - desktopModeTaskRepository, mainExecutor)); + desktopModeTaskRepository, taskStackTransitionObserver, mainExecutor)); } @BindsOptionalOf @@ -924,6 +926,19 @@ public abstract class WMShellBaseModule { } // + // Task Stack + // + + @WMSingleton + @Provides + static TaskStackTransitionObserver provideTaskStackTransitionObserver( + Lazy<Transitions> transitions, + ShellInit shellInit + ) { + return new TaskStackTransitionObserver(transitions, shellInit); + } + + // // Misc // diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java index 12bbd51b968d..87bd84017dee 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java @@ -121,9 +121,9 @@ import java.util.Optional; */ @Module( includes = { - WMShellBaseModule.class, - PipModule.class, - ShellBackAnimationModule.class, + WMShellBaseModule.class, + PipModule.class, + ShellBackAnimationModule.class, }) public abstract class WMShellModule { @@ -664,7 +664,8 @@ public abstract class WMShellModule { @Provides static Object provideIndependentShellComponentsToCreate( DragAndDropController dragAndDropController, - Optional<DesktopTasksTransitionObserver> desktopTasksTransitionObserverOptional) { + Optional<DesktopTasksTransitionObserver> desktopTasksTransitionObserverOptional + ) { return new Object(); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasksListener.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasksListener.aidl index 62d195efb381..245829ecafb3 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasksListener.aidl +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/IRecentTasksListener.aidl @@ -42,4 +42,7 @@ oneway interface IRecentTasksListener { * Called when a running task changes. */ void onRunningTaskChanged(in RunningTaskInfo taskInfo); -} + + /** A task has moved to front. */ + oneway void onTaskMovedToFront(in RunningTaskInfo taskInfo); +}
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/OWNERS new file mode 100644 index 000000000000..452644b05a2a --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/OWNERS @@ -0,0 +1,6 @@ +# WM shell sub-module task stack owners +uysalorhan@google.com +samcackett@google.com +alexchau@google.com +silvajordan@google.com +uwaisashraf@google.com
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java index 9d162462830f..03c8cf8cc795 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentTasksController.java @@ -20,6 +20,7 @@ import static android.app.ActivityTaskManager.INVALID_TASK_ID; import static android.content.pm.PackageManager.FEATURE_PC; import static com.android.window.flags.Flags.enableDesktopWindowingTaskbarRunningApps; +import static com.android.window.flags.Flags.enableTaskStackObserverInShell; import static com.android.wm.shell.sysui.ShellSharedConstants.KEY_EXTRA_SHELL_RECENT_TASKS; import android.app.ActivityManager; @@ -57,6 +58,7 @@ import com.android.wm.shell.shared.annotations.ShellMainThread; import com.android.wm.shell.sysui.ShellCommandHandler; import com.android.wm.shell.sysui.ShellController; import com.android.wm.shell.sysui.ShellInit; +import com.android.wm.shell.transition.Transitions; import com.android.wm.shell.util.GroupedRecentTaskInfo; import com.android.wm.shell.util.SplitBounds; @@ -73,7 +75,8 @@ import java.util.function.Consumer; * Manages the recent task list from the system, caching it as necessary. */ public class RecentTasksController implements TaskStackListenerCallback, - RemoteCallable<RecentTasksController>, DesktopModeTaskRepository.ActiveTasksListener { + RemoteCallable<RecentTasksController>, DesktopModeTaskRepository.ActiveTasksListener, + TaskStackTransitionObserver.TaskStackTransitionObserverListener { private static final String TAG = RecentTasksController.class.getSimpleName(); private final Context mContext; @@ -84,6 +87,7 @@ public class RecentTasksController implements TaskStackListenerCallback, private final TaskStackListenerImpl mTaskStackListener; private final RecentTasksImpl mImpl = new RecentTasksImpl(); private final ActivityTaskManager mActivityTaskManager; + private final TaskStackTransitionObserver mTaskStackTransitionObserver; private RecentsTransitionHandler mTransitionHandler = null; private IRecentTasksListener mListener; private final boolean mPcFeatureEnabled; @@ -112,13 +116,15 @@ public class RecentTasksController implements TaskStackListenerCallback, TaskStackListenerImpl taskStackListener, ActivityTaskManager activityTaskManager, Optional<DesktopModeTaskRepository> desktopModeTaskRepository, + TaskStackTransitionObserver taskStackTransitionObserver, @ShellMainThread ShellExecutor mainExecutor ) { if (!context.getResources().getBoolean(com.android.internal.R.bool.config_hasRecents)) { return null; } return new RecentTasksController(context, shellInit, shellController, shellCommandHandler, - taskStackListener, activityTaskManager, desktopModeTaskRepository, mainExecutor); + taskStackListener, activityTaskManager, desktopModeTaskRepository, + taskStackTransitionObserver, mainExecutor); } RecentTasksController(Context context, @@ -128,6 +134,7 @@ public class RecentTasksController implements TaskStackListenerCallback, TaskStackListenerImpl taskStackListener, ActivityTaskManager activityTaskManager, Optional<DesktopModeTaskRepository> desktopModeTaskRepository, + TaskStackTransitionObserver taskStackTransitionObserver, ShellExecutor mainExecutor) { mContext = context; mShellController = shellController; @@ -136,6 +143,7 @@ public class RecentTasksController implements TaskStackListenerCallback, mPcFeatureEnabled = mContext.getPackageManager().hasSystemFeature(FEATURE_PC); mTaskStackListener = taskStackListener; mDesktopModeTaskRepository = desktopModeTaskRepository; + mTaskStackTransitionObserver = taskStackTransitionObserver; mMainExecutor = mainExecutor; shellInit.addInitCallback(this::onInit, this); } @@ -154,6 +162,10 @@ public class RecentTasksController implements TaskStackListenerCallback, mShellCommandHandler.addDumpCallback(this::dump, this); mTaskStackListener.addListener(this); mDesktopModeTaskRepository.ifPresent(it -> it.addActiveTaskListener(this)); + if (Transitions.ENABLE_SHELL_TRANSITIONS) { + mTaskStackTransitionObserver.addTaskStackTransitionObserverListener(this, + mMainExecutor); + } } void setTransitionHandler(RecentsTransitionHandler handler) { @@ -267,6 +279,12 @@ public class RecentTasksController implements TaskStackListenerCallback, notifyRecentTasksChanged(); } + @Override + public void onTaskMovedToFrontThroughTransition( + ActivityManager.RunningTaskInfo runningTaskInfo) { + notifyTaskMovedToFront(runningTaskInfo); + } + @VisibleForTesting void notifyRecentTasksChanged() { ProtoLog.v(ShellProtoLogGroup.WM_SHELL_RECENT_TASKS, "Notify recent tasks changed"); @@ -328,6 +346,19 @@ public class RecentTasksController implements TaskStackListenerCallback, } } + private void notifyTaskMovedToFront(ActivityManager.RunningTaskInfo taskInfo) { + if (mListener == null + || !enableTaskStackObserverInShell() + || taskInfo.realActivity == null) { + return; + } + try { + mListener.onTaskMovedToFront(taskInfo); + } catch (RemoteException e) { + Slog.w(TAG, "Failed call onTaskMovedToFront", e); + } + } + private boolean shouldEnableRunningTasksForDesktopMode() { return mPcFeatureEnabled || (DesktopModeStatus.canEnterDesktopMode(mContext) @@ -464,6 +495,7 @@ public class RecentTasksController implements TaskStackListenerCallback, } return null; } + public void dump(@NonNull PrintWriter pw, String prefix) { final String innerPrefix = prefix + " "; pw.println(prefix + TAG); @@ -547,6 +579,11 @@ public class RecentTasksController implements TaskStackListenerCallback, public void onRunningTaskChanged(ActivityManager.RunningTaskInfo taskInfo) { mListener.call(l -> l.onRunningTaskChanged(taskInfo)); } + + @Override + public void onTaskMovedToFront(ActivityManager.RunningTaskInfo taskInfo) { + mListener.call(l -> l.onTaskMovedToFront(taskInfo)); + } }; public IRecentTasksImpl(RecentTasksController controller) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/TaskStackTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/TaskStackTransitionObserver.kt new file mode 100644 index 000000000000..7c5f10a5bcca --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/TaskStackTransitionObserver.kt @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.recents + +import android.app.ActivityManager.RunningTaskInfo +import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM +import android.os.IBinder +import android.util.ArrayMap +import android.view.SurfaceControl +import android.view.WindowManager +import android.window.TransitionInfo +import com.android.window.flags.Flags.enableTaskStackObserverInShell +import com.android.wm.shell.shared.TransitionUtil +import com.android.wm.shell.sysui.ShellInit +import com.android.wm.shell.transition.Transitions +import dagger.Lazy +import java.util.concurrent.Executor + +/** + * A [Transitions.TransitionObserver] that observes shell transitions and sends updates to listeners + * about task stack changes. + * + * TODO(346588978) Move split/pip signals here as well so that launcher don't need to handle it + */ +class TaskStackTransitionObserver( + private val transitions: Lazy<Transitions>, + shellInit: ShellInit +) : Transitions.TransitionObserver { + + private val transitionToTransitionChanges: MutableMap<IBinder, TransitionChanges> = + mutableMapOf() + private val taskStackTransitionObserverListeners = + ArrayMap<TaskStackTransitionObserverListener, Executor>() + + init { + if (Transitions.ENABLE_SHELL_TRANSITIONS) { + shellInit.addInitCallback(::onInit, this) + } + } + + fun onInit() { + transitions.get().registerObserver(this) + } + + override fun onTransitionReady( + transition: IBinder, + info: TransitionInfo, + startTransaction: SurfaceControl.Transaction, + finishTransaction: SurfaceControl.Transaction + ) { + if (enableTaskStackObserverInShell()) { + val taskInfoList = mutableListOf<RunningTaskInfo>() + val transitionTypeList = mutableListOf<Int>() + + for (change in info.changes) { + if (change.flags and TransitionInfo.FLAG_IS_WALLPAPER != 0) { + continue + } + + val taskInfo = change.taskInfo + if (taskInfo == null || taskInfo.taskId == -1) { + continue + } + + if (change.mode == WindowManager.TRANSIT_OPEN) { + change.taskInfo?.let { taskInfoList.add(it) } + transitionTypeList.add(change.mode) + } + } + transitionToTransitionChanges.put( + transition, + TransitionChanges(taskInfoList, transitionTypeList) + ) + } + } + + override fun onTransitionStarting(transition: IBinder) {} + + override fun onTransitionMerged(merged: IBinder, playing: IBinder) {} + + override fun onTransitionFinished(transition: IBinder, aborted: Boolean) { + val taskInfoList = + transitionToTransitionChanges.getOrDefault(transition, TransitionChanges()).taskInfoList + val typeList = + transitionToTransitionChanges + .getOrDefault(transition, TransitionChanges()) + .transitionTypeList + transitionToTransitionChanges.remove(transition) + + for ((index, taskInfo) in taskInfoList.withIndex()) { + if ( + TransitionUtil.isOpeningType(typeList[index]) && + taskInfo.windowingMode == WINDOWING_MODE_FREEFORM + ) { + notifyTaskStackTransitionObserverListeners(taskInfo) + } + } + } + + fun addTaskStackTransitionObserverListener( + taskStackTransitionObserverListener: TaskStackTransitionObserverListener, + executor: Executor + ) { + taskStackTransitionObserverListeners[taskStackTransitionObserverListener] = executor + } + + fun removeTaskStackTransitionObserverListener( + taskStackTransitionObserverListener: TaskStackTransitionObserverListener + ) { + taskStackTransitionObserverListeners.remove(taskStackTransitionObserverListener) + } + + private fun notifyTaskStackTransitionObserverListeners(taskInfo: RunningTaskInfo) { + taskStackTransitionObserverListeners.forEach { (listener, executor) -> + executor.execute { listener.onTaskMovedToFrontThroughTransition(taskInfo) } + } + } + + /** Listener to use to get updates regarding task stack from this observer */ + interface TaskStackTransitionObserverListener { + /** Called when a task is moved to front. */ + fun onTaskMovedToFrontThroughTransition(taskInfo: RunningTaskInfo) {} + } + + private data class TransitionChanges( + val taskInfoList: MutableList<RunningTaskInfo> = ArrayList(), + val transitionTypeList: MutableList<Int> = ArrayList() + ) +} diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/Android.bp b/libs/WindowManager/Shell/tests/flicker/splitscreen/Android.bp index f813b0d3b0b7..0fe7a16be851 100644 --- a/libs/WindowManager/Shell/tests/flicker/splitscreen/Android.bp +++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/Android.bp @@ -65,9 +65,11 @@ android_test { android_test { name: "WMShellFlickerTestsSplitScreenGroup2", + defaults: ["WMShellFlickerTestsDefault"], manifest: "AndroidManifest.xml", package_name: "com.android.wm.shell.flicker.splitscreen", instrumentation_target_package: "com.android.wm.shell.flicker.splitscreen", + test_config_template: "AndroidTestTemplate.xml", srcs: [ ":WMShellFlickerTestsSplitScreenBase-src", ":WMShellFlickerTestsSplitScreenGroup2-src", diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/MultipleShowImeRequestsInSplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/MultipleShowImeRequestsInSplitScreen.kt new file mode 100644 index 000000000000..dad5db94d062 --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/MultipleShowImeRequestsInSplitScreen.kt @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.flicker.splitscreen + +import android.platform.test.annotations.Presubmit +import android.tools.Rotation +import android.tools.flicker.junit.FlickerParametersRunnerFactory +import android.tools.flicker.legacy.FlickerBuilder +import android.tools.flicker.legacy.LegacyFlickerTest +import android.tools.flicker.legacy.LegacyFlickerTestFactory +import android.tools.traces.component.ComponentNameMatcher +import androidx.test.filters.RequiresDevice +import com.android.wm.shell.flicker.splitscreen.benchmark.MultipleShowImeRequestsInSplitScreenBenchmark +import com.android.wm.shell.flicker.utils.ICommonAssertions +import org.junit.FixMethodOrder +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +/** + * Test quick switch between two split pairs. + * + * To run this test: `atest WMShellFlickerTestsSplitScreenGroup2:MultipleShowImeRequestsInSplitScreen` + */ +@RequiresDevice +@RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +class MultipleShowImeRequestsInSplitScreen(override val flicker: LegacyFlickerTest) : + MultipleShowImeRequestsInSplitScreenBenchmark(flicker), ICommonAssertions { + override val transition: FlickerBuilder.() -> Unit + get() = { + defaultSetup(this) + defaultTeardown(this) + thisTransition(this) + } + + @Presubmit + @Test + fun imeLayerAlwaysVisible() = + flicker.assertLayers { + this.isVisible(ComponentNameMatcher.IME) + } + + companion object { + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams() = LegacyFlickerTestFactory.nonRotationTests( + supportedRotations = listOf(Rotation.ROTATION_0) + ) + } +} diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt index 90453640c91a..d34998815fca 100644 --- a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt +++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/UnlockKeyguardToSplitScreen.kt @@ -47,7 +47,7 @@ import org.junit.runners.Parameterized * To run this test: `atest WMShellFlickerTestsSplitScreen:UnlockKeyguardToSplitScreen` */ @RequiresDevice -@Postsubmit +@FlakyTest(bugId = 293578017) @RunWith(Parameterized::class) @Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) @FixMethodOrder(MethodSorters.NAME_ASCENDING) @@ -61,7 +61,6 @@ class UnlockKeyguardToSplitScreen(override val flicker: LegacyFlickerTest) : } @Test - @FlakyTest(bugId = 293578017) override fun visibleLayersShownMoreThanOneConsecutiveEntry() = super.visibleLayersShownMoreThanOneConsecutiveEntry() diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/MultipleShowImeRequestsInSplitScreenBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/MultipleShowImeRequestsInSplitScreenBenchmark.kt new file mode 100644 index 000000000000..249253185607 --- /dev/null +++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/MultipleShowImeRequestsInSplitScreenBenchmark.kt @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.flicker.splitscreen.benchmark + +import android.tools.flicker.junit.FlickerParametersRunnerFactory +import android.tools.flicker.legacy.FlickerBuilder +import android.tools.flicker.legacy.LegacyFlickerTest +import android.tools.flicker.legacy.LegacyFlickerTestFactory +import androidx.test.filters.RequiresDevice +import com.android.server.wm.flicker.helpers.ImeAppHelper +import com.android.wm.shell.flicker.utils.SplitScreenUtils +import org.junit.FixMethodOrder +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters +import org.junit.runners.Parameterized + +@RequiresDevice +@RunWith(Parameterized::class) +@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class) +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +abstract class MultipleShowImeRequestsInSplitScreenBenchmark( + override val flicker: LegacyFlickerTest +) : SplitScreenBase(flicker) { + override val primaryApp = ImeAppHelper(instrumentation) + override val defaultTeardown: FlickerBuilder.() -> Unit + get() = { + teardown { + primaryApp.closeIME(wmHelper) + super.defaultTeardown + } + } + + protected val thisTransition: FlickerBuilder.() -> Unit + get() = { + setup { + SplitScreenUtils.enterSplit( + wmHelper, + tapl, + device, + primaryApp, + secondaryApp, + flicker.scenario.startRotation + ) + // initially open the IME + primaryApp.openIME(wmHelper) + } + transitions { + for (i in 1..OPEN_IME_COUNT) { + primaryApp.openIME(wmHelper) + } + } + } + + companion object { + const val OPEN_IME_COUNT = 30 + + @Parameterized.Parameters(name = "{0}") + @JvmStatic + fun getParams() = LegacyFlickerTestFactory.nonRotationTests() + } +} diff --git a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SplitScreenBase.kt b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SplitScreenBase.kt index 4b106034b2b5..51074f634e30 100644 --- a/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SplitScreenBase.kt +++ b/libs/WindowManager/Shell/tests/flicker/splitscreen/src/com/android/wm/shell/flicker/splitscreen/benchmark/SplitScreenBase.kt @@ -25,7 +25,7 @@ import com.android.wm.shell.flicker.utils.SplitScreenUtils abstract class SplitScreenBase(flicker: LegacyFlickerTest) : BaseBenchmarkTest(flicker) { protected val context: Context = instrumentation.context - protected val primaryApp = SplitScreenUtils.getPrimary(instrumentation) + protected open val primaryApp = SplitScreenUtils.getPrimary(instrumentation) protected val secondaryApp = SplitScreenUtils.getSecondary(instrumentation) protected open val defaultSetup: FlickerBuilder.() -> Unit = { diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt index 5f6132ab9e58..0d3cd10e90df 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt @@ -40,14 +40,14 @@ import com.android.modules.utils.testing.ExtendedMockitoRule import com.android.wm.shell.common.ShellExecutor import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.EnterReason import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ExitReason -import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_HANDLE_MENU_BUTTON -import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_KEYBOARD_SHORTCUT -import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_TASK_DRAG -import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_UNKNOWN import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_ENTER_DESKTOP_FROM_APP_FROM_OVERVIEW import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_ENTER_DESKTOP_FROM_APP_HANDLE_MENU_BUTTON import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_ENTER_DESKTOP_FROM_KEYBOARD_SHORTCUT import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_ENTER_DESKTOP_FROM_UNKNOWN +import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_HANDLE_MENU_BUTTON +import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_KEYBOARD_SHORTCUT +import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_TASK_DRAG +import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_UNKNOWN import com.android.wm.shell.shared.DesktopModeStatus import com.android.wm.shell.sysui.ShellInit import com.android.wm.shell.transition.TransitionInfoBuilder @@ -78,409 +78,397 @@ import org.mockito.kotlin.verifyZeroInteractions @RunWith(AndroidTestingRunner::class) class DesktopModeLoggerTransitionObserverTest { - @JvmField - @Rule - val extendedMockitoRule = - ExtendedMockitoRule.Builder(this) - .mockStatic(DesktopModeEventLogger::class.java) - .mockStatic(DesktopModeStatus::class.java) - .build()!! - - @Mock lateinit var testExecutor: ShellExecutor - @Mock private lateinit var mockShellInit: ShellInit - @Mock private lateinit var transitions: Transitions - @Mock private lateinit var context: Context - - private lateinit var transitionObserver: DesktopModeLoggerTransitionObserver - private lateinit var shellInit: ShellInit - private lateinit var desktopModeEventLogger: DesktopModeEventLogger - - @Before - fun setup() { - doReturn(true).`when` { DesktopModeStatus.canEnterDesktopMode(any()) } - shellInit = Mockito.spy(ShellInit(testExecutor)) - desktopModeEventLogger = mock(DesktopModeEventLogger::class.java) - - transitionObserver = - DesktopModeLoggerTransitionObserver( - context, - mockShellInit, - transitions, - desktopModeEventLogger - ) - if (Transitions.ENABLE_SHELL_TRANSITIONS) { - val initRunnableCaptor = ArgumentCaptor.forClass(Runnable::class.java) - verify(mockShellInit) - .addInitCallback(initRunnableCaptor.capture(), same(transitionObserver)) - initRunnableCaptor.value.run() - } else { - transitionObserver.onInit() - } - } - - @Test - fun testRegistersObserverAtInit() { - verify(transitions).registerObserver(same(transitionObserver)) - } - - @Test - fun transitOpen_notFreeformWindow_doesNotLogTaskAddedOrSessionEnter() { - val change = createChange(TRANSIT_OPEN, createTaskInfo(1, WINDOWING_MODE_FULLSCREEN)) - val transitionInfo = TransitionInfoBuilder(TRANSIT_OPEN, 0).addChange(change).build() - - callOnTransitionReady(transitionInfo) - - verify(desktopModeEventLogger, never()).logSessionEnter(any(), any()) - verify(desktopModeEventLogger, never()).logTaskAdded(any(), any()) - } - - @Test - fun transitOpen_logTaskAddedAndEnterReasonAppFreeformIntent() { - val change = createChange(TRANSIT_OPEN, createTaskInfo(1, WINDOWING_MODE_FREEFORM)) - val transitionInfo = TransitionInfoBuilder(TRANSIT_OPEN, 0).addChange(change).build() - - callOnTransitionReady(transitionInfo) - val sessionId = transitionObserver.getLoggerSessionId() - - assertThat(sessionId).isNotNull() - verify(desktopModeEventLogger, times(1)) - .logSessionEnter(eq(sessionId!!), eq(EnterReason.APP_FREEFORM_INTENT)) - verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any()) - verifyZeroInteractions(desktopModeEventLogger) - } - - @Test - fun transitEndDragToDesktop_logTaskAddedAndEnterReasonAppHandleDrag() { - val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM)) - // task change is finalised when drag ends - val transitionInfo = - TransitionInfoBuilder(Transitions.TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP, 0) - .addChange(change) - .build() - - callOnTransitionReady(transitionInfo) - val sessionId = transitionObserver.getLoggerSessionId() - - assertThat(sessionId).isNotNull() - verify(desktopModeEventLogger, times(1)) - .logSessionEnter(eq(sessionId!!), eq(EnterReason.APP_HANDLE_DRAG)) - verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any()) - verifyZeroInteractions(desktopModeEventLogger) - } - - @Test - fun transitEnterDesktopByButtonTap_logTaskAddedAndEnterReasonButtonTap() { - val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM)) - val transitionInfo = - TransitionInfoBuilder(TRANSIT_ENTER_DESKTOP_FROM_APP_HANDLE_MENU_BUTTON, 0) - .addChange(change) - .build() - - callOnTransitionReady(transitionInfo) - val sessionId = transitionObserver.getLoggerSessionId() - - assertThat(sessionId).isNotNull() - verify(desktopModeEventLogger, times(1)) - .logSessionEnter(eq(sessionId!!), eq(EnterReason.APP_HANDLE_MENU_BUTTON)) - verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any()) - verifyZeroInteractions(desktopModeEventLogger) - } - - @Test - fun transitEnterDesktopFromAppFromOverview_logTaskAddedAndEnterReasonUnknown() { - val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM)) - val transitionInfo = - TransitionInfoBuilder(TRANSIT_ENTER_DESKTOP_FROM_APP_FROM_OVERVIEW, 0) - .addChange(change) - .build() - - callOnTransitionReady(transitionInfo) - val sessionId = transitionObserver.getLoggerSessionId() - - assertThat(sessionId).isNotNull() - verify(desktopModeEventLogger, times(1)) - .logSessionEnter(eq(sessionId!!), eq(EnterReason.APP_FROM_OVERVIEW)) - verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any()) - verifyZeroInteractions(desktopModeEventLogger) - } - - @Test - fun transitEnterDesktopFromKeyboardShortcut_logTaskAddedAndEnterReasonKeyboardShortcut() { - val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM)) - val transitionInfo = - TransitionInfoBuilder(TRANSIT_ENTER_DESKTOP_FROM_KEYBOARD_SHORTCUT, 0) - .addChange(change) - .build() - - callOnTransitionReady(transitionInfo) - val sessionId = transitionObserver.getLoggerSessionId() - - assertThat(sessionId).isNotNull() - verify(desktopModeEventLogger, times(1)) - .logSessionEnter(eq(sessionId!!), eq(EnterReason.KEYBOARD_SHORTCUT_ENTER)) - verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any()) - verifyZeroInteractions(desktopModeEventLogger) - } - - @Test - fun transitEnterDesktopFromUnknown_logTaskAddedAndEnterReasonUnknown() { - val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM)) - val transitionInfo = - TransitionInfoBuilder(TRANSIT_ENTER_DESKTOP_FROM_UNKNOWN, 0).addChange(change).build() - - callOnTransitionReady(transitionInfo) - val sessionId = transitionObserver.getLoggerSessionId() - - assertThat(sessionId).isNotNull() - verify(desktopModeEventLogger, times(1)) - .logSessionEnter(eq(sessionId!!), eq(EnterReason.UNKNOWN_ENTER)) - verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any()) - verifyZeroInteractions(desktopModeEventLogger) - } - - @Test - fun transitWake_logTaskAddedAndEnterReasonScreenOn() { - val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM)) - val transitionInfo = TransitionInfoBuilder(TRANSIT_WAKE, 0).addChange(change).build() - - callOnTransitionReady(transitionInfo) - val sessionId = transitionObserver.getLoggerSessionId() - - assertThat(sessionId).isNotNull() - verify(desktopModeEventLogger, times(1)) - .logSessionEnter(eq(sessionId!!), eq(EnterReason.SCREEN_ON)) - verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any()) - verifyZeroInteractions(desktopModeEventLogger) - } - - @Test - fun transitSleep_logTaskAddedAndExitReasonScreenOff_sessionIdNull() { - val sessionId = 1 - // add a freeform task - transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM)) - transitionObserver.setLoggerSessionId(sessionId) - - val transitionInfo = TransitionInfoBuilder(TRANSIT_SLEEP).build() - callOnTransitionReady(transitionInfo) - - verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any()) - verify(desktopModeEventLogger, times(1)) - .logSessionExit(eq(sessionId), eq(ExitReason.SCREEN_OFF)) - verifyZeroInteractions(desktopModeEventLogger) - assertThat(transitionObserver.getLoggerSessionId()).isNull() - } - - @Test - fun transitExitDesktopTaskDrag_logTaskRemovedAndExitReasonDragToExit_sessionIdNull() { - val sessionId = 1 - // add a freeform task - transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM)) - transitionObserver.setLoggerSessionId(sessionId) - - // window mode changing from FREEFORM to FULLSCREEN - val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FULLSCREEN)) - val transitionInfo = - TransitionInfoBuilder(TRANSIT_EXIT_DESKTOP_MODE_TASK_DRAG).addChange(change).build() - callOnTransitionReady(transitionInfo) - - verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any()) - verify(desktopModeEventLogger, times(1)) - .logSessionExit(eq(sessionId), eq(ExitReason.DRAG_TO_EXIT)) - verifyZeroInteractions(desktopModeEventLogger) - assertThat(transitionObserver.getLoggerSessionId()).isNull() - } - - @Test - fun transitExitDesktopAppHandleButton_logTaskRemovedAndExitReasonButton_sessionIdNull() { - val sessionId = 1 - // add a freeform task - transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM)) - transitionObserver.setLoggerSessionId(sessionId) - - // window mode changing from FREEFORM to FULLSCREEN - val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FULLSCREEN)) - val transitionInfo = - TransitionInfoBuilder(TRANSIT_EXIT_DESKTOP_MODE_HANDLE_MENU_BUTTON) - .addChange(change) - .build() - callOnTransitionReady(transitionInfo) - - verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any()) - verify(desktopModeEventLogger, times(1)) - .logSessionExit(eq(sessionId), eq(ExitReason.APP_HANDLE_MENU_BUTTON_EXIT)) - verifyZeroInteractions(desktopModeEventLogger) - assertThat(transitionObserver.getLoggerSessionId()).isNull() - } - - @Test - fun transitExitDesktopUsingKeyboard_logTaskRemovedAndExitReasonKeyboard_sessionIdNull() { - val sessionId = 1 - // add a freeform task - transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM)) - transitionObserver.setLoggerSessionId(sessionId) - - // window mode changing from FREEFORM to FULLSCREEN - val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FULLSCREEN)) - val transitionInfo = - TransitionInfoBuilder(TRANSIT_EXIT_DESKTOP_MODE_KEYBOARD_SHORTCUT) - .addChange(change) - .build() - callOnTransitionReady(transitionInfo) - - verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any()) - verify(desktopModeEventLogger, times(1)) - .logSessionExit(eq(sessionId), eq(ExitReason.KEYBOARD_SHORTCUT_EXIT)) - verifyZeroInteractions(desktopModeEventLogger) - assertThat(transitionObserver.getLoggerSessionId()).isNull() - } - - @Test - fun transitExitDesktopUnknown_logTaskRemovedAndExitReasonUnknown_sessionIdNull() { - val sessionId = 1 - // add a freeform task - transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM)) - transitionObserver.setLoggerSessionId(sessionId) - - // window mode changing from FREEFORM to FULLSCREEN - val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FULLSCREEN)) - val transitionInfo = - TransitionInfoBuilder(TRANSIT_EXIT_DESKTOP_MODE_UNKNOWN).addChange(change).build() - callOnTransitionReady(transitionInfo) - - verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any()) - verify(desktopModeEventLogger, times(1)) - .logSessionExit(eq(sessionId), eq(ExitReason.UNKNOWN_EXIT)) - verifyZeroInteractions(desktopModeEventLogger) - assertThat(transitionObserver.getLoggerSessionId()).isNull() + @JvmField + @Rule + val extendedMockitoRule = + ExtendedMockitoRule.Builder(this) + .mockStatic(DesktopModeEventLogger::class.java) + .mockStatic(DesktopModeStatus::class.java) + .build()!! + + @Mock lateinit var testExecutor: ShellExecutor + @Mock private lateinit var mockShellInit: ShellInit + @Mock private lateinit var transitions: Transitions + @Mock private lateinit var context: Context + + private lateinit var transitionObserver: DesktopModeLoggerTransitionObserver + private lateinit var shellInit: ShellInit + private lateinit var desktopModeEventLogger: DesktopModeEventLogger + + @Before + fun setup() { + doReturn(true).`when` { DesktopModeStatus.canEnterDesktopMode(any()) } + shellInit = Mockito.spy(ShellInit(testExecutor)) + desktopModeEventLogger = mock(DesktopModeEventLogger::class.java) + + transitionObserver = + DesktopModeLoggerTransitionObserver( + context, mockShellInit, transitions, desktopModeEventLogger) + if (Transitions.ENABLE_SHELL_TRANSITIONS) { + val initRunnableCaptor = ArgumentCaptor.forClass(Runnable::class.java) + verify(mockShellInit).addInitCallback(initRunnableCaptor.capture(), same(transitionObserver)) + initRunnableCaptor.value.run() + } else { + transitionObserver.onInit() } - - @Test - fun transitToFrontWithFlagRecents_logTaskRemovedAndExitReasonOverview_sessionIdNull() { - val sessionId = 1 - // add a freeform task - transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM)) - transitionObserver.setLoggerSessionId(sessionId) - - // recents transition - val change = createChange(TRANSIT_TO_BACK, createTaskInfo(1, WINDOWING_MODE_FREEFORM)) - val transitionInfo = - TransitionInfoBuilder(TRANSIT_TO_FRONT, TRANSIT_FLAG_IS_RECENTS) - .addChange(change) - .build() - callOnTransitionReady(transitionInfo) - - verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any()) - verify(desktopModeEventLogger, times(1)) - .logSessionExit(eq(sessionId), eq(ExitReason.RETURN_HOME_OR_OVERVIEW)) - verifyZeroInteractions(desktopModeEventLogger) - assertThat(transitionObserver.getLoggerSessionId()).isNull() - } - - @Test - fun transitClose_logTaskRemovedAndExitReasonTaskFinished_sessionIdNull() { - val sessionId = 1 - // add a freeform task - transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM)) - transitionObserver.setLoggerSessionId(sessionId) - - // task closing - val change = createChange(TRANSIT_CLOSE, createTaskInfo(1, WINDOWING_MODE_FULLSCREEN)) - val transitionInfo = TransitionInfoBuilder(TRANSIT_CLOSE).addChange(change).build() - callOnTransitionReady(transitionInfo) - - verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any()) - verify(desktopModeEventLogger, times(1)) - .logSessionExit(eq(sessionId), eq(ExitReason.TASK_FINISHED)) - verifyZeroInteractions(desktopModeEventLogger) - assertThat(transitionObserver.getLoggerSessionId()).isNull() - } - - @Test - fun sessionExitByRecents_cancelledAnimation_sessionRestored() { - val sessionId = 1 - // add a freeform task to an existing session - transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM)) - transitionObserver.setLoggerSessionId(sessionId) - - // recents transition sent freeform window to back - val change = createChange(TRANSIT_TO_BACK, createTaskInfo(1, WINDOWING_MODE_FREEFORM)) - val transitionInfo1 = - TransitionInfoBuilder(TRANSIT_TO_FRONT, TRANSIT_FLAG_IS_RECENTS) - .addChange(change) - .build() - callOnTransitionReady(transitionInfo1) - verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any()) - verify(desktopModeEventLogger, times(1)) - .logSessionExit(eq(sessionId), eq(ExitReason.RETURN_HOME_OR_OVERVIEW)) - assertThat(transitionObserver.getLoggerSessionId()).isNull() - - val transitionInfo2 = TransitionInfoBuilder(TRANSIT_NONE).build() - callOnTransitionReady(transitionInfo2) - - verify(desktopModeEventLogger, times(1)).logSessionEnter(any(), any()) - verify(desktopModeEventLogger, times(1)).logTaskAdded(any(), any()) - } - - @Test - fun sessionAlreadyStarted_newFreeformTaskAdded_logsTaskAdded() { - val sessionId = 1 - // add an existing freeform task - transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM)) - transitionObserver.setLoggerSessionId(sessionId) - - // new freeform task added - val change = createChange(TRANSIT_OPEN, createTaskInfo(2, WINDOWING_MODE_FREEFORM)) - val transitionInfo = TransitionInfoBuilder(TRANSIT_OPEN, 0).addChange(change).build() - callOnTransitionReady(transitionInfo) - - verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any()) - verify(desktopModeEventLogger, never()).logSessionEnter(any(), any()) - } - - @Test - fun sessionAlreadyStarted_freeformTaskRemoved_logsTaskRemoved() { - val sessionId = 1 - // add two existing freeform tasks - transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM)) - transitionObserver.addTaskInfosToCachedMap(createTaskInfo(2, WINDOWING_MODE_FREEFORM)) - transitionObserver.setLoggerSessionId(sessionId) - - // new freeform task added - val change = createChange(TRANSIT_CLOSE, createTaskInfo(2, WINDOWING_MODE_FREEFORM)) - val transitionInfo = TransitionInfoBuilder(TRANSIT_CLOSE, 0).addChange(change).build() - callOnTransitionReady(transitionInfo) - - verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any()) - verify(desktopModeEventLogger, never()).logSessionExit(any(), any()) - } - - /** Simulate calling the onTransitionReady() method */ - private fun callOnTransitionReady(transitionInfo: TransitionInfo) { - val transition = mock(IBinder::class.java) - val startT = mock(SurfaceControl.Transaction::class.java) - val finishT = mock(SurfaceControl.Transaction::class.java) - - transitionObserver.onTransitionReady(transition, transitionInfo, startT, finishT) + } + + @Test + fun testRegistersObserverAtInit() { + verify(transitions).registerObserver(same(transitionObserver)) + } + + @Test + fun transitOpen_notFreeformWindow_doesNotLogTaskAddedOrSessionEnter() { + val change = createChange(TRANSIT_OPEN, createTaskInfo(1, WINDOWING_MODE_FULLSCREEN)) + val transitionInfo = TransitionInfoBuilder(TRANSIT_OPEN, 0).addChange(change).build() + + callOnTransitionReady(transitionInfo) + + verify(desktopModeEventLogger, never()).logSessionEnter(any(), any()) + verify(desktopModeEventLogger, never()).logTaskAdded(any(), any()) + } + + @Test + fun transitOpen_logTaskAddedAndEnterReasonAppFreeformIntent() { + val change = createChange(TRANSIT_OPEN, createTaskInfo(1, WINDOWING_MODE_FREEFORM)) + val transitionInfo = TransitionInfoBuilder(TRANSIT_OPEN, 0).addChange(change).build() + + callOnTransitionReady(transitionInfo) + val sessionId = transitionObserver.getLoggerSessionId() + + assertThat(sessionId).isNotNull() + verify(desktopModeEventLogger, times(1)) + .logSessionEnter(eq(sessionId!!), eq(EnterReason.APP_FREEFORM_INTENT)) + verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any()) + verifyZeroInteractions(desktopModeEventLogger) + } + + @Test + fun transitEndDragToDesktop_logTaskAddedAndEnterReasonAppHandleDrag() { + val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM)) + // task change is finalised when drag ends + val transitionInfo = + TransitionInfoBuilder(Transitions.TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP, 0) + .addChange(change) + .build() + + callOnTransitionReady(transitionInfo) + val sessionId = transitionObserver.getLoggerSessionId() + + assertThat(sessionId).isNotNull() + verify(desktopModeEventLogger, times(1)) + .logSessionEnter(eq(sessionId!!), eq(EnterReason.APP_HANDLE_DRAG)) + verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any()) + verifyZeroInteractions(desktopModeEventLogger) + } + + @Test + fun transitEnterDesktopByButtonTap_logTaskAddedAndEnterReasonButtonTap() { + val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM)) + val transitionInfo = + TransitionInfoBuilder(TRANSIT_ENTER_DESKTOP_FROM_APP_HANDLE_MENU_BUTTON, 0) + .addChange(change) + .build() + + callOnTransitionReady(transitionInfo) + val sessionId = transitionObserver.getLoggerSessionId() + + assertThat(sessionId).isNotNull() + verify(desktopModeEventLogger, times(1)) + .logSessionEnter(eq(sessionId!!), eq(EnterReason.APP_HANDLE_MENU_BUTTON)) + verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any()) + verifyZeroInteractions(desktopModeEventLogger) + } + + @Test + fun transitEnterDesktopFromAppFromOverview_logTaskAddedAndEnterReasonUnknown() { + val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM)) + val transitionInfo = + TransitionInfoBuilder(TRANSIT_ENTER_DESKTOP_FROM_APP_FROM_OVERVIEW, 0) + .addChange(change) + .build() + + callOnTransitionReady(transitionInfo) + val sessionId = transitionObserver.getLoggerSessionId() + + assertThat(sessionId).isNotNull() + verify(desktopModeEventLogger, times(1)) + .logSessionEnter(eq(sessionId!!), eq(EnterReason.APP_FROM_OVERVIEW)) + verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any()) + verifyZeroInteractions(desktopModeEventLogger) + } + + @Test + fun transitEnterDesktopFromKeyboardShortcut_logTaskAddedAndEnterReasonKeyboardShortcut() { + val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM)) + val transitionInfo = + TransitionInfoBuilder(TRANSIT_ENTER_DESKTOP_FROM_KEYBOARD_SHORTCUT, 0) + .addChange(change) + .build() + + callOnTransitionReady(transitionInfo) + val sessionId = transitionObserver.getLoggerSessionId() + + assertThat(sessionId).isNotNull() + verify(desktopModeEventLogger, times(1)) + .logSessionEnter(eq(sessionId!!), eq(EnterReason.KEYBOARD_SHORTCUT_ENTER)) + verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any()) + verifyZeroInteractions(desktopModeEventLogger) + } + + @Test + fun transitEnterDesktopFromUnknown_logTaskAddedAndEnterReasonUnknown() { + val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM)) + val transitionInfo = + TransitionInfoBuilder(TRANSIT_ENTER_DESKTOP_FROM_UNKNOWN, 0).addChange(change).build() + + callOnTransitionReady(transitionInfo) + val sessionId = transitionObserver.getLoggerSessionId() + + assertThat(sessionId).isNotNull() + verify(desktopModeEventLogger, times(1)) + .logSessionEnter(eq(sessionId!!), eq(EnterReason.UNKNOWN_ENTER)) + verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any()) + verifyZeroInteractions(desktopModeEventLogger) + } + + @Test + fun transitWake_logTaskAddedAndEnterReasonScreenOn() { + val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FREEFORM)) + val transitionInfo = TransitionInfoBuilder(TRANSIT_WAKE, 0).addChange(change).build() + + callOnTransitionReady(transitionInfo) + val sessionId = transitionObserver.getLoggerSessionId() + + assertThat(sessionId).isNotNull() + verify(desktopModeEventLogger, times(1)) + .logSessionEnter(eq(sessionId!!), eq(EnterReason.SCREEN_ON)) + verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any()) + verifyZeroInteractions(desktopModeEventLogger) + } + + @Test + fun transitSleep_logTaskAddedAndExitReasonScreenOff_sessionIdNull() { + val sessionId = 1 + // add a freeform task + transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM)) + transitionObserver.setLoggerSessionId(sessionId) + + val transitionInfo = TransitionInfoBuilder(TRANSIT_SLEEP).build() + callOnTransitionReady(transitionInfo) + + verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any()) + verify(desktopModeEventLogger, times(1)) + .logSessionExit(eq(sessionId), eq(ExitReason.SCREEN_OFF)) + verifyZeroInteractions(desktopModeEventLogger) + assertThat(transitionObserver.getLoggerSessionId()).isNull() + } + + @Test + fun transitExitDesktopTaskDrag_logTaskRemovedAndExitReasonDragToExit_sessionIdNull() { + val sessionId = 1 + // add a freeform task + transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM)) + transitionObserver.setLoggerSessionId(sessionId) + + // window mode changing from FREEFORM to FULLSCREEN + val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FULLSCREEN)) + val transitionInfo = + TransitionInfoBuilder(TRANSIT_EXIT_DESKTOP_MODE_TASK_DRAG).addChange(change).build() + callOnTransitionReady(transitionInfo) + + verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any()) + verify(desktopModeEventLogger, times(1)) + .logSessionExit(eq(sessionId), eq(ExitReason.DRAG_TO_EXIT)) + verifyZeroInteractions(desktopModeEventLogger) + assertThat(transitionObserver.getLoggerSessionId()).isNull() + } + + @Test + fun transitExitDesktopAppHandleButton_logTaskRemovedAndExitReasonButton_sessionIdNull() { + val sessionId = 1 + // add a freeform task + transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM)) + transitionObserver.setLoggerSessionId(sessionId) + + // window mode changing from FREEFORM to FULLSCREEN + val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FULLSCREEN)) + val transitionInfo = + TransitionInfoBuilder(TRANSIT_EXIT_DESKTOP_MODE_HANDLE_MENU_BUTTON) + .addChange(change) + .build() + callOnTransitionReady(transitionInfo) + + verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any()) + verify(desktopModeEventLogger, times(1)) + .logSessionExit(eq(sessionId), eq(ExitReason.APP_HANDLE_MENU_BUTTON_EXIT)) + verifyZeroInteractions(desktopModeEventLogger) + assertThat(transitionObserver.getLoggerSessionId()).isNull() + } + + @Test + fun transitExitDesktopUsingKeyboard_logTaskRemovedAndExitReasonKeyboard_sessionIdNull() { + val sessionId = 1 + // add a freeform task + transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM)) + transitionObserver.setLoggerSessionId(sessionId) + + // window mode changing from FREEFORM to FULLSCREEN + val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FULLSCREEN)) + val transitionInfo = + TransitionInfoBuilder(TRANSIT_EXIT_DESKTOP_MODE_KEYBOARD_SHORTCUT).addChange(change).build() + callOnTransitionReady(transitionInfo) + + verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any()) + verify(desktopModeEventLogger, times(1)) + .logSessionExit(eq(sessionId), eq(ExitReason.KEYBOARD_SHORTCUT_EXIT)) + verifyZeroInteractions(desktopModeEventLogger) + assertThat(transitionObserver.getLoggerSessionId()).isNull() + } + + @Test + fun transitExitDesktopUnknown_logTaskRemovedAndExitReasonUnknown_sessionIdNull() { + val sessionId = 1 + // add a freeform task + transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM)) + transitionObserver.setLoggerSessionId(sessionId) + + // window mode changing from FREEFORM to FULLSCREEN + val change = createChange(TRANSIT_TO_FRONT, createTaskInfo(1, WINDOWING_MODE_FULLSCREEN)) + val transitionInfo = + TransitionInfoBuilder(TRANSIT_EXIT_DESKTOP_MODE_UNKNOWN).addChange(change).build() + callOnTransitionReady(transitionInfo) + + verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any()) + verify(desktopModeEventLogger, times(1)) + .logSessionExit(eq(sessionId), eq(ExitReason.UNKNOWN_EXIT)) + verifyZeroInteractions(desktopModeEventLogger) + assertThat(transitionObserver.getLoggerSessionId()).isNull() + } + + @Test + fun transitToFrontWithFlagRecents_logTaskRemovedAndExitReasonOverview_sessionIdNull() { + val sessionId = 1 + // add a freeform task + transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM)) + transitionObserver.setLoggerSessionId(sessionId) + + // recents transition + val change = createChange(TRANSIT_TO_BACK, createTaskInfo(1, WINDOWING_MODE_FREEFORM)) + val transitionInfo = + TransitionInfoBuilder(TRANSIT_TO_FRONT, TRANSIT_FLAG_IS_RECENTS).addChange(change).build() + callOnTransitionReady(transitionInfo) + + verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any()) + verify(desktopModeEventLogger, times(1)) + .logSessionExit(eq(sessionId), eq(ExitReason.RETURN_HOME_OR_OVERVIEW)) + verifyZeroInteractions(desktopModeEventLogger) + assertThat(transitionObserver.getLoggerSessionId()).isNull() + } + + @Test + fun transitClose_logTaskRemovedAndExitReasonTaskFinished_sessionIdNull() { + val sessionId = 1 + // add a freeform task + transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM)) + transitionObserver.setLoggerSessionId(sessionId) + + // task closing + val change = createChange(TRANSIT_CLOSE, createTaskInfo(1, WINDOWING_MODE_FULLSCREEN)) + val transitionInfo = TransitionInfoBuilder(TRANSIT_CLOSE).addChange(change).build() + callOnTransitionReady(transitionInfo) + + verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any()) + verify(desktopModeEventLogger, times(1)) + .logSessionExit(eq(sessionId), eq(ExitReason.TASK_FINISHED)) + verifyZeroInteractions(desktopModeEventLogger) + assertThat(transitionObserver.getLoggerSessionId()).isNull() + } + + @Test + fun sessionExitByRecents_cancelledAnimation_sessionRestored() { + val sessionId = 1 + // add a freeform task to an existing session + transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM)) + transitionObserver.setLoggerSessionId(sessionId) + + // recents transition sent freeform window to back + val change = createChange(TRANSIT_TO_BACK, createTaskInfo(1, WINDOWING_MODE_FREEFORM)) + val transitionInfo1 = + TransitionInfoBuilder(TRANSIT_TO_FRONT, TRANSIT_FLAG_IS_RECENTS).addChange(change).build() + callOnTransitionReady(transitionInfo1) + verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any()) + verify(desktopModeEventLogger, times(1)) + .logSessionExit(eq(sessionId), eq(ExitReason.RETURN_HOME_OR_OVERVIEW)) + assertThat(transitionObserver.getLoggerSessionId()).isNull() + + val transitionInfo2 = TransitionInfoBuilder(TRANSIT_NONE).build() + callOnTransitionReady(transitionInfo2) + + verify(desktopModeEventLogger, times(1)).logSessionEnter(any(), any()) + verify(desktopModeEventLogger, times(1)).logTaskAdded(any(), any()) + } + + @Test + fun sessionAlreadyStarted_newFreeformTaskAdded_logsTaskAdded() { + val sessionId = 1 + // add an existing freeform task + transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM)) + transitionObserver.setLoggerSessionId(sessionId) + + // new freeform task added + val change = createChange(TRANSIT_OPEN, createTaskInfo(2, WINDOWING_MODE_FREEFORM)) + val transitionInfo = TransitionInfoBuilder(TRANSIT_OPEN, 0).addChange(change).build() + callOnTransitionReady(transitionInfo) + + verify(desktopModeEventLogger, times(1)).logTaskAdded(eq(sessionId), any()) + verify(desktopModeEventLogger, never()).logSessionEnter(any(), any()) + } + + @Test + fun sessionAlreadyStarted_freeformTaskRemoved_logsTaskRemoved() { + val sessionId = 1 + // add two existing freeform tasks + transitionObserver.addTaskInfosToCachedMap(createTaskInfo(1, WINDOWING_MODE_FREEFORM)) + transitionObserver.addTaskInfosToCachedMap(createTaskInfo(2, WINDOWING_MODE_FREEFORM)) + transitionObserver.setLoggerSessionId(sessionId) + + // new freeform task added + val change = createChange(TRANSIT_CLOSE, createTaskInfo(2, WINDOWING_MODE_FREEFORM)) + val transitionInfo = TransitionInfoBuilder(TRANSIT_CLOSE, 0).addChange(change).build() + callOnTransitionReady(transitionInfo) + + verify(desktopModeEventLogger, times(1)).logTaskRemoved(eq(sessionId), any()) + verify(desktopModeEventLogger, never()).logSessionExit(any(), any()) + } + + /** Simulate calling the onTransitionReady() method */ + private fun callOnTransitionReady(transitionInfo: TransitionInfo) { + val transition = mock(IBinder::class.java) + val startT = mock(SurfaceControl.Transaction::class.java) + val finishT = mock(SurfaceControl.Transaction::class.java) + + transitionObserver.onTransitionReady(transition, transitionInfo, startT, finishT) + } + + companion object { + fun createTaskInfo(taskId: Int, windowMode: Int): ActivityManager.RunningTaskInfo { + val taskInfo = ActivityManager.RunningTaskInfo() + taskInfo.taskId = taskId + taskInfo.configuration.windowConfiguration.windowingMode = windowMode + + return taskInfo } - companion object { - fun createTaskInfo(taskId: Int, windowMode: Int): ActivityManager.RunningTaskInfo { - val taskInfo = ActivityManager.RunningTaskInfo() - taskInfo.taskId = taskId - taskInfo.configuration.windowConfiguration.windowingMode = windowMode - - return taskInfo - } - - fun createChange(mode: Int, taskInfo: ActivityManager.RunningTaskInfo): Change { - val change = - Change( - WindowContainerToken(mock(IWindowContainerToken::class.java)), - mock(SurfaceControl::class.java) - ) - change.mode = mode - change.taskInfo = taskInfo - return change - } + fun createChange(mode: Int, taskInfo: ActivityManager.RunningTaskInfo): Change { + val change = + Change( + WindowContainerToken(mock(IWindowContainerToken::class.java)), + mock(SurfaceControl::class.java)) + change.mode = mode + change.taskInfo = taskInfo + return change } + } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java index 56c4ceacc8ab..e291c0e1a151 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/RecentTasksControllerTest.java @@ -113,6 +113,8 @@ public class RecentTasksControllerTest extends ShellTestCase { private DisplayInsetsController mDisplayInsetsController; @Mock private IRecentTasksListener mRecentTasksListener; + @Mock + private TaskStackTransitionObserver mTaskStackTransitionObserver; @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); @@ -139,7 +141,8 @@ public class RecentTasksControllerTest extends ShellTestCase { mDisplayInsetsController, mMainExecutor)); mRecentTasksControllerReal = new RecentTasksController(mContext, mShellInit, mShellController, mShellCommandHandler, mTaskStackListener, mActivityTaskManager, - Optional.of(mDesktopModeTaskRepository), mMainExecutor); + Optional.of(mDesktopModeTaskRepository), mTaskStackTransitionObserver, + mMainExecutor); mRecentTasksController = spy(mRecentTasksControllerReal); mShellTaskOrganizer = new ShellTaskOrganizer(mShellInit, mShellCommandHandler, null /* sizeCompatUI */, Optional.empty(), Optional.of(mRecentTasksController), @@ -557,6 +560,30 @@ public class RecentTasksControllerTest extends ShellTestCase { } @Test + @EnableFlags(Flags.FLAG_ENABLE_TASK_STACK_OBSERVER_IN_SHELL) + public void onTaskMovedToFront_TaskStackObserverEnabled_triggersOnTaskMovedToFront() + throws Exception { + mRecentTasksControllerReal.registerRecentTasksListener(mRecentTasksListener); + ActivityManager.RunningTaskInfo taskInfo = makeRunningTaskInfo(/* taskId= */10); + + mRecentTasksControllerReal.onTaskMovedToFrontThroughTransition(taskInfo); + + verify(mRecentTasksListener).onTaskMovedToFront(taskInfo); + } + + @Test + @DisableFlags(Flags.FLAG_ENABLE_TASK_STACK_OBSERVER_IN_SHELL) + public void onTaskMovedToFront_TaskStackObserverEnabled_doesNotTriggersOnTaskMovedToFront() + throws Exception { + mRecentTasksControllerReal.registerRecentTasksListener(mRecentTasksListener); + ActivityManager.RunningTaskInfo taskInfo = makeRunningTaskInfo(/* taskId= */10); + + mRecentTasksControllerReal.onTaskMovedToFront(taskInfo); + + verify(mRecentTasksListener, never()).onTaskMovedToFront(any()); + } + + @Test public void getNullSplitBoundsNonSplitTask() { SplitBounds sb = mRecentTasksController.getSplitBoundsForTaskId(3); assertNull("splitBounds should be null for non-split task", sb); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/TaskStackTransitionObserverTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/TaskStackTransitionObserverTest.kt new file mode 100644 index 000000000000..f9599702e763 --- /dev/null +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/recents/TaskStackTransitionObserverTest.kt @@ -0,0 +1,217 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.recents + +import android.app.ActivityManager +import android.app.WindowConfiguration +import android.os.IBinder +import android.platform.test.annotations.EnableFlags +import android.platform.test.flag.junit.SetFlagsRule +import android.testing.AndroidTestingRunner +import android.view.SurfaceControl +import android.view.WindowManager +import android.window.IWindowContainerToken +import android.window.TransitionInfo +import android.window.WindowContainerToken +import androidx.test.filters.SmallTest +import com.android.window.flags.Flags +import com.android.wm.shell.TestShellExecutor +import com.android.wm.shell.common.ShellExecutor +import com.android.wm.shell.sysui.ShellInit +import com.android.wm.shell.transition.TransitionInfoBuilder +import com.android.wm.shell.transition.Transitions +import com.google.common.truth.Truth.assertThat +import dagger.Lazy +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentCaptor +import org.mockito.Mock +import org.mockito.Mockito +import org.mockito.MockitoAnnotations +import org.mockito.kotlin.same +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever + + +/** + * Test class for {@link TaskStackTransitionObserver} + * + * Usage: atest WMShellUnitTests:TaskStackTransitionObserverTest + */ +@SmallTest +@RunWith(AndroidTestingRunner::class) +class TaskStackTransitionObserverTest { + + @JvmField @Rule val setFlagsRule = SetFlagsRule() + + @Mock private lateinit var shellInit: ShellInit + @Mock lateinit var testExecutor: ShellExecutor + @Mock private lateinit var transitionsLazy: Lazy<Transitions> + @Mock private lateinit var transitions: Transitions + @Mock private lateinit var mockTransitionBinder: IBinder + + private lateinit var transitionObserver: TaskStackTransitionObserver + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + shellInit = Mockito.spy(ShellInit(testExecutor)) + whenever(transitionsLazy.get()).thenReturn(transitions) + transitionObserver = TaskStackTransitionObserver(transitionsLazy, shellInit) + if (Transitions.ENABLE_SHELL_TRANSITIONS) { + val initRunnableCaptor = ArgumentCaptor.forClass(Runnable::class.java) + verify(shellInit) + .addInitCallback(initRunnableCaptor.capture(), same(transitionObserver)) + initRunnableCaptor.value.run() + } else { + transitionObserver.onInit() + } + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_TASK_STACK_OBSERVER_IN_SHELL) + fun testRegistersObserverAtInit() { + verify(transitions).registerObserver(same(transitionObserver)) + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_TASK_STACK_OBSERVER_IN_SHELL) + fun taskCreated_freeformWindow_listenerNotified() { + val listener = TestListener() + val executor = TestShellExecutor() + transitionObserver.addTaskStackTransitionObserverListener(listener, executor) + val change = + createChange( + WindowManager.TRANSIT_OPEN, + createTaskInfo(1, WindowConfiguration.WINDOWING_MODE_FREEFORM) + ) + val transitionInfo = + TransitionInfoBuilder(WindowManager.TRANSIT_OPEN, 0).addChange(change).build() + + callOnTransitionReady(transitionInfo) + callOnTransitionFinished() + executor.flushAll() + + assertThat(listener.taskInfoToBeNotified.taskId).isEqualTo(change.taskInfo?.taskId) + assertThat(listener.taskInfoToBeNotified.windowingMode) + .isEqualTo(change.taskInfo?.windowingMode) + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_TASK_STACK_OBSERVER_IN_SHELL) + fun taskCreated_fullscreenWindow_listenerNotNotified() { + val listener = TestListener() + val executor = TestShellExecutor() + transitionObserver.addTaskStackTransitionObserverListener(listener, executor) + val change = + createChange( + WindowManager.TRANSIT_OPEN, + createTaskInfo(1, WindowConfiguration.WINDOWING_MODE_FULLSCREEN) + ) + val transitionInfo = + TransitionInfoBuilder(WindowManager.TRANSIT_OPEN, 0).addChange(change).build() + + callOnTransitionReady(transitionInfo) + callOnTransitionFinished() + executor.flushAll() + + assertThat(listener.taskInfoToBeNotified.taskId).isEqualTo(0) + assertThat(listener.taskInfoToBeNotified.windowingMode) + .isEqualTo(WindowConfiguration.WINDOWING_MODE_UNDEFINED) + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_TASK_STACK_OBSERVER_IN_SHELL) + fun taskCreated_freeformWindowOnTopOfFreeform_listenerNotified() { + val listener = TestListener() + val executor = TestShellExecutor() + transitionObserver.addTaskStackTransitionObserverListener(listener, executor) + val freeformOpenChange = + createChange( + WindowManager.TRANSIT_OPEN, + createTaskInfo(1, WindowConfiguration.WINDOWING_MODE_FREEFORM) + ) + val freeformReorderChange = + createChange( + WindowManager.TRANSIT_TO_BACK, + createTaskInfo(2, WindowConfiguration.WINDOWING_MODE_FREEFORM) + ) + val transitionInfo = + TransitionInfoBuilder(WindowManager.TRANSIT_OPEN, 0) + .addChange(freeformOpenChange) + .addChange(freeformReorderChange) + .build() + + callOnTransitionReady(transitionInfo) + callOnTransitionFinished() + executor.flushAll() + + assertThat(listener.taskInfoToBeNotified.taskId) + .isEqualTo(freeformOpenChange.taskInfo?.taskId) + assertThat(listener.taskInfoToBeNotified.windowingMode) + .isEqualTo(freeformOpenChange.taskInfo?.windowingMode) + } + + class TestListener : TaskStackTransitionObserver.TaskStackTransitionObserverListener { + var taskInfoToBeNotified = ActivityManager.RunningTaskInfo() + + override fun onTaskMovedToFrontThroughTransition( + taskInfo: ActivityManager.RunningTaskInfo + ) { + taskInfoToBeNotified = taskInfo + } + } + + /** Simulate calling the onTransitionReady() method */ + private fun callOnTransitionReady(transitionInfo: TransitionInfo) { + val startT = Mockito.mock(SurfaceControl.Transaction::class.java) + val finishT = Mockito.mock(SurfaceControl.Transaction::class.java) + + transitionObserver.onTransitionReady(mockTransitionBinder, transitionInfo, startT, finishT) + } + + /** Simulate calling the onTransitionFinished() method */ + private fun callOnTransitionFinished() { + transitionObserver.onTransitionFinished(mockTransitionBinder, false) + } + + companion object { + fun createTaskInfo(taskId: Int, windowingMode: Int): ActivityManager.RunningTaskInfo { + val taskInfo = ActivityManager.RunningTaskInfo() + taskInfo.taskId = taskId + taskInfo.configuration.windowConfiguration.windowingMode = windowingMode + + return taskInfo + } + + fun createChange( + mode: Int, + taskInfo: ActivityManager.RunningTaskInfo + ): TransitionInfo.Change { + val change = + TransitionInfo.Change( + WindowContainerToken(Mockito.mock(IWindowContainerToken::class.java)), + Mockito.mock(SurfaceControl::class.java) + ) + change.mode = mode + change.taskInfo = taskInfo + return change + } + } +} diff --git a/nfc/api/system-current.txt b/nfc/api/system-current.txt index 055ccbced58a..3375e18c001d 100644 --- a/nfc/api/system-current.txt +++ b/nfc/api/system-current.txt @@ -57,7 +57,9 @@ package android.nfc { @FlaggedApi("android.nfc.nfc_oem_extension") public final class NfcOemExtension { method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void clearPreference(); + method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void maybeTriggerFirmwareUpdate(); method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void registerCallback(@NonNull java.util.concurrent.Executor, @NonNull android.nfc.NfcOemExtension.Callback); + method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void synchronizeScreenState(); method @FlaggedApi("android.nfc.nfc_oem_extension") @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void unregisterCallback(@NonNull android.nfc.NfcOemExtension.Callback); } diff --git a/nfc/java/android/nfc/INfcAdapter.aidl b/nfc/java/android/nfc/INfcAdapter.aidl index 7150b54cf7f1..fd77820afc81 100644 --- a/nfc/java/android/nfc/INfcAdapter.aidl +++ b/nfc/java/android/nfc/INfcAdapter.aidl @@ -110,4 +110,6 @@ interface INfcAdapter void registerOemExtensionCallback(INfcOemExtensionCallback callbacks); void unregisterOemExtensionCallback(INfcOemExtensionCallback callbacks); void clearPreference(); + void setScreenState(); + void checkFirmware(); } diff --git a/nfc/java/android/nfc/NfcOemExtension.java b/nfc/java/android/nfc/NfcOemExtension.java index 1eff58cb80fd..f6138a63fae4 100644 --- a/nfc/java/android/nfc/NfcOemExtension.java +++ b/nfc/java/android/nfc/NfcOemExtension.java @@ -141,6 +141,34 @@ public final class NfcOemExtension { } } + /** + * Get the screen state from system and set it to current screen state. + */ + @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) + @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) + public void synchronizeScreenState() { + try { + NfcAdapter.sService.setScreenState(); + } catch (RemoteException e) { + mAdapter.attemptDeadServiceRecovery(e); + } + } + + /** + * Check if the firmware needs updating. + * + * <p>If an update is needed, a firmware will be triggered when NFC is disabled. + */ + @FlaggedApi(Flags.FLAG_NFC_OEM_EXTENSION) + @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) + public void maybeTriggerFirmwareUpdate() { + try { + NfcAdapter.sService.checkFirmware(); + } catch (RemoteException e) { + mAdapter.attemptDeadServiceRecovery(e); + } + } + private final class NfcOemExtensionCallback extends INfcOemExtensionCallback.Stub { @Override public void onTagConnected(boolean connected, Tag tag) throws RemoteException { diff --git a/packages/CredentialManager/res/values-pt-rBR/strings.xml b/packages/CredentialManager/res/values-pt-rBR/strings.xml index bc8a969ff44e..91385c6b8f62 100644 --- a/packages/CredentialManager/res/values-pt-rBR/strings.xml +++ b/packages/CredentialManager/res/values-pt-rBR/strings.xml @@ -48,7 +48,7 @@ <string name="passwords" msgid="5419394230391253816">"senhas"</string> <string name="sign_ins" msgid="4710739369149469208">"logins"</string> <string name="sign_in_info" msgid="2627704710674232328">"informações de login"</string> - <string name="save_credential_to_title" msgid="3172811692275634301">"Salvar <xliff:g id="CREDENTIALTYPES">%1$s</xliff:g> em"</string> + <string name="save_credential_to_title" msgid="3172811692275634301">"Salvar <xliff:g id="CREDENTIALTYPES">%1$s</xliff:g> no"</string> <string name="create_passkey_in_other_device_title" msgid="2360053098931886245">"Criar chave de acesso em outro dispositivo?"</string> <string name="save_password_on_other_device_title" msgid="5829084591948321207">"Salvar senha em outro dispositivo?"</string> <string name="save_sign_in_on_other_device_title" msgid="2827990118560134692">"Salvar credenciais de login em outro dispositivo?"</string> @@ -57,9 +57,9 @@ <string name="set_as_default" msgid="4415328591568654603">"Definir como padrão"</string> <string name="settings" msgid="6536394145760913145">"Configurações"</string> <string name="use_once" msgid="9027366575315399714">"Usar uma vez"</string> - <string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> senhas • <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> chaves de acesso"</string> - <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> senhas"</string> - <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> chaves de acesso"</string> + <string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"Senhas (<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>) • Chaves de acesso (<xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g>)"</string> + <string name="more_options_usage_passwords" msgid="1632047277723187813">"Senhas (<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>)"</string> + <string name="more_options_usage_passkeys" msgid="5390320437243042237">"Chaves de acesso (<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g>)"</string> <string name="more_options_usage_credentials" msgid="1785697001787193984">"<xliff:g id="TOTALCREDENTIALSNUMBER">%1$s</xliff:g> credenciais"</string> <string name="passkey_before_subtitle" msgid="2448119456208647444">"Chave de acesso"</string> <string name="another_device" msgid="5147276802037801217">"Outro dispositivo"</string> diff --git a/packages/CredentialManager/res/values-pt/strings.xml b/packages/CredentialManager/res/values-pt/strings.xml index bc8a969ff44e..91385c6b8f62 100644 --- a/packages/CredentialManager/res/values-pt/strings.xml +++ b/packages/CredentialManager/res/values-pt/strings.xml @@ -48,7 +48,7 @@ <string name="passwords" msgid="5419394230391253816">"senhas"</string> <string name="sign_ins" msgid="4710739369149469208">"logins"</string> <string name="sign_in_info" msgid="2627704710674232328">"informações de login"</string> - <string name="save_credential_to_title" msgid="3172811692275634301">"Salvar <xliff:g id="CREDENTIALTYPES">%1$s</xliff:g> em"</string> + <string name="save_credential_to_title" msgid="3172811692275634301">"Salvar <xliff:g id="CREDENTIALTYPES">%1$s</xliff:g> no"</string> <string name="create_passkey_in_other_device_title" msgid="2360053098931886245">"Criar chave de acesso em outro dispositivo?"</string> <string name="save_password_on_other_device_title" msgid="5829084591948321207">"Salvar senha em outro dispositivo?"</string> <string name="save_sign_in_on_other_device_title" msgid="2827990118560134692">"Salvar credenciais de login em outro dispositivo?"</string> @@ -57,9 +57,9 @@ <string name="set_as_default" msgid="4415328591568654603">"Definir como padrão"</string> <string name="settings" msgid="6536394145760913145">"Configurações"</string> <string name="use_once" msgid="9027366575315399714">"Usar uma vez"</string> - <string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> senhas • <xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g> chaves de acesso"</string> - <string name="more_options_usage_passwords" msgid="1632047277723187813">"<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g> senhas"</string> - <string name="more_options_usage_passkeys" msgid="5390320437243042237">"<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g> chaves de acesso"</string> + <string name="more_options_usage_passwords_passkeys" msgid="3470113942332934279">"Senhas (<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>) • Chaves de acesso (<xliff:g id="PASSKEYSNUMBER">%2$s</xliff:g>)"</string> + <string name="more_options_usage_passwords" msgid="1632047277723187813">"Senhas (<xliff:g id="PASSWORDSNUMBER">%1$s</xliff:g>)"</string> + <string name="more_options_usage_passkeys" msgid="5390320437243042237">"Chaves de acesso (<xliff:g id="PASSKEYSNUMBER">%1$s</xliff:g>)"</string> <string name="more_options_usage_credentials" msgid="1785697001787193984">"<xliff:g id="TOTALCREDENTIALSNUMBER">%1$s</xliff:g> credenciais"</string> <string name="passkey_before_subtitle" msgid="2448119456208647444">"Chave de acesso"</string> <string name="another_device" msgid="5147276802037801217">"Outro dispositivo"</string> diff --git a/packages/EasterEgg/AndroidManifest.xml b/packages/EasterEgg/AndroidManifest.xml index 1500583a7677..754abb2f76be 100644 --- a/packages/EasterEgg/AndroidManifest.xml +++ b/packages/EasterEgg/AndroidManifest.xml @@ -33,7 +33,7 @@ <uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> <application - android:icon="@drawable/android14_patch_adaptive" + android:icon="@drawable/android15_patch_adaptive" android:label="@string/app_name"> <!-- Android V easter egg: Daydream version of Landroid @@ -41,7 +41,7 @@ <service android:name=".landroid.DreamUniverse" android:exported="true" - android:icon="@drawable/android14_patch_adaptive" + android:icon="@drawable/android15_patch_adaptive" android:label="@string/v_egg_name" android:description="@string/dream_description" android:enabled="false" @@ -62,7 +62,7 @@ android:name=".landroid.MainActivity" android:exported="true" android:label="@string/u_egg_name" - android:icon="@drawable/android14_patch_adaptive" + android:icon="@drawable/android15_patch_adaptive" android:configChanges="orientation|screenLayout|screenSize|density" android:theme="@android:style/Theme.DeviceDefault.NoActionBar.Fullscreen"> <intent-filter> diff --git a/packages/EasterEgg/res/drawable/android15_patch_adaptive.xml b/packages/EasterEgg/res/drawable/android15_patch_adaptive.xml new file mode 100644 index 000000000000..d9492000beeb --- /dev/null +++ b/packages/EasterEgg/res/drawable/android15_patch_adaptive.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> + <background android:drawable="@drawable/android15_patch_adaptive_background"/> + <foreground android:drawable="@drawable/android15_patch_adaptive_foreground"/> + <monochrome android:drawable="@drawable/android15_patch_monochrome"/> +</adaptive-icon> diff --git a/packages/EasterEgg/res/drawable/android15_patch_adaptive_background.xml b/packages/EasterEgg/res/drawable/android15_patch_adaptive_background.xml new file mode 100644 index 000000000000..642b30aedf4c --- /dev/null +++ b/packages/EasterEgg/res/drawable/android15_patch_adaptive_background.xml @@ -0,0 +1,75 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="108dp" + android:height="108dp" + android:viewportWidth="108" + android:viewportHeight="108"> + <!-- space --> + <path + android:pathData="M0,0h108v108h-108z" + android:fillColor="#202124"/> + <!-- stars --> + <group> + <path + android:pathData="M32,34 h1v1h-1z" + android:fillColor="#ffffff"/> + <path + android:pathData="M33,61 h1v1h-1z" + android:fillColor="#ffffff"/> + <path + android:pathData="M71,34 h1v1h-1z" + android:fillColor="#ffffff"/> + <path + android:pathData="M62,56 h1v1h-1z" + android:fillColor="#ffffff"/> + <path + android:pathData="M68,47 h1v1h-1z" + android:fillColor="#ffffff"/> + <path + android:pathData="M72,55 h1v1h-1z" + android:fillColor="#ffffff"/> + <path + android:pathData="M39,36 h1v1h-1z" + android:fillColor="#ffffff"/> + + <path + android:pathData="M72,49 h2v2h-2z" + android:fillColor="#ffffff"/> + <path + android:pathData="M46,53 h2v2h-2z" + android:fillColor="#ffffff"/> + <path + android:pathData="M32,45 h2v2h-2z" + android:fillColor="#ffffff"/> + <path + android:pathData="M43,37 h2v2h-2z" + android:fillColor="#ffffff"/> + <path + android:pathData="M78,51 h2v2h-2z" + android:fillColor="#ffffff"/> + <path + android:pathData="M34,51 h2v2h-2z" + android:fillColor="#ffffff"/> + <path + android:pathData="M76,41 h2v2h-2z" + android:fillColor="#ffffff"/> + <path + android:pathData="M39,46 h2v2h-2z" + android:fillColor="#ffffff"/> + </group> +</vector> diff --git a/packages/EasterEgg/res/drawable/android15_patch_adaptive_foreground.xml b/packages/EasterEgg/res/drawable/android15_patch_adaptive_foreground.xml new file mode 100644 index 000000000000..1100eb725780 --- /dev/null +++ b/packages/EasterEgg/res/drawable/android15_patch_adaptive_foreground.xml @@ -0,0 +1,60 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="108dp" + android:height="108dp" + android:viewportWidth="108" + android:viewportHeight="108"> + + <!-- zoomies --> + <group> + <path + android:pathData="M53,42C52.21,50.58 46.46,68.95 32.11,74.63C19.22,79.75 5.77,82.32 1.19,83.19C0.68,83.29 0.28,83.36 0,83.42V108H54H108V83.42C107.72,83.36 107.32,83.29 106.81,83.19C102.23,82.32 88.78,79.75 75.89,74.63C61.54,68.95 55.79,50.58 55,42H54H53Z" + android:fillColor="#C6FF00" + android:fillType="evenOdd"/> + <path + android:pathData="M53.25,42C52.88,50.53 50.44,69.01 43.68,74.67C36.91,80.33 32.65,82.86 31.37,83.41L54,102.87L76.63,83.41C75.35,82.86 71.09,80.33 64.32,74.67C57.56,69.01 55.12,50.53 54.75,42H54H53.25Z" + android:fillColor="#ffffff" + android:fillType="evenOdd"/> + <path + android:pathData="M54,42m-1,0a1,1 0,1 1,2 0a1,1 0,1 1,-2 0" + android:fillColor="#ffffff"/> + </group> + <group> + <!-- head! it's like sputnik --> + <path + android:pathData="M54,94.25m-26.25,0a26.25,26.25 0,1 1,52.5 0a26.25,26.25 0,1 1,-52.5 0" + android:fillColor="#34A853"/> + <!-- ant --> + <path + android:pathData="M38,63.5L44.5,74.5" + android:strokeWidth="5" + android:fillColor="#00000000" + android:strokeColor="#34A853" + android:strokeLineCap="round"/> + <path + android:pathData="M70,63.5L63.5,74.5" + android:strokeWidth="5" + android:fillColor="#00000000" + android:strokeColor="#34A853" + android:strokeLineCap="round"/> + </group> + <!-- spaceship --> + <path + android:pathData="M54,34C52.34,34 51,35.29 51,36.88V40.44C51,40.75 51.25,41 51.56,41C51.87,41 52.13,40.75 52.13,40.44V39.48C52.13,38.87 52.63,38.37 53.25,38.37H54.75C55.37,38.37 55.87,38.87 55.87,39.48V40.44C55.87,40.75 56.13,41 56.44,41C56.75,41 57,40.75 57,40.44V36.88C57,35.29 55.66,34 54,34H54Z" + android:fillColor="#E9F3EB"/> +</vector> diff --git a/packages/EasterEgg/res/drawable/android15_patch_monochrome.xml b/packages/EasterEgg/res/drawable/android15_patch_monochrome.xml new file mode 100644 index 000000000000..a91cc864e7cd --- /dev/null +++ b/packages/EasterEgg/res/drawable/android15_patch_monochrome.xml @@ -0,0 +1,119 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="108dp" + android:height="108dp" + android:viewportWidth="108" + android:viewportHeight="108"> + <group> + <path + android:pathData=" + M54,94.25 + m-26.25,0 + a26.25,26.25 0,1 1,52.5 0 + a26.25,26.25 0,1 1,-52.5 0 + " + android:fillColor="#000000"/> + <path + android:pathData="M38,63.5L44.5,74.5" + android:strokeWidth="5" + android:fillColor="#00000000" + android:strokeColor="#000000" + android:strokeLineCap="round"/> + <path + android:pathData="M70,63.5L63.5,74.5" + android:strokeWidth="5" + android:fillColor="#00000000" + android:strokeColor="#000000" + android:strokeLineCap="round"/> + + <path + android:pathData=" + M54,34 + C52.34,34 51,35.29 51,36.88 + V40.44 + C51,40.75 51.25,41 51.56,41 + C51.87,41 52.13,40.75 52.13,40.44 + V39.48 + C52.13,38.87 52.63,38.37 53.25,38.37 + H54.75 + C55.37,38.37 55.87,38.87 55.87,39.48 + V40.44 + C55.87,40.75 56.13,41 56.44,41 + C56.75,41 57,40.75 57,40.44 + V36.88 + C57,35.29 55.66,34 54,34 + H54 + Z + " + android:fillColor="#34A853"/> + <path + android:strokeWidth="1" + android:pathData="M54,40V67" + android:fillColor="#00000000" + android:strokeColor="#40FFFFFF" + /> + + <path + android:pathData="M32,34 h1v1h-1z" + android:fillColor="#ffffff"/> + <path + android:pathData="M33,61 h1v1h-1z" + android:fillColor="#ffffff"/> + <path + android:pathData="M71,34 h1v1h-1z" + android:fillColor="#ffffff"/> + <path + android:pathData="M62,56 h1v1h-1z" + android:fillColor="#ffffff"/> + <path + android:pathData="M68,47 h1v1h-1z" + android:fillColor="#ffffff"/> + <path + android:pathData="M72,55 h1v1h-1z" + android:fillColor="#ffffff"/> + <path + android:pathData="M39,36 h1v1h-1z" + android:fillColor="#ffffff"/> + + <path + android:pathData="M72,49 h2v2h-2z" + android:fillColor="#ffffff"/> + <path + android:pathData="M46,53 h2v2h-2z" + android:fillColor="#ffffff"/> + <path + android:pathData="M32,45 h2v2h-2z" + android:fillColor="#ffffff"/> + <path + android:pathData="M43,37 h2v2h-2z" + android:fillColor="#ffffff"/> + <path + android:pathData="M78,51 h2v2h-2z" + android:fillColor="#ffffff"/> + <path + android:pathData="M34,51 h2v2h-2z" + android:fillColor="#ffffff"/> + <path + android:pathData="M76,41 h2v2h-2z" + android:fillColor="#ffffff"/> + <path + android:pathData="M39,46 h2v2h-2z" + android:fillColor="#ffffff"/> + + </group> +</vector> diff --git a/packages/PackageInstaller/res/values-pa/strings.xml b/packages/PackageInstaller/res/values-pa/strings.xml index 1db92a00683e..80dc8897e3c3 100644 --- a/packages/PackageInstaller/res/values-pa/strings.xml +++ b/packages/PackageInstaller/res/values-pa/strings.xml @@ -63,7 +63,7 @@ <string name="archive_application_text_all_users" msgid="3151229641681672580">"ਕੀ ਇਸ ਐਪ ਨੂੰ ਸਾਰੇ ਵਰਤੋਂਕਾਰਾਂ ਲਈ ਪੁਰਾਲੇਖਬੱਧ ਕਰਨਾ ਹੈ? ਤੁਹਾਡਾ ਨਿੱਜੀ ਡਾਟਾ ਰੱਖਿਅਤ ਕੀਤਾ ਜਾਵੇਗਾ"</string> <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"ਕੀ ਇਸ ਐਪ ਨੂੰ ਤੁਹਾਡੇ ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ \'ਤੇ ਪੁਰਾਲੇਖਬੱਧ ਕਰਨਾ ਹੈ? ਤੁਹਾਡਾ ਨਿੱਜੀ ਡਾਟਾ ਰੱਖਿਅਤ ਕੀਤਾ ਜਾਵੇਗਾ"</string> <string name="archive_application_text_user" msgid="2586558895535581451">"ਕੀ ਇਸ ਐਪ ਨੂੰ <xliff:g id="USERNAME">%1$s</xliff:g> ਲਈ ਪੁਰਾਲੇਖਬੱਧ ਕਰਨਾ ਹੈ? ਤੁਹਾਡਾ ਨਿੱਜੀ ਡਾਟਾ ਰੱਖਿਅਤ ਕੀਤਾ ਜਾਵੇਗਾ"</string> - <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"ਕੀ ਤੁਸੀਂ ਇਸ ਐਪ ਨੂੰ ਆਪਣੀ ਨਿੱਜੀ ਸਪੇਸ ਤੋਂ ਪੁਰਾਲੇਖਬੱਧ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ? ਤੁਹਾਡਾ ਨਿੱਜੀ ਡਾਟਾ ਰੱਖਿਅਤ ਕੀਤਾ ਜਾਵੇਗਾ"</string> + <string name="archive_application_text_current_user_private_profile" msgid="1958423158655599132">"ਕੀ ਤੁਸੀਂ ਇਸ ਐਪ ਨੂੰ ਆਪਣੀ ਪ੍ਰਾਈਵੇਟ ਸਪੇਸ ਤੋਂ ਪੁਰਾਲੇਖਬੱਧ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ? ਤੁਹਾਡਾ ਨਿੱਜੀ ਡਾਟਾ ਰੱਖਿਅਤ ਕੀਤਾ ਜਾਵੇਗਾ"</string> <string name="uninstall_application_text_all_users" msgid="575491774380227119">"ਕੀ ਤੁਸੀਂ ਇਸ ਐਪ ਨੂੰ "<b>"ਸਾਰੇ"</b>" ਵਰਤੋਂਕਾਰਾਂ ਲਈ ਅਣਸਥਾਪਤ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ? ਐਪਲੀਕੇਸ਼ਨ ਅਤੇ ਇਸਦਾ ਡਾਟਾ ਡੀਵਾਈਸ \'ਤੇ "<b>"ਸਾਰੇ"</b>" ਵਰਤੋਂਕਾਰਾਂ ਵੱਲੋਂ ਹਟਾ ਦਿੱਤਾ ਜਾਵੇਗਾ।"</string> <string name="uninstall_application_text_user" msgid="498072714173920526">"ਕੀ ਤੁਸੀਂ ਵਰਤੋਂਕਾਰ <xliff:g id="USERNAME">%1$s</xliff:g> ਲਈ ਇਸ ਐਪ ਨੂੰ ਅਣਸਥਾਪਤ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ?"</string> <string name="uninstall_application_text_current_user_work_profile" msgid="8788387739022366193">"ਕੀ ਤੁਸੀਂ ਇਸ ਐਪ ਨੂੰ ਆਪਣੇ ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ ਤੋਂ ਅਣਸਥਾਪਤ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ?"</string> diff --git a/packages/PackageInstaller/res/values-tr/strings.xml b/packages/PackageInstaller/res/values-tr/strings.xml index 006ad52297de..e10eb0e52fcd 100644 --- a/packages/PackageInstaller/res/values-tr/strings.xml +++ b/packages/PackageInstaller/res/values-tr/strings.xml @@ -55,7 +55,7 @@ <string name="user_is_not_allowed_dlg_text" msgid="3468447791330611681">"Geçerli kullanıcının bu yüklemeyi kaldırma izni yok."</string> <string name="generic_error_dlg_title" msgid="5863195085927067752">"Hata"</string> <string name="generic_error_dlg_text" msgid="5287861443265795232">"Uygulamanın yüklemesi kaldırılamadı."</string> - <string name="uninstall_application_title" msgid="4045420072401428123">"Uygulamanın yüklemesini kaldır"</string> + <string name="uninstall_application_title" msgid="4045420072401428123">"Uygulamayı kaldır"</string> <string name="uninstall_update_title" msgid="824411791011583031">"Güncelleme kaldırılsın mı?"</string> <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g>, şu uygulamanın bir parçasıdır:"</string> <string name="uninstall_application_text" msgid="3816830743706143980">"Bu uygulamanın yüklemesini kaldırmak istiyor musunuz?"</string> diff --git a/packages/PrintSpooler/res/values-as/strings.xml b/packages/PrintSpooler/res/values-as/strings.xml index 020eac770134..950c5c8f585c 100644 --- a/packages/PrintSpooler/res/values-as/strings.xml +++ b/packages/PrintSpooler/res/values-as/strings.xml @@ -100,7 +100,7 @@ </string-array> <string-array name="orientation_labels"> <item msgid="4061931020926489228">"প\'ৰ্ট্ৰেইট"</item> - <item msgid="3199660090246166812">"লেণ্ডস্কেইপ"</item> + <item msgid="3199660090246166812">"লেণ্ডস্কে’প"</item> </string-array> <string name="print_write_error_message" msgid="5787642615179572543">"ফাইলত লিখিব পৰা নহ\'ল"</string> <string name="print_error_default_message" msgid="8602678405502922346">"দুঃখিত, প্ৰিণ্টিঙৰ কাম নহ\'ল। পুনৰ চেষ্টা কৰক।"</string> diff --git a/packages/SettingsLib/ProfileSelector/res/values-pt-rBR/strings.xml b/packages/SettingsLib/ProfileSelector/res/values-pt-rBR/strings.xml index eb33d573c495..75e1a186d028 100644 --- a/packages/SettingsLib/ProfileSelector/res/values-pt-rBR/strings.xml +++ b/packages/SettingsLib/ProfileSelector/res/values-pt-rBR/strings.xml @@ -19,5 +19,5 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="settingslib_category_personal" msgid="1142302328104700620">"Pessoal"</string> <string name="settingslib_category_work" msgid="4867750733682444676">"Trabalho"</string> - <string name="settingslib_category_private" msgid="5039276873477591386">"Particular"</string> + <string name="settingslib_category_private" msgid="5039276873477591386">"Privado"</string> </resources> diff --git a/packages/SettingsLib/ProfileSelector/res/values-pt/strings.xml b/packages/SettingsLib/ProfileSelector/res/values-pt/strings.xml index eb33d573c495..75e1a186d028 100644 --- a/packages/SettingsLib/ProfileSelector/res/values-pt/strings.xml +++ b/packages/SettingsLib/ProfileSelector/res/values-pt/strings.xml @@ -19,5 +19,5 @@ xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="settingslib_category_personal" msgid="1142302328104700620">"Pessoal"</string> <string name="settingslib_category_work" msgid="4867750733682444676">"Trabalho"</string> - <string name="settingslib_category_private" msgid="5039276873477591386">"Particular"</string> + <string name="settingslib_category_private" msgid="5039276873477591386">"Privado"</string> </resources> diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml index f0fb4df441b6..caeee064bf99 100644 --- a/packages/SettingsLib/res/values-de/strings.xml +++ b/packages/SettingsLib/res/values-de/strings.xml @@ -710,7 +710,7 @@ <string name="keyboard_layout_default_label" msgid="1997292217218546957">"Standard"</string> <string name="turn_screen_on_title" msgid="2662312432042116026">"Steuerelement zum Aktivieren des Displays"</string> <string name="allow_turn_screen_on" msgid="6194845766392742639">"Aktivieren des Displays erlauben"</string> - <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Einer App erlauben, das Display zu aktivieren. Wenn du diese Erlaubnis erteilst, kann die App jederzeit das Display aktivieren – auch ohne deine explizite Absicht."</string> + <string name="allow_turn_screen_on_description" msgid="43834403291575164">"Einer App erlauben, das Display zu aktivieren. Wenn du diese Erlaubnis erteilst, kann die App jederzeit das Display aktivieren – auch ohne dass du dies beabsichtigst."</string> <string name="bt_le_audio_broadcast_dialog_title" msgid="5392738488989777074">"<xliff:g id="APP_NAME">%1$s</xliff:g> nicht mehr streamen?"</string> <string name="bt_le_audio_broadcast_dialog_sub_title" msgid="268234802198852753">"Wenn du <xliff:g id="SWITCHAPP">%1$s</xliff:g> streamst oder die Ausgabe änderst, wird dein aktueller Stream beendet"</string> <string name="bt_le_audio_broadcast_dialog_switch_app" msgid="5749813313369517812">"<xliff:g id="SWITCHAPP">%1$s</xliff:g> streamen"</string> diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml index 13819033d166..a25e179ee452 100644 --- a/packages/SettingsLib/res/values-kn/strings.xml +++ b/packages/SettingsLib/res/values-kn/strings.xml @@ -632,7 +632,7 @@ <string name="profile_info_settings_title" msgid="105699672534365099">"ಪ್ರೊಫೈಲ್ ಮಾಹಿತಿ"</string> <string name="user_need_lock_message" msgid="4311424336209509301">"ನೀವು ನಿರ್ಬಂಧಿತ ಪ್ರೊಫೈಲ್ ಅನ್ನು ರಚಿಸಬಹುದಾದರ ಮೊದಲು, ನಿಮ್ಮ ಅಪ್ಲಿಕೇಶನ್ಗಳು ಮತ್ತು ವೈಯಕ್ತಿಕ ಡೇಟಾವನ್ನು ರಕ್ಷಿಸಲು ನೀವು ಪರದೆಯ ಲಾಕ್ ಹೊಂದಿಸುವ ಅಗತ್ಯವಿದೆ."</string> <string name="user_set_lock_button" msgid="1427128184982594856">"ಲಾಕ್ ಹೊಂದಿಸಿ"</string> - <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g> ಗೆ ಬದಲಿಸಿ"</string> + <string name="user_switch_to_user" msgid="6975428297154968543">"<xliff:g id="USER_NAME">%s</xliff:g>ಗೆ ಬದಲಿಸಿ"</string> <string name="creating_new_user_dialog_message" msgid="7232880257538970375">"ಹೊಸ ಬಳಕೆದಾರರನ್ನು ರಚಿಸಲಾಗುತ್ತಿದೆ…"</string> <string name="creating_new_guest_dialog_message" msgid="1114905602181350690">"ಹೊಸ ಅತಿಥಿಯನ್ನು ರಚಿಸಲಾಗುತ್ತಿದೆ…"</string> <string name="add_user_failed" msgid="4809887794313944872">"ಹೊಸ ಬಳಕೆದಾರರನ್ನು ರಚಿಸಲು ವಿಫಲವಾಗಿದೆ"</string> diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml index 5032e358f213..1e33f88879d1 100644 --- a/packages/SettingsLib/res/values-ne/strings.xml +++ b/packages/SettingsLib/res/values-ne/strings.xml @@ -641,7 +641,7 @@ <string name="edit_user_info_message" msgid="6677556031419002895">"यो डिभाइस प्रयोग गर्ने सबै जना तपाईंले छनौट गर्ने नाम र फोटो देख्न सक्ने छन्।"</string> <string name="user_add_user" msgid="7876449291500212468">"प्रयोगकर्ता कनेक्ट गर्नुहोस्"</string> <string name="guest_new_guest" msgid="3482026122932643557">"अतिथि कनेक्ट गर्नुहोस्"</string> - <string name="guest_exit_guest" msgid="5908239569510734136">"गेस्ट मोडबाट बाहिर निस्कियोस्"</string> + <string name="guest_exit_guest" msgid="5908239569510734136">"अथिति हटाउनुहोस्"</string> <string name="guest_reset_guest" msgid="6110013010356013758">"अतिथि सत्र रिसेट गर्नुहोस्"</string> <string name="guest_reset_guest_dialog_title" msgid="8047270010895437534">"अतिथिका रूपमा ब्राउज गर्ने सेसन रिसेट गर्ने हो?"</string> <string name="guest_remove_guest_dialog_title" msgid="4548511006624088072">"यी अतिथि हटाउने हो?"</string> diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml index d47a1f4d3b5a..337b011cc359 100644 --- a/packages/SettingsLib/res/values-pt-rBR/strings.xml +++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml @@ -237,7 +237,7 @@ <string name="choose_profile" msgid="343803890897657450">"Escolher perfil"</string> <string name="category_personal" msgid="6236798763159385225">"Pessoal"</string> <string name="category_work" msgid="4014193632325996115">"Trabalho"</string> - <string name="category_private" msgid="4244892185452788977">"Particular"</string> + <string name="category_private" msgid="4244892185452788977">"Privado"</string> <string name="category_clone" msgid="1554511758987195974">"Clone"</string> <string name="development_settings_title" msgid="140296922921597393">"Opções do desenvolvedor"</string> <string name="development_settings_enable" msgid="4285094651288242183">"Ativar opções do desenvolvedor"</string> diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml index d47a1f4d3b5a..337b011cc359 100644 --- a/packages/SettingsLib/res/values-pt/strings.xml +++ b/packages/SettingsLib/res/values-pt/strings.xml @@ -237,7 +237,7 @@ <string name="choose_profile" msgid="343803890897657450">"Escolher perfil"</string> <string name="category_personal" msgid="6236798763159385225">"Pessoal"</string> <string name="category_work" msgid="4014193632325996115">"Trabalho"</string> - <string name="category_private" msgid="4244892185452788977">"Particular"</string> + <string name="category_private" msgid="4244892185452788977">"Privado"</string> <string name="category_clone" msgid="1554511758987195974">"Clone"</string> <string name="development_settings_title" msgid="140296922921597393">"Opções do desenvolvedor"</string> <string name="development_settings_enable" msgid="4285094651288242183">"Ativar opções do desenvolvedor"</string> diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml index ff62ba0d29a4..e8acd6ec391d 100644 --- a/packages/SettingsLib/res/values-ta/strings.xml +++ b/packages/SettingsLib/res/values-ta/strings.xml @@ -161,7 +161,7 @@ <string name="bluetooth_pairing_decline" msgid="6483118841204885890">"ரத்துசெய்"</string> <string name="bluetooth_pairing_will_share_phonebook" msgid="3064334458659165176">"இணைத்தலானது உங்கள் தொடர்புகள், அழைப்பு வரலாறுக்கான அணுகலை வழங்குகிறது."</string> <string name="bluetooth_pairing_error_message" msgid="6626399020672335565">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> உடன் இணைய முடியவில்லை."</string> - <string name="bluetooth_pairing_pin_error_message" msgid="264422127613704940">"தவறான பின் அல்லது கடவுச்சொல் காரணமாக <xliff:g id="DEVICE_NAME">%1$s</xliff:g> உடன் இணைக்க முடியவில்லை."</string> + <string name="bluetooth_pairing_pin_error_message" msgid="264422127613704940">"தவறான பின் அல்லது கடவுச்சாவி காரணமாக <xliff:g id="DEVICE_NAME">%1$s</xliff:g> உடன் இணைக்க முடியவில்லை."</string> <string name="bluetooth_pairing_device_down_error_message" msgid="2554424863101358857">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> உடன் இணைக்க முடியவில்லை."</string> <string name="bluetooth_pairing_rejected_error_message" msgid="5943444352777314442">"<xliff:g id="DEVICE_NAME">%1$s</xliff:g> இணைப்பதை நிராகரித்தது."</string> <string name="bluetooth_talkback_computer" msgid="3736623135703893773">"கணினி"</string> diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml index 6aea659292c3..e8bac312b4dc 100644 --- a/packages/SettingsLib/res/values-tr/strings.xml +++ b/packages/SettingsLib/res/values-tr/strings.xml @@ -492,9 +492,9 @@ <string name="power_charging_duration" msgid="6127154952524919719">"<xliff:g id="LEVEL">%1$s</xliff:g> - Tamamen şarj olmasına <xliff:g id="TIME">%2$s</xliff:g> kaldı"</string> <string name="power_charging_limited" msgid="8202147604844938236">"<xliff:g id="LEVEL">%1$s</xliff:g> - Şarj işlemi optimize edildi"</string> <string name="power_charging_future_paused" msgid="1809543660923642799">"<xliff:g id="LEVEL">%1$s</xliff:g> ‑ Şarj ediliyor"</string> - <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - <xliff:g id="TIME">%3$s</xliff:g> içinde tamamen dolacak"</string> - <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="TIME">%2$s</xliff:g> içinde tamamen şarj olacak"</string> - <string name="power_remaining_charging_duration_only_v2" msgid="5358176435722950193">"<xliff:g id="TIME">%1$s</xliff:g> içinde tamamen şarj olacak"</string> + <string name="power_fast_charging_duration_v2" msgid="3797735998640359490">"<xliff:g id="LEVEL">%1$s</xliff:g> - <xliff:g id="STATUS">%2$s</xliff:g> - Tamamen dolacağı zaman: <xliff:g id="TIME">%3$s</xliff:g>"</string> + <string name="power_charging_duration_v2" msgid="2938998284074003248">"<xliff:g id="LEVEL">%1$s</xliff:g> - Tamamen şarj olacağı zaman: <xliff:g id="TIME">%2$s</xliff:g>"</string> + <string name="power_remaining_charging_duration_only_v2" msgid="5358176435722950193">"Tamamen şarj olacağı zaman: <xliff:g id="TIME">%1$s</xliff:g>"</string> <string name="power_remaining_fast_charging_duration_only_v2" msgid="6270950195810579563">"<xliff:g id="TIME">%1$s</xliff:g> itibarıyla tamamen dolacak"</string> <string name="battery_info_status_unknown" msgid="268625384868401114">"Bilinmiyor"</string> <string name="battery_info_status_charging" msgid="4279958015430387405">"Şarj oluyor"</string> diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java index ed964a9d0f40..b3e48b26782e 100644 --- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidDeviceManager.java @@ -209,44 +209,34 @@ public class HearingAidDeviceManager { CachedBluetoothDevice mainDevice = findMainDevice(cachedDevice); if (mainDevice != null) { if (mainDevice.isConnected()) { - // When main device exists and in connected state, receiving sub device - // connection. To refresh main device UI + // Sub/member device is connected and main device is connected + // To refresh main device UI mainDevice.refresh(); } else { - // When both Hearing Aid devices are disconnected, receiving sub device - // connection. To switch content and dispatch to notify UI change - mBtManager.getEventManager().dispatchDeviceRemoved(mainDevice); - mainDevice.switchSubDeviceContent(); - mainDevice.refresh(); - // It is necessary to do remove and add for updating the mapping on - // preference and device - mBtManager.getEventManager().dispatchDeviceAdded(mainDevice); + // Sub/member device is connected and main device is disconnected + // To switch content and dispatch to notify UI change + switchDeviceContent(mainDevice, cachedDevice); } return true; } break; case BluetoothProfile.STATE_DISCONNECTED: - mainDevice = findMainDevice(cachedDevice); if (cachedDevice.getUnpairing()) { return true; } + mainDevice = findMainDevice(cachedDevice); if (mainDevice != null) { - // When main device exists, receiving sub device disconnection + // Sub/member device is disconnected and main device exists // To update main device UI mainDevice.refresh(); return true; } - CachedBluetoothDevice subDevice = cachedDevice.getSubDevice(); - if (subDevice != null && subDevice.isConnected()) { - // Main device is disconnected and sub device is connected - // To copy data from sub device to main device - mBtManager.getEventManager().dispatchDeviceRemoved(cachedDevice); - cachedDevice.switchSubDeviceContent(); - cachedDevice.refresh(); - // It is necessary to do remove and add for updating the mapping on - // preference and device - mBtManager.getEventManager().dispatchDeviceAdded(cachedDevice); - + CachedBluetoothDevice connectedSecondaryDevice = getConnectedSecondaryDevice( + cachedDevice); + if (connectedSecondaryDevice != null) { + // Main device is disconnected and sub/member device is connected + // To switch content and dispatch to notify UI change + switchDeviceContent(cachedDevice, connectedSecondaryDevice); return true; } break; @@ -254,6 +244,29 @@ public class HearingAidDeviceManager { return false; } + private void switchDeviceContent(CachedBluetoothDevice mainDevice, + CachedBluetoothDevice secondaryDevice) { + mBtManager.getEventManager().dispatchDeviceRemoved(mainDevice); + if (mainDevice.getSubDevice() != null + && mainDevice.getSubDevice().equals(secondaryDevice)) { + mainDevice.switchSubDeviceContent(); + } else { + mainDevice.switchMemberDeviceContent(secondaryDevice); + } + mainDevice.refresh(); + // It is necessary to do remove and add for updating the mapping on + // preference and device + mBtManager.getEventManager().dispatchDeviceAdded(mainDevice); + } + + private CachedBluetoothDevice getConnectedSecondaryDevice(CachedBluetoothDevice cachedDevice) { + if (cachedDevice.getSubDevice() != null && cachedDevice.getSubDevice().isConnected()) { + return cachedDevice.getSubDevice(); + } + return cachedDevice.getMemberDevice().stream().filter( + CachedBluetoothDevice::isConnected).findAny().orElse(null); + } + void onActiveDeviceChanged(CachedBluetoothDevice device) { if (FeatureFlagUtils.isEnabled(mContext, FeatureFlagUtils.SETTINGS_AUDIO_ROUTING)) { if (device.isActiveDevice(BluetoothProfile.HEARING_AID) || device.isActiveDevice( diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java index 4188d2ec7aaa..bf927a1eb4cc 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/HearingAidDeviceManagerTest.java @@ -681,6 +681,53 @@ public class HearingAidDeviceManagerTest { verify(mCachedDevice1).refresh(); } + + /** + * Test onProfileConnectionStateChangedIfProcessed. + * When main device is disconnected, to verify switch() result for member device connected + * event + */ + @Test + public void onProfileConnectionStateChanged_connect_member_mainDisconnected_switch() { + when(mCachedDevice1.isConnected()).thenReturn(false); + when(mCachedDevice1.getGroupId()).thenReturn(GROUP_ID_1); + when(mCachedDevice2.getGroupId()).thenReturn(GROUP_ID_1); + mCachedDeviceManager.mCachedDevices.add(mCachedDevice1); + mCachedDevice1.addMemberDevice(mCachedDevice2); + + assertThat(mCachedDevice1.mDevice).isEqualTo(mDevice1); + assertThat(mCachedDevice2.mDevice).isEqualTo(mDevice2); + assertThat(mHearingAidDeviceManager.onProfileConnectionStateChangedIfProcessed( + mCachedDevice2, BluetoothProfile.STATE_CONNECTED)).isTrue(); + + assertThat(mCachedDevice1.mDevice).isEqualTo(mDevice2); + assertThat(mCachedDevice2.mDevice).isEqualTo(mDevice1); + verify(mCachedDevice1).refresh(); + } + + /** + * Test onProfileConnectionStateChangedIfProcessed. + * When member device is connected, to verify switch() result for main device disconnected + * event + */ + @Test + public void onProfileConnectionStateChanged_disconnect_main_subDeviceConnected_switch() { + when(mCachedDevice2.isConnected()).thenReturn(true); + when(mCachedDevice1.getGroupId()).thenReturn(GROUP_ID_1); + when(mCachedDevice2.getGroupId()).thenReturn(GROUP_ID_1); + mCachedDeviceManager.mCachedDevices.add(mCachedDevice1); + mCachedDevice1.addMemberDevice(mCachedDevice2); + + assertThat(mCachedDevice1.mDevice).isEqualTo(mDevice1); + assertThat(mCachedDevice2.mDevice).isEqualTo(mDevice2); + assertThat(mHearingAidDeviceManager.onProfileConnectionStateChangedIfProcessed( + mCachedDevice1, BluetoothProfile.STATE_DISCONNECTED)).isTrue(); + + assertThat(mCachedDevice1.mDevice).isEqualTo(mDevice2); + assertThat(mCachedDevice2.mDevice).isEqualTo(mDevice1); + verify(mCachedDevice1).refresh(); + } + @Test public void onActiveDeviceChanged_connected_callSetStrategies() { when(mHelper.getMatchedHearingDeviceAttributes(mCachedDevice1)).thenReturn( diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java index 5245456d98c2..00fb7a1feab1 100644 --- a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java +++ b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java @@ -80,6 +80,7 @@ public class SystemSettings { Settings.System.SIP_RECEIVE_CALLS, Settings.System.POINTER_SPEED, Settings.System.POINTER_FILL_STYLE, + Settings.System.POINTER_SCALE, Settings.System.VIBRATE_ON, Settings.System.VIBRATE_WHEN_RINGING, Settings.System.RINGTONE, diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java index 2c3be4c10f5a..4235bc4157bf 100644 --- a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java +++ b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java @@ -26,6 +26,8 @@ import static android.provider.settings.validators.SettingsValidators.NON_NEGATI import static android.provider.settings.validators.SettingsValidators.NON_NEGATIVE_INTEGER_VALIDATOR; import static android.provider.settings.validators.SettingsValidators.URI_VALIDATOR; import static android.provider.settings.validators.SettingsValidators.VIBRATION_INTENSITY_VALIDATOR; +import static android.view.PointerIcon.DEFAULT_POINTER_SCALE; +import static android.view.PointerIcon.LARGE_POINTER_SCALE; import static android.view.PointerIcon.POINTER_ICON_VECTOR_STYLE_FILL_BEGIN; import static android.view.PointerIcon.POINTER_ICON_VECTOR_STYLE_FILL_END; @@ -211,6 +213,8 @@ public class SystemSettingsValidators { VALIDATORS.put(System.POINTER_FILL_STYLE, new InclusiveIntegerRangeValidator(POINTER_ICON_VECTOR_STYLE_FILL_BEGIN, POINTER_ICON_VECTOR_STYLE_FILL_END)); + VALIDATORS.put(System.POINTER_SCALE, + new InclusiveFloatRangeValidator(DEFAULT_POINTER_SCALE, LARGE_POINTER_SCALE)); VALIDATORS.put(System.TOUCHPAD_POINTER_SPEED, new InclusiveIntegerRangeValidator(-7, 7)); VALIDATORS.put(System.TOUCHPAD_NATURAL_SCROLLING, BOOLEAN_VALIDATOR); VALIDATORS.put(System.TOUCHPAD_TAP_TO_CLICK, BOOLEAN_VALIDATOR); diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java index 625b8e4e2911..384cb7ee9c49 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProtoDumpUtil.java @@ -2915,6 +2915,9 @@ class SettingsProtoDumpUtil { dumpSetting(s, p, Settings.System.POINTER_FILL_STYLE, SystemSettingsProto.Pointer.POINTER_FILL_STYLE); + dumpSetting(s, p, + Settings.System.POINTER_SCALE, + SystemSettingsProto.Pointer.POINTER_SCALE); p.end(pointerToken); dumpSetting(s, p, Settings.System.POINTER_SPEED, diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp index 1e79bb7b8cc8..58c39b477a82 100644 --- a/packages/SystemUI/Android.bp +++ b/packages/SystemUI/Android.bp @@ -368,6 +368,7 @@ filegroup { "tests/src/**/systemui/shared/system/RemoteTransitionTest.java", "tests/src/**/systemui/navigationbar/NavigationBarControllerImplTest.java", "tests/src/**/systemui/bluetooth/qsdialog/AudioSharingInteractorTest.kt", + "tests/src/**/systemui/bluetooth/qsdialog/DeviceItemActionInteractorTest.kt", "tests/src/**/systemui/notetask/quickaffordance/NoteTaskQuickAffordanceConfigTest.kt", "tests/src/**/systemui/notetask/LaunchNotesRoleSettingsTrampolineActivityTest.kt", "tests/src/**/systemui/notetask/shortcut/LaunchNoteTaskActivityTest.kt", @@ -784,7 +785,6 @@ android_app { kotlincflags: ["-Xjvm-default=all"], optimize: { shrink_resources: false, - optimized_shrink_resources: false, proguard_flags_files: ["proguard.flags"], }, @@ -921,7 +921,6 @@ systemui_optimized_java_defaults { optimize: true, shrink: true, shrink_resources: true, - optimized_shrink_resources: true, ignore_warnings: false, proguard_compatibility: false, }, diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index bd6efe5242b2..8a99263637af 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -387,7 +387,7 @@ android:killAfterRestore="false" android:hardwareAccelerated="true" android:label="@string/app_label" - android:icon="@drawable/android14_patch_adaptive" + android:icon="@drawable/android15_patch_adaptive" android:process="com.android.systemui" android:supportsRtl="true" android:theme="@style/Theme.SystemUI" diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/values-pt-rBR/strings.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/values-pt-rBR/strings.xml index 160d310f5f7d..f12278a15a8b 100644 --- a/packages/SystemUI/accessibility/accessibilitymenu/res/values-pt-rBR/strings.xml +++ b/packages/SystemUI/accessibility/accessibilitymenu/res/values-pt-rBR/strings.xml @@ -2,7 +2,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="accessibility_menu_service_name" msgid="730136711554740131">"Menu de acessibilidade"</string> - <string name="accessibility_menu_intro" msgid="3164193281544042394">"\"Acessibilidade\" é um menu grande mostrado na tela para controlar seu dispositivo. Você pode bloquear o dispositivo, controlar o volume e o brilho, fazer capturas de tela e muito mais."</string> + <string name="accessibility_menu_intro" msgid="3164193281544042394">"Com este menu de tamanho grande na tela, você consegue bloquear o dispositivo, ajustar o volume e o brilho, fazer capturas de tela, entre outras funções de controle do aparelho."</string> <string name="assistant_label" msgid="6796392082252272356">"Assistente"</string> <string name="assistant_utterance" msgid="65509599221141377">"Google Assistente"</string> <string name="a11y_settings_label" msgid="3977714687248445050">"Configurações de acessibilidade"</string> diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/values-pt/strings.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/values-pt/strings.xml index 160d310f5f7d..f12278a15a8b 100644 --- a/packages/SystemUI/accessibility/accessibilitymenu/res/values-pt/strings.xml +++ b/packages/SystemUI/accessibility/accessibilitymenu/res/values-pt/strings.xml @@ -2,7 +2,7 @@ <resources xmlns:android="http://schemas.android.com/apk/res/android" xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <string name="accessibility_menu_service_name" msgid="730136711554740131">"Menu de acessibilidade"</string> - <string name="accessibility_menu_intro" msgid="3164193281544042394">"\"Acessibilidade\" é um menu grande mostrado na tela para controlar seu dispositivo. Você pode bloquear o dispositivo, controlar o volume e o brilho, fazer capturas de tela e muito mais."</string> + <string name="accessibility_menu_intro" msgid="3164193281544042394">"Com este menu de tamanho grande na tela, você consegue bloquear o dispositivo, ajustar o volume e o brilho, fazer capturas de tela, entre outras funções de controle do aparelho."</string> <string name="assistant_label" msgid="6796392082252272356">"Assistente"</string> <string name="assistant_utterance" msgid="65509599221141377">"Google Assistente"</string> <string name="a11y_settings_label" msgid="3977714687248445050">"Configurações de acessibilidade"</string> diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig index 85aa33aef26e..c61f996ae7cf 100644 --- a/packages/SystemUI/aconfig/systemui.aconfig +++ b/packages/SystemUI/aconfig/systemui.aconfig @@ -1076,6 +1076,16 @@ flag { } flag { + name: "enable_efficient_display_repository" + namespace: "systemui" + description: "Decide whether to use the new implementation of DisplayRepository that minimizes binder calls and background lock contention." + bug: "345472038" + metadata { + purpose: PURPOSE_BUGFIX + } +} + +flag { name: "notification_media_manager_background_execution" namespace: "systemui" description: "Decide whether to execute binder calls in background thread" diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt index a1f8f1b32f77..b1258ba82bc2 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContainer.kt @@ -68,6 +68,7 @@ object Communal { val Grid = ElementKey("CommunalContent") val LockIcon = ElementKey("CommunalLockIcon") val IndicationArea = ElementKey("CommunalIndicationArea") + val StatusBar = ElementKey("StatusBar") } } @@ -92,6 +93,7 @@ val sceneTransitions = transitions { fade(Communal.Elements.Grid) fade(Communal.Elements.IndicationArea) fade(Communal.Elements.LockIcon) + fade(Communal.Elements.StatusBar) } timestampRange(startMillis = 167, endMillis = 334) { fade(Communal.Elements.Scrim) } } @@ -252,7 +254,10 @@ private fun BoxScope.AnimatedLinearGradient() { Box( Modifier.matchParentSize() .background(colors.primary) - .animatedRadialGradientBackground(colors.primary, colors.primaryContainer) + .animatedRadialGradientBackground( + toColor = colors.primary, + fromColor = colors.primaryContainer.copy(alpha = 0.6f) + ) ) BackgroundTopScrim() } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt index 60b6f62dfa46..b353b5adec4e 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalContent.kt @@ -16,13 +16,16 @@ package com.android.systemui.communal.ui.compose +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.layout.Layout import androidx.compose.ui.unit.IntRect import com.android.compose.animation.scene.SceneScope import com.android.compose.theme.LocalAndroidColorScheme +import com.android.systemui.communal.ui.compose.section.AmbientStatusBarSection import com.android.systemui.communal.ui.viewmodel.CommunalViewModel import com.android.systemui.communal.widgets.WidgetInteractionHandler import com.android.systemui.keyguard.ui.composable.blueprint.BlueprintAlignmentLines @@ -38,19 +41,24 @@ constructor( private val interactionHandler: WidgetInteractionHandler, private val dialogFactory: SystemUIDialogFactory, private val lockSection: LockSection, + private val ambientStatusBarSection: AmbientStatusBarSection, ) { - @Composable fun SceneScope.Content(modifier: Modifier = Modifier) { Layout( modifier = modifier.fillMaxSize(), content = { - CommunalHub( - viewModel = viewModel, - interactionHandler = interactionHandler, - dialogFactory = dialogFactory, - modifier = Modifier.element(Communal.Elements.Grid) - ) + Box(modifier = Modifier.fillMaxSize()) { + with(ambientStatusBarSection) { + AmbientStatusBar(modifier = Modifier.fillMaxWidth()) + } + CommunalHub( + viewModel = viewModel, + interactionHandler = interactionHandler, + dialogFactory = dialogFactory, + modifier = Modifier.element(Communal.Elements.Grid) + ) + } with(lockSection) { LockIcon( overrideColor = LocalAndroidColorScheme.current.onPrimaryContainer, diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt index eccb0724a8d4..f43064aea7b0 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt @@ -26,7 +26,6 @@ import android.widget.RemoteViews import androidx.annotation.VisibleForTesting import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.AnimatedVisibilityScope -import androidx.compose.animation.ExperimentalAnimationApi import androidx.compose.animation.core.LinearEasing import androidx.compose.animation.core.Spring import androidx.compose.animation.core.animateFloatAsState @@ -46,6 +45,7 @@ import androidx.compose.foundation.layout.BoxScope import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth @@ -585,24 +585,23 @@ private fun Toolbar( ) .onSizeChanged { setToolbarSize(it) }, ) { - val spacerModifier = Modifier.width(ButtonDefaults.IconSpacing) - - if (!removeEnabled) { - Button( - modifier = Modifier.align(Alignment.CenterStart), - onClick = onOpenWidgetPicker, - colors = filledButtonColors(), - contentPadding = Dimensions.ButtonPadding - ) { - Icon(Icons.Default.Add, stringResource(R.string.hub_mode_add_widget_button_text)) - Spacer(spacerModifier) - Text( - text = stringResource(R.string.hub_mode_add_widget_button_text), - ) - } + ToolbarButton( + isPrimary = !removeEnabled, + modifier = Modifier.align(Alignment.CenterStart), + onClick = onOpenWidgetPicker, + ) { + Icon(Icons.Default.Add, stringResource(R.string.hub_mode_add_widget_button_text)) + Text( + text = stringResource(R.string.hub_mode_add_widget_button_text), + ) } - if (removeEnabled) { + AnimatedVisibility( + modifier = Modifier.align(Alignment.Center), + visible = removeEnabled, + enter = fadeIn(), + exit = fadeOut() + ) { Button( onClick = onRemoveClicked, colors = filledButtonColors(), @@ -610,33 +609,97 @@ private fun Toolbar( modifier = Modifier.graphicsLayer { alpha = removeButtonAlpha } .onGloballyPositioned { setRemoveButtonCoordinates(it) } - .align(Alignment.Center) ) { - RemoveButtonContent(spacerModifier) + Row( + horizontalArrangement = + Arrangement.spacedBy( + ButtonDefaults.IconSpacing, + Alignment.CenterHorizontally + ), + verticalAlignment = Alignment.CenterVertically + ) { + Icon(Icons.Default.Close, stringResource(R.string.button_to_remove_widget)) + Text( + text = stringResource(R.string.button_to_remove_widget), + ) + } } } - if (!removeEnabled) { - Button( - modifier = Modifier.align(Alignment.CenterEnd), - onClick = onEditDone, - colors = filledButtonColors(), - contentPadding = Dimensions.ButtonPadding + ToolbarButton( + isPrimary = !removeEnabled, + modifier = Modifier.align(Alignment.CenterEnd), + onClick = onEditDone, + ) { + Icon( + Icons.Default.Check, + stringResource(id = R.string.hub_mode_editing_exit_button_text) + ) + Text( + text = stringResource(R.string.hub_mode_editing_exit_button_text), + ) + } + } +} + +/** + * Toolbar button that displays as a filled button if primary, and an outline button if secondary. + */ +@Composable +private fun ToolbarButton( + isPrimary: Boolean = true, + onClick: () -> Unit, + modifier: Modifier = Modifier, + content: @Composable RowScope.() -> Unit +) { + val colors = LocalAndroidColorScheme.current + AnimatedVisibility( + visible = isPrimary, + modifier = modifier, + enter = fadeIn(), + exit = fadeOut() + ) { + Button( + onClick = onClick, + colors = filledButtonColors(), + contentPadding = Dimensions.ButtonPadding, + ) { + Row( + horizontalArrangement = + Arrangement.spacedBy(ButtonDefaults.IconSpacing, Alignment.CenterHorizontally), + verticalAlignment = Alignment.CenterVertically ) { - Icon( - Icons.Default.Check, - stringResource(id = R.string.hub_mode_editing_exit_button_text) - ) - Spacer(spacerModifier) - Text( - text = stringResource(R.string.hub_mode_editing_exit_button_text), - ) + content() + } + } + } + + AnimatedVisibility( + visible = !isPrimary, + modifier = modifier, + enter = fadeIn(), + exit = fadeOut() + ) { + OutlinedButton( + onClick = onClick, + colors = + ButtonDefaults.outlinedButtonColors( + contentColor = colors.primary, + ), + border = BorderStroke(width = 2.0.dp, color = colors.primary), + contentPadding = Dimensions.ButtonPadding, + ) { + Row( + horizontalArrangement = + Arrangement.spacedBy(ButtonDefaults.IconSpacing, Alignment.CenterHorizontally), + verticalAlignment = Alignment.CenterVertically + ) { + content() } } } } -@OptIn(ExperimentalAnimationApi::class) @Composable private fun AnimatedVisibilityScope.ButtonToEditWidgets( onClick: () -> Unit, @@ -739,15 +802,6 @@ private fun PopupOnDismissCtaTile(onHidePopup: () -> Unit) { } @Composable -private fun RemoveButtonContent(spacerModifier: Modifier) { - Icon(Icons.Default.Close, stringResource(R.string.button_to_remove_widget)) - Spacer(spacerModifier) - Text( - text = stringResource(R.string.button_to_remove_widget), - ) -} - -@Composable private fun filledButtonColors(): ButtonColors { val colors = LocalAndroidColorScheme.current return ButtonDefaults.buttonColors( diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/section/AmbientStatusBarSection.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/section/AmbientStatusBarSection.kt new file mode 100644 index 000000000000..3b335fa3141e --- /dev/null +++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/section/AmbientStatusBarSection.kt @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.communal.ui.compose.section + +import android.view.LayoutInflater +import android.view.View +import android.widget.FrameLayout +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.viewinterop.AndroidView +import com.android.compose.animation.scene.SceneScope +import com.android.systemui.ambient.statusbar.dagger.AmbientStatusBarComponent +import com.android.systemui.ambient.statusbar.ui.AmbientStatusBarView +import com.android.systemui.communal.ui.compose.Communal +import com.android.systemui.res.R +import javax.inject.Inject + +class AmbientStatusBarSection +@Inject +constructor( + private val factory: AmbientStatusBarComponent.Factory, +) { + @Composable + fun SceneScope.AmbientStatusBar(modifier: Modifier = Modifier) { + AndroidView( + factory = { context -> + (LayoutInflater.from(context) + .inflate( + /* resource = */ R.layout.ambient_status_bar_view, + /* root = */ FrameLayout(context), + /* attachToRoot = */ false, + ) as AmbientStatusBarView) + .apply { + visibility = View.VISIBLE + factory.create(this).getController().apply { init() } + } + }, + modifier = modifier.element(Communal.Elements.StatusBar) + ) + } +} diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt index 10c403055c8e..68395b463b6a 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/scene/ui/composable/SceneContainerTransitions.kt @@ -59,6 +59,13 @@ val SceneContainerTransitions = transitions { goneToShadeTransition(durationScale = 0.9) } from(Scenes.Gone, to = Scenes.QuickSettings) { goneToQuickSettingsTransition() } + from( + Scenes.Gone, + to = Scenes.QuickSettings, + key = SlightlyFasterShadeCollapse, + ) { + goneToQuickSettingsTransition(durationScale = 0.9) + } from(Scenes.Gone, to = Scenes.QuickSettingsShade) { goneToQuickSettingsShadeTransition() } from(Scenes.Lockscreen, to = Scenes.Bouncer) { lockscreenToBouncerTransition() } from(Scenes.Lockscreen, to = Scenes.Communal) { lockscreenToCommunalTransition() } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt index ac3e015e52a9..b5a10ca1e478 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeHeader.kt @@ -19,7 +19,10 @@ package com.android.systemui.shade.ui.composable import android.view.ContextThemeWrapper import android.view.ViewGroup +import androidx.compose.foundation.background import androidx.compose.foundation.clickable +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.interaction.collectIsHoveredAsState import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -32,7 +35,9 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.widthIn +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.ColorScheme +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass import androidx.compose.runtime.Composable import androidx.compose.runtime.derivedStateOf @@ -40,6 +45,7 @@ import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.TransformOrigin import androidx.compose.ui.graphics.graphicsLayer @@ -58,6 +64,7 @@ import com.android.compose.animation.scene.SceneScope import com.android.compose.animation.scene.TransitionState import com.android.compose.animation.scene.ValueKey import com.android.compose.animation.scene.animateElementFloatAsState +import com.android.compose.modifiers.thenIf import com.android.compose.windowsizeclass.LocalWindowSizeClass import com.android.settingslib.Utils import com.android.systemui.battery.BatteryMeterView @@ -69,6 +76,7 @@ import com.android.systemui.compose.modifiers.sysuiResTag import com.android.systemui.privacy.OngoingPrivacyChip import com.android.systemui.res.R import com.android.systemui.scene.shared.model.Scenes +import com.android.systemui.shade.ui.composable.ShadeHeader.Colors.onScrimDim import com.android.systemui.shade.ui.composable.ShadeHeader.Dimensions.CollapsedHeight import com.android.systemui.shade.ui.composable.ShadeHeader.Values.ClockScale import com.android.systemui.shade.ui.viewmodel.ShadeHeaderViewModel @@ -79,7 +87,6 @@ import com.android.systemui.statusbar.phone.ui.TintedIconManager import com.android.systemui.statusbar.pipeline.mobile.ui.view.ModernShadeCarrierGroupMobileView import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.ShadeCarrierGroupMobileIconViewModel import com.android.systemui.statusbar.policy.Clock -import kotlin.math.max object ShadeHeader { object Elements { @@ -103,6 +110,8 @@ object ShadeHeader { object Colors { val ColorScheme.shadeHeaderText: Color get() = Color.White + val ColorScheme.onScrimDim: Color + get() = Color.DarkGray } object TestTags { @@ -130,7 +139,7 @@ fun SceneScope.CollapsedShadeHeader( val horizontalPadding = max(LocalScreenCornerRadius.current / 2f, Shade.Dimensions.HorizontalPadding) - val useExpandedFormat by + val useExpandedTextFormat by remember(cutoutLocation) { derivedStateOf { cutoutLocation != CutoutLocation.CENTER || @@ -138,6 +147,10 @@ fun SceneScope.CollapsedShadeHeader( } } + val isLargeScreenLayout = + LocalWindowSizeClass.current.widthSizeClass == WindowWidthSizeClass.Medium || + LocalWindowSizeClass.current.widthSizeClass == WindowWidthSizeClass.Expanded + val isPrivacyChipVisible by viewModel.isPrivacyChipVisible.collectAsStateWithLifecycle() // This layout assumes it is globally positioned at (0, 0) and is the @@ -182,22 +195,22 @@ fun SceneScope.CollapsedShadeHeader( Modifier.element(ShadeHeader.Elements.CollapsedContentEnd) .padding(horizontal = horizontalPadding) ) { + if (isLargeScreenLayout) { + ShadeCarrierGroup( + viewModel = viewModel, + modifier = Modifier.align(Alignment.CenterVertically), + ) + } SystemIconContainer( + viewModel = viewModel, + isClickable = isLargeScreenLayout, modifier = Modifier.align(Alignment.CenterVertically) ) { - when (LocalWindowSizeClass.current.widthSizeClass) { - WindowWidthSizeClass.Medium, - WindowWidthSizeClass.Expanded -> - ShadeCarrierGroup( - viewModel = viewModel, - modifier = Modifier.align(Alignment.CenterVertically), - ) - } StatusIcons( viewModel = viewModel, createTintedIconManager = createTintedIconManager, statusBarIconController = statusBarIconController, - useExpandedFormat = useExpandedFormat, + useExpandedFormat = useExpandedTextFormat, modifier = Modifier.align(Alignment.CenterVertically) .padding(end = 6.dp) @@ -206,7 +219,7 @@ fun SceneScope.CollapsedShadeHeader( BatteryIcon( createBatteryMeterViewController = createBatteryMeterViewController, - useExpandedFormat = useExpandedFormat, + useExpandedFormat = useExpandedTextFormat, modifier = Modifier.align(Alignment.CenterVertically), ) } @@ -322,7 +335,7 @@ fun SceneScope.ExpandedShadeHeader( modifier = Modifier.widthIn(max = 90.dp).align(Alignment.CenterVertically), ) Spacer(modifier = Modifier.weight(1f)) - SystemIconContainer { + SystemIconContainer(viewModel = viewModel, isClickable = false) { StatusIcons( viewModel = viewModel, createTintedIconManager = createTintedIconManager, @@ -531,12 +544,30 @@ private fun SceneScope.StatusIcons( @Composable private fun SystemIconContainer( + viewModel: ShadeHeaderViewModel, + isClickable: Boolean, modifier: Modifier = Modifier, content: @Composable RowScope.() -> Unit ) { - // TODO(b/298524053): add hover state for this container + val interactionSource = remember { MutableInteractionSource() } + val isHovered by interactionSource.collectIsHoveredAsState() + + val hoverModifier = Modifier + .clip(RoundedCornerShape(CollapsedHeight / 4)) + .background(MaterialTheme.colorScheme.onScrimDim) + Row( - modifier = modifier.height(CollapsedHeight), + modifier = modifier + .height(CollapsedHeight) + .padding(vertical = CollapsedHeight / 4) + .thenIf(isClickable) { + Modifier.clickable( + interactionSource = interactionSource, + indication = null, + onClick = { viewModel.onSystemIconContainerClicked() }, + ) + } + .thenIf(isHovered) { hoverModifier }, content = content, ) } diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ButtonComponent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ButtonComponent.kt index e1ae80f13312..6d03118645f3 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ButtonComponent.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ButtonComponent.kt @@ -88,7 +88,7 @@ class ButtonComponent( } else { MaterialTheme.colorScheme.surface }, - shape = RoundedCornerShape(28.dp), + shape = RoundedCornerShape(20.dp), contentColor = if (viewModel.isActive) { MaterialTheme.colorScheme.onTertiaryContainer diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ToggleButtonComponent.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ToggleButtonComponent.kt index 1b821d36dceb..bb2daecd3a25 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ToggleButtonComponent.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/button/ui/composable/ToggleButtonComponent.kt @@ -91,7 +91,7 @@ class ToggleButtonComponent( contentDescription = label }, onClick = { onCheckedChange(!viewModel.isActive) }, - shape = RoundedCornerShape(28.dp), + shape = RoundedCornerShape(20.dp), colors = colors, contentPadding = PaddingValues(0.dp) ) { diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt index 7fd3a176acaa..114dcf4fbc7e 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/AnimateSharedAsState.kt @@ -381,18 +381,17 @@ private class AnimatedStateImpl<T, Delta>( // relayout/redraw for nothing. fromValue } else { - // In the case of bouncing, if the value remains constant during the overscroll, we - // should use the value of the scene we are bouncing around. - if (!canOverflow && transition is TransitionState.HasOverscrollProperties) { - val bouncingScene = transition.bouncingScene - if (bouncingScene != null) { - return sharedValue[bouncingScene] + val overscrollSpec = transition.currentOverscrollSpec + val progress = + when { + overscrollSpec == null -> { + if (canOverflow) transition.progress + else transition.progress.fastCoerceIn(0f, 1f) + } + overscrollSpec.scene == transition.toScene -> 1f + else -> 0f } - } - val progress = - if (canOverflow) transition.progress - else transition.progress.fastCoerceIn(0f, 1f) sharedValue.type.lerp(fromValue, toValue, progress) } } else fromValue ?: toValue diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt index 980982a30926..5611c6ec1dee 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/Element.kt @@ -39,6 +39,8 @@ import androidx.compose.ui.layout.MeasureScope import androidx.compose.ui.layout.Placeable import androidx.compose.ui.node.DrawModifierNode import androidx.compose.ui.node.ModifierNodeElement +import androidx.compose.ui.node.TraversableNode +import androidx.compose.ui.node.traverseDescendants import androidx.compose.ui.platform.testTag import androidx.compose.ui.unit.Constraints import androidx.compose.ui.unit.IntSize @@ -165,7 +167,7 @@ internal class ElementNode( private var currentTransitions: List<TransitionState.Transition>, private var scene: Scene, private var key: ElementKey, -) : Modifier.Node(), DrawModifierNode, ApproachLayoutModifierNode { +) : Modifier.Node(), DrawModifierNode, ApproachLayoutModifierNode, TraversableNode { private var _element: Element? = null private val element: Element get() = _element!! @@ -174,6 +176,8 @@ internal class ElementNode( private val sceneState: Element.SceneState get() = _sceneState!! + override val traverseKey: Any = ElementTraverseKey + override fun onAttach() { super.onAttach() updateElementAndSceneValues() @@ -289,18 +293,15 @@ internal class ElementNode( val isOtherSceneOverscrolling = overscrollScene != null && overscrollScene != scene.key val isNotPartOfAnyOngoingTransitions = transitions.isNotEmpty() && transition == null if (isNotPartOfAnyOngoingTransitions || isOtherSceneOverscrolling) { - sceneState.lastOffset = Offset.Unspecified - sceneState.lastScale = Scale.Unspecified - sceneState.lastAlpha = Element.AlphaUnspecified + recursivelyClearPlacementValues() + sceneState.lastSize = Element.SizeUnspecified val placeable = measurable.measure(constraints) - sceneState.lastSize = placeable.size() - return layout(placeable.width, placeable.height) { /* Do not place */ } } val placeable = - measure(layoutImpl, scene, element, transition, sceneState, measurable, constraints) + measure(layoutImpl, element, transition, sceneState, measurable, constraints) sceneState.lastSize = placeable.size() return layout(placeable.width, placeable.height) { place(transition, placeable) } } @@ -315,13 +316,10 @@ internal class ElementNode( // scene when idle. val coords = coordinates ?: error("Element ${element.key} does not have any coordinates") - val targetOffsetInScene = lookaheadScopeCoordinates.localLookaheadPositionOf(coords) // No need to place the element in this scene if we don't want to draw it anyways. if (!shouldPlaceElement(layoutImpl, scene.key, element, transition)) { - sceneState.lastOffset = Offset.Unspecified - sceneState.lastScale = Scale.Unspecified - sceneState.lastAlpha = Element.AlphaUnspecified + recursivelyClearPlacementValues() return } @@ -329,12 +327,11 @@ internal class ElementNode( val targetOffset = computeValue( layoutImpl, - scene, + sceneState, element, transition, sceneValue = { it.targetOffset }, transformation = { it.offset }, - idleValue = targetOffsetInScene, currentValue = { currentOffset }, isSpecified = { it != Offset.Unspecified }, ::lerp, @@ -395,18 +392,37 @@ internal class ElementNode( return@placeWithLayer } - alpha = elementAlpha(layoutImpl, scene, element, transition, sceneState) + alpha = elementAlpha(layoutImpl, element, transition, sceneState) compositingStrategy = CompositingStrategy.ModulateAlpha } } } } + /** + * Recursively clear the last placement values on this node and all descendants ElementNodes. + * This should be called when this node is not placed anymore, so that we correctly clear values + * for the descendants for which approachMeasure() won't be called. + */ + private fun recursivelyClearPlacementValues() { + fun Element.SceneState.clearLastPlacementValues() { + lastOffset = Offset.Unspecified + lastScale = Scale.Unspecified + lastAlpha = Element.AlphaUnspecified + } + + sceneState.clearLastPlacementValues() + traverseDescendants(ElementTraverseKey) { node -> + (node as ElementNode).sceneState.clearLastPlacementValues() + TraversableNode.Companion.TraverseDescendantsAction.ContinueTraversal + } + } + override fun ContentDrawScope.draw() { element.wasDrawnInAnyScene = true val transition = elementTransition(layoutImpl, element, currentTransitions) - val drawScale = getDrawScale(layoutImpl, scene, element, transition, sceneState) + val drawScale = getDrawScale(layoutImpl, element, transition, sceneState) if (drawScale == Scale.Default) { drawContent() } else { @@ -421,6 +437,8 @@ internal class ElementNode( } companion object { + private val ElementTraverseKey = Any() + private fun maybePruneMaps( layoutImpl: SceneTransitionLayoutImpl, element: Element, @@ -494,22 +512,23 @@ private fun prepareInterruption( // Remove the interruption values to all scenes but the scene(s) where the element will be // placed, to make sure that interruption deltas are computed only right after this interruption // is prepared. - fun maybeCleanPlacementValuesBeforeInterruption(sceneState: Element.SceneState) { + fun cleanInterruptionValues(sceneState: Element.SceneState) { + sceneState.sizeInterruptionDelta = IntSize.Zero + sceneState.offsetInterruptionDelta = Offset.Zero + sceneState.alphaInterruptionDelta = 0f + sceneState.scaleInterruptionDelta = Scale.Zero + if (!shouldPlaceElement(layoutImpl, sceneState.scene, element, transition)) { sceneState.offsetBeforeInterruption = Offset.Unspecified sceneState.alphaBeforeInterruption = Element.AlphaUnspecified sceneState.scaleBeforeInterruption = Scale.Unspecified - - sceneState.offsetInterruptionDelta = Offset.Zero - sceneState.alphaInterruptionDelta = 0f - sceneState.scaleInterruptionDelta = Scale.Zero } } - previousFromState?.let { maybeCleanPlacementValuesBeforeInterruption(it) } - previousToState?.let { maybeCleanPlacementValuesBeforeInterruption(it) } - fromState?.let { maybeCleanPlacementValuesBeforeInterruption(it) } - toState?.let { maybeCleanPlacementValuesBeforeInterruption(it) } + previousFromState?.let { cleanInterruptionValues(it) } + previousToState?.let { cleanInterruptionValues(it) } + fromState?.let { cleanInterruptionValues(it) } + toState?.let { cleanInterruptionValues(it) } } /** @@ -780,7 +799,6 @@ private fun isElementOpaque( */ private fun elementAlpha( layoutImpl: SceneTransitionLayoutImpl, - scene: Scene, element: Element, transition: TransitionState.Transition?, sceneState: Element.SceneState, @@ -788,12 +806,11 @@ private fun elementAlpha( val alpha = computeValue( layoutImpl, - scene, + sceneState, element, transition, sceneValue = { 1f }, transformation = { it.alpha }, - idleValue = 1f, currentValue = { 1f }, isSpecified = { true }, ::lerp, @@ -841,9 +858,8 @@ private fun interruptedAlpha( ) } -private fun ApproachMeasureScope.measure( +private fun measure( layoutImpl: SceneTransitionLayoutImpl, - scene: Scene, element: Element, transition: TransitionState.Transition?, sceneState: Element.SceneState, @@ -858,12 +874,11 @@ private fun ApproachMeasureScope.measure( val targetSize = computeValue( layoutImpl, - scene, + sceneState, element, transition, sceneValue = { it.targetSize }, transformation = { it.size }, - idleValue = lookaheadSize, currentValue = { measurable.measure(constraints).also { maybePlaceable = it }.size() }, isSpecified = { it != Element.SizeUnspecified }, ::lerp, @@ -909,7 +924,6 @@ private fun Placeable.size(): IntSize = IntSize(width, height) private fun ContentDrawScope.getDrawScale( layoutImpl: SceneTransitionLayoutImpl, - scene: Scene, element: Element, transition: TransitionState.Transition?, sceneState: Element.SceneState, @@ -917,12 +931,11 @@ private fun ContentDrawScope.getDrawScale( val scale = computeValue( layoutImpl, - scene, + sceneState, element, transition, sceneValue = { Scale.Default }, transformation = { it.drawScale }, - idleValue = Scale.Default, currentValue = { Scale.Default }, isSpecified = { true }, ::lerp, @@ -989,11 +1002,12 @@ private fun ContentDrawScope.getDrawScale( * Measurable. * * @param layoutImpl the [SceneTransitionLayoutImpl] associated to [element]. - * @param scene the scene containing [element]. + * @param currentSceneState the scene state of the scene for which we are computing the value. Note + * that during interruptions, this could be the state of a scene that is neither + * [transition.toScene] nor [transition.fromScene]. * @param element the element being animated. * @param sceneValue the value being animated. * @param transformation the transformation associated to the value being animated. - * @param idleValue the value when idle, i.e. when there is no transition happening. * @param currentValue the value that would be used if it is not transformed. Note that this is * different than [idleValue] even if the value is not transformed directly because it could be * impacted by the transformations on other elements, like a parent that is being translated or @@ -1003,12 +1017,11 @@ private fun ContentDrawScope.getDrawScale( */ private inline fun <T> computeValue( layoutImpl: SceneTransitionLayoutImpl, - scene: Scene, + currentSceneState: Element.SceneState, element: Element, transition: TransitionState.Transition?, sceneValue: (Element.SceneState) -> T, transformation: (ElementTransformations) -> PropertyTransformation<T>?, - idleValue: T, currentValue: () -> T, isSpecified: (T) -> Boolean, lerp: (T, T, Float) -> T, @@ -1030,19 +1043,22 @@ private inline fun <T> computeValue( if (fromState == null && toState == null) { // TODO(b/311600838): Throw an exception instead once layers of disposed elements are not // run anymore. - return idleValue + return sceneValue(currentSceneState) } + val currentScene = currentSceneState.scene if (transition is TransitionState.HasOverscrollProperties) { val overscroll = transition.currentOverscrollSpec - if (overscroll?.scene == scene.key) { - val elementSpec = overscroll.transformationSpec.transformations(element.key, scene.key) + if (overscroll?.scene == currentScene) { + val elementSpec = + overscroll.transformationSpec.transformations(element.key, currentScene) val propertySpec = transformation(elementSpec) ?: return currentValue() - val overscrollState = checkNotNull(if (scene.key == toScene) toState else fromState) + val overscrollState = checkNotNull(if (currentScene == toScene) toState else fromState) + val idleValue = sceneValue(overscrollState) val targetValue = propertySpec.transform( layoutImpl, - scene, + currentScene, element, overscrollState, transition, @@ -1086,24 +1102,30 @@ private inline fun <T> computeValue( return if (start == end) start else lerp(start, end, transition.progress) } - val transformation = - transformation(transition.transformationSpec.transformations(element.key, scene.key)) - // If there is no transformation explicitly associated to this element value, let's use - // the value given by the system (like the current position and size given by the layout - // pass). - ?: return currentValue() - // Get the transformed value, i.e. the target value at the beginning (for entering elements) or // end (for leaving elements) of the transition. val sceneState = checkNotNull( when { - isSharedElement && scene.key == fromScene -> fromState + isSharedElement && currentScene == fromScene -> fromState isSharedElement -> toState else -> fromState ?: toState } ) + // The scene for which we compute the transformation. Note that this is not necessarily + // [currentScene] because [currentScene] could be a different scene than the transition + // fromScene or toScene during interruptions. + val scene = sceneState.scene + + val transformation = + transformation(transition.transformationSpec.transformations(element.key, scene)) + // If there is no transformation explicitly associated to this element value, let's use + // the value given by the system (like the current position and size given by the layout + // pass). + ?: return currentValue() + + val idleValue = sceneValue(sceneState) val targetValue = transformation.transform( layoutImpl, @@ -1125,7 +1147,7 @@ private inline fun <T> computeValue( val rangeProgress = transformation.range?.progress(progress) ?: progress // Interpolate between the value at rest and the value before entering/after leaving. - val isEntering = scene.key == toScene + val isEntering = scene == toScene return if (isEntering) { lerp(targetValue, idleValue, rangeProgress) } else { diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt index f32720c4716d..7ea8cbdd7a97 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutImpl.kt @@ -293,7 +293,15 @@ private class LayoutNode(var layoutImpl: SceneTransitionLayoutImpl) : width = fromSize.width height = fromSize.height } else { - val size = lerp(fromSize, toSize, transition.progress) + val overscrollSpec = transition.currentOverscrollSpec + val progress = + when { + overscrollSpec == null -> transition.progress + overscrollSpec.scene == transition.toScene -> 1f + else -> 0f + } + + val size = lerp(fromSize, toSize, progress) width = size.width.coerceAtLeast(0) height = size.height.coerceAtLeast(0) } diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt index 6a178c8b0c25..a8df6f4ae8b1 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/SceneTransitionLayoutState.kt @@ -768,7 +768,7 @@ internal class HoistedSceneTransitionLayoutState( /** A [MutableSceneTransitionLayoutState] that holds the value for the current scene. */ internal class MutableSceneTransitionLayoutStateImpl( initialScene: SceneKey, - override var transitions: SceneTransitions, + override var transitions: SceneTransitions = transitions {}, private val canChangeScene: (SceneKey) -> Boolean = { true }, stateLinks: List<StateLink> = emptyList(), enableInterruptions: Boolean = DEFAULT_INTERRUPTIONS_ENABLED, diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredSize.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredSize.kt index b54afae70a55..73ee4512c31f 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredSize.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredSize.kt @@ -20,7 +20,6 @@ import androidx.compose.ui.unit.IntSize import com.android.compose.animation.scene.Element import com.android.compose.animation.scene.ElementKey import com.android.compose.animation.scene.ElementMatcher -import com.android.compose.animation.scene.Scene import com.android.compose.animation.scene.SceneKey import com.android.compose.animation.scene.SceneTransitionLayoutImpl import com.android.compose.animation.scene.TransitionState @@ -34,7 +33,7 @@ internal class AnchoredSize( ) : PropertyTransformation<IntSize> { override fun transform( layoutImpl: SceneTransitionLayoutImpl, - scene: Scene, + scene: SceneKey, element: Element, sceneState: Element.SceneState, transition: TransitionState.Transition, @@ -60,7 +59,7 @@ internal class AnchoredSize( // This simple implementation assumes that the size of [element] is the same as the size of // the [anchor] in [scene], so simply transform to the size of the anchor in the other // scene. - return if (scene.key == transition.fromScene) { + return if (scene == transition.fromScene) { anchorSizeIn(transition.toScene) } else { anchorSizeIn(transition.fromScene) diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredTranslate.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredTranslate.kt index 2bab4f88ffdd..70dca4c065d3 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredTranslate.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/AnchoredTranslate.kt @@ -21,7 +21,6 @@ import androidx.compose.ui.geometry.isSpecified import com.android.compose.animation.scene.Element import com.android.compose.animation.scene.ElementKey import com.android.compose.animation.scene.ElementMatcher -import com.android.compose.animation.scene.Scene import com.android.compose.animation.scene.SceneKey import com.android.compose.animation.scene.SceneTransitionLayoutImpl import com.android.compose.animation.scene.TransitionState @@ -33,7 +32,7 @@ internal class AnchoredTranslate( ) : PropertyTransformation<Offset> { override fun transform( layoutImpl: SceneTransitionLayoutImpl, - scene: Scene, + scene: SceneKey, element: Element, sceneState: Element.SceneState, transition: TransitionState.Transition, @@ -61,7 +60,7 @@ internal class AnchoredTranslate( anchorOffsetIn(transition.toScene) ?: throwException(transition.toScene) val offset = anchorToOffset - anchorFromOffset - return if (scene.key == transition.toScene) { + return if (scene == transition.toScene) { Offset( value.x - offset.x, value.y - offset.y, diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/DrawScale.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/DrawScale.kt index 6704a3bbeff2..98c2dd3dc1cc 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/DrawScale.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/DrawScale.kt @@ -20,7 +20,7 @@ import androidx.compose.ui.geometry.Offset import com.android.compose.animation.scene.Element import com.android.compose.animation.scene.ElementMatcher import com.android.compose.animation.scene.Scale -import com.android.compose.animation.scene.Scene +import com.android.compose.animation.scene.SceneKey import com.android.compose.animation.scene.SceneTransitionLayoutImpl import com.android.compose.animation.scene.TransitionState @@ -37,7 +37,7 @@ internal class DrawScale( override fun transform( layoutImpl: SceneTransitionLayoutImpl, - scene: Scene, + scene: SceneKey, element: Element, sceneState: Element.SceneState, transition: TransitionState.Transition, diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/EdgeTranslate.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/EdgeTranslate.kt index 191a8fbcd009..aa8dc38fdd8f 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/EdgeTranslate.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/EdgeTranslate.kt @@ -20,7 +20,7 @@ import androidx.compose.ui.geometry.Offset import com.android.compose.animation.scene.Edge import com.android.compose.animation.scene.Element import com.android.compose.animation.scene.ElementMatcher -import com.android.compose.animation.scene.Scene +import com.android.compose.animation.scene.SceneKey import com.android.compose.animation.scene.SceneTransitionLayoutImpl import com.android.compose.animation.scene.TransitionState @@ -32,13 +32,13 @@ internal class EdgeTranslate( ) : PropertyTransformation<Offset> { override fun transform( layoutImpl: SceneTransitionLayoutImpl, - scene: Scene, + scene: SceneKey, element: Element, sceneState: Element.SceneState, transition: TransitionState.Transition, value: Offset ): Offset { - val sceneSize = scene.targetSize + val sceneSize = layoutImpl.scene(scene).targetSize val elementSize = sceneState.targetSize if (elementSize == Element.SizeUnspecified) { return value diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Fade.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Fade.kt index 41f626e24e79..ada814e04ab2 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Fade.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Fade.kt @@ -18,7 +18,7 @@ package com.android.compose.animation.scene.transformation import com.android.compose.animation.scene.Element import com.android.compose.animation.scene.ElementMatcher -import com.android.compose.animation.scene.Scene +import com.android.compose.animation.scene.SceneKey import com.android.compose.animation.scene.SceneTransitionLayoutImpl import com.android.compose.animation.scene.TransitionState @@ -28,7 +28,7 @@ internal class Fade( ) : PropertyTransformation<Float> { override fun transform( layoutImpl: SceneTransitionLayoutImpl, - scene: Scene, + scene: SceneKey, element: Element, sceneState: Element.SceneState, transition: TransitionState.Transition, diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/ScaleSize.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/ScaleSize.kt index f5207dc4d345..dca8f8521f1a 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/ScaleSize.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/ScaleSize.kt @@ -19,7 +19,7 @@ package com.android.compose.animation.scene.transformation import androidx.compose.ui.unit.IntSize import com.android.compose.animation.scene.Element import com.android.compose.animation.scene.ElementMatcher -import com.android.compose.animation.scene.Scene +import com.android.compose.animation.scene.SceneKey import com.android.compose.animation.scene.SceneTransitionLayoutImpl import com.android.compose.animation.scene.TransitionState import kotlin.math.roundToInt @@ -35,7 +35,7 @@ internal class ScaleSize( ) : PropertyTransformation<IntSize> { override fun transform( layoutImpl: SceneTransitionLayoutImpl, - scene: Scene, + scene: SceneKey, element: Element, sceneState: Element.SceneState, transition: TransitionState.Transition, diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Transformation.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Transformation.kt index 603f7ba947c4..7be9ce1e39fc 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Transformation.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Transformation.kt @@ -21,7 +21,7 @@ import androidx.compose.ui.util.fastCoerceAtMost import androidx.compose.ui.util.fastCoerceIn import com.android.compose.animation.scene.Element import com.android.compose.animation.scene.ElementMatcher -import com.android.compose.animation.scene.Scene +import com.android.compose.animation.scene.SceneKey import com.android.compose.animation.scene.SceneTransitionLayoutImpl import com.android.compose.animation.scene.TransitionState @@ -61,7 +61,7 @@ internal sealed interface PropertyTransformation<T> : Transformation { // to these internal classes. fun transform( layoutImpl: SceneTransitionLayoutImpl, - scene: Scene, + scene: SceneKey, element: Element, sceneState: Element.SceneState, transition: TransitionState.Transition, diff --git a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Translate.kt b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Translate.kt index 849c9d71ec2f..f066511f68ab 100644 --- a/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Translate.kt +++ b/packages/SystemUI/compose/scene/src/com/android/compose/animation/scene/transformation/Translate.kt @@ -22,7 +22,7 @@ import androidx.compose.ui.unit.dp import com.android.compose.animation.scene.Element import com.android.compose.animation.scene.ElementMatcher import com.android.compose.animation.scene.OverscrollScope -import com.android.compose.animation.scene.Scene +import com.android.compose.animation.scene.SceneKey import com.android.compose.animation.scene.SceneTransitionLayoutImpl import com.android.compose.animation.scene.TransitionState @@ -33,7 +33,7 @@ internal class Translate( ) : PropertyTransformation<Offset> { override fun transform( layoutImpl: SceneTransitionLayoutImpl, - scene: Scene, + scene: SceneKey, element: Element, sceneState: Element.SceneState, transition: TransitionState.Transition, @@ -55,7 +55,7 @@ internal class OverscrollTranslate( ) : PropertyTransformation<Offset> { override fun transform( layoutImpl: SceneTransitionLayoutImpl, - scene: Scene, + scene: SceneKey, element: Element, sceneState: Element.SceneState, transition: TransitionState.Transition, diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/AnimatedSharedAsStateTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/AnimatedSharedAsStateTest.kt index 6e8b208ea9e8..a7889e2fac58 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/AnimatedSharedAsStateTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/AnimatedSharedAsStateTest.kt @@ -18,10 +18,13 @@ package com.android.compose.animation.scene import androidx.compose.animation.core.LinearEasing import androidx.compose.animation.core.tween +import androidx.compose.foundation.gestures.Orientation import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.SideEffect import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue import androidx.compose.runtime.snapshotFlow import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color @@ -443,4 +446,56 @@ class AnimatedSharedAsStateTest { assertThat(lastValues[bar]?.get(SceneC)).isWithin(0.001f).of(7f) assertThat(lastValues[bar]?.get(SceneD)).isWithin(0.001f).of(7f) } + + @Test + fun animatedValueDoesNotOverscrollWhenOverscrollIsSpecified() { + val state = + rule.runOnUiThread { + MutableSceneTransitionLayoutStateImpl( + SceneA, + transitions { overscroll(SceneB, Orientation.Horizontal) } + ) + } + + val key = ValueKey("foo") + val lastValues = mutableMapOf<SceneKey, Float>() + + @Composable + fun SceneScope.animateFloat(value: Float, key: ValueKey) { + val animatedValue = animateSceneFloatAsState(value, key) + LaunchedEffect(animatedValue) { + snapshotFlow { animatedValue.value }.collect { lastValues[sceneKey] = it } + } + } + + rule.setContent { + SceneTransitionLayout(state) { + scene(SceneA) { animateFloat(0f, key) } + scene(SceneB) { animateFloat(100f, key) } + } + } + + // Overscroll on A at -100%: value should be interpolated given that there is no overscroll + // defined for scene A. + var progress by mutableStateOf(-1f) + rule.runOnIdle { + state.startTransition(transition(from = SceneA, to = SceneB, progress = { progress })) + } + rule.waitForIdle() + assertThat(lastValues[SceneA]).isWithin(0.001f).of(-100f) + assertThat(lastValues[SceneB]).isWithin(0.001f).of(-100f) + + // Middle of the transition. + progress = 0.5f + rule.waitForIdle() + assertThat(lastValues[SceneA]).isWithin(0.001f).of(50f) + assertThat(lastValues[SceneB]).isWithin(0.001f).of(50f) + + // Overscroll on B at 200%: value should not be interpolated given that there is an + // overscroll defined for scene B. + progress = 2f + rule.waitForIdle() + assertThat(lastValues[SceneA]).isWithin(0.001f).of(100f) + assertThat(lastValues[SceneB]).isWithin(0.001f).of(100f) + } } diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt index 41cacb4c71fc..a18da7364983 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/ElementTest.kt @@ -47,10 +47,12 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.geometry.Offset import androidx.compose.ui.layout.approachLayout import androidx.compose.ui.platform.LocalViewConfiguration +import androidx.compose.ui.platform.testTag import androidx.compose.ui.test.assertIsDisplayed import androidx.compose.ui.test.assertIsNotDisplayed import androidx.compose.ui.test.assertPositionInRootIsEqualTo import androidx.compose.ui.test.assertTopPositionInRootIsEqualTo +import androidx.compose.ui.test.hasTestTag import androidx.compose.ui.test.hasText import androidx.compose.ui.test.junit4.createComposeRule import androidx.compose.ui.test.onNodeWithTag @@ -1719,4 +1721,220 @@ class ElementTest { rule.onNode(isElement(TestElements.Foo, SceneB)).assertIsNotDisplayed() rule.onNode(isElement(TestElements.Foo, SceneC)).assertPositionInRootIsEqualTo(40.dp, 40.dp) } + + @Test + fun lastPlacementValuesAreClearedOnNestedElements() { + val state = rule.runOnIdle { MutableSceneTransitionLayoutStateImpl(SceneA) } + + @Composable + fun SceneScope.NestedFooBar() { + Box(Modifier.element(TestElements.Foo)) { + Box(Modifier.element(TestElements.Bar).size(10.dp)) + } + } + + lateinit var layoutImpl: SceneTransitionLayoutImpl + rule.setContent { + SceneTransitionLayoutForTesting(state, onLayoutImpl = { layoutImpl = it }) { + scene(SceneA) { NestedFooBar() } + scene(SceneB) { NestedFooBar() } + } + } + + // Idle on A: composed and placed only in B. + rule.onNode(isElement(TestElements.Foo, SceneA)).assertIsDisplayed() + rule.onNode(isElement(TestElements.Bar, SceneA)).assertIsDisplayed() + rule.onNode(isElement(TestElements.Foo, SceneB)).assertDoesNotExist() + rule.onNode(isElement(TestElements.Bar, SceneB)).assertDoesNotExist() + + assertThat(layoutImpl.elements).containsKey(TestElements.Foo) + assertThat(layoutImpl.elements).containsKey(TestElements.Bar) + val foo = layoutImpl.elements.getValue(TestElements.Foo) + val bar = layoutImpl.elements.getValue(TestElements.Bar) + + assertThat(foo.sceneStates).containsKey(SceneA) + assertThat(bar.sceneStates).containsKey(SceneA) + assertThat(foo.sceneStates).doesNotContainKey(SceneB) + assertThat(bar.sceneStates).doesNotContainKey(SceneB) + + val fooInA = foo.sceneStates.getValue(SceneA) + val barInA = bar.sceneStates.getValue(SceneA) + assertThat(fooInA.lastOffset).isNotEqualTo(Offset.Unspecified) + assertThat(fooInA.lastAlpha).isNotEqualTo(Element.AlphaUnspecified) + assertThat(fooInA.lastScale).isNotEqualTo(Scale.Unspecified) + + assertThat(barInA.lastOffset).isNotEqualTo(Offset.Unspecified) + assertThat(barInA.lastAlpha).isNotEqualTo(Element.AlphaUnspecified) + assertThat(barInA.lastScale).isNotEqualTo(Scale.Unspecified) + + // A => B: composed in both and placed only in B. + rule.runOnUiThread { state.startTransition(transition(from = SceneA, to = SceneB)) } + rule.onNode(isElement(TestElements.Foo, SceneA)).assertExists().assertIsNotDisplayed() + rule.onNode(isElement(TestElements.Bar, SceneA)).assertExists().assertIsNotDisplayed() + rule.onNode(isElement(TestElements.Foo, SceneB)).assertIsDisplayed() + rule.onNode(isElement(TestElements.Bar, SceneB)).assertIsDisplayed() + + assertThat(foo.sceneStates).containsKey(SceneB) + assertThat(bar.sceneStates).containsKey(SceneB) + + val fooInB = foo.sceneStates.getValue(SceneB) + val barInB = bar.sceneStates.getValue(SceneB) + assertThat(fooInA.lastOffset).isEqualTo(Offset.Unspecified) + assertThat(fooInA.lastAlpha).isEqualTo(Element.AlphaUnspecified) + assertThat(fooInA.lastScale).isEqualTo(Scale.Unspecified) + assertThat(fooInB.lastOffset).isNotEqualTo(Offset.Unspecified) + assertThat(fooInB.lastAlpha).isNotEqualTo(Element.AlphaUnspecified) + assertThat(fooInB.lastScale).isNotEqualTo(Scale.Unspecified) + + assertThat(barInA.lastOffset).isEqualTo(Offset.Unspecified) + assertThat(barInA.lastAlpha).isEqualTo(Element.AlphaUnspecified) + assertThat(barInA.lastScale).isEqualTo(Scale.Unspecified) + assertThat(barInB.lastOffset).isNotEqualTo(Offset.Unspecified) + assertThat(barInB.lastAlpha).isNotEqualTo(Element.AlphaUnspecified) + assertThat(barInB.lastScale).isNotEqualTo(Scale.Unspecified) + } + + @Test + fun currentTransitionSceneIsUsedToComputeElementValues() = runTest { + val state = + rule.runOnIdle { + MutableSceneTransitionLayoutStateImpl( + SceneA, + transitions { + from(SceneB, to = SceneC) { + scaleSize(TestElements.Foo, width = 2f, height = 3f) + } + } + ) + } + + @Composable + fun SceneScope.Foo() { + Box(Modifier.testTag("fooParentIn${sceneKey.debugName}")) { + Box(Modifier.element(TestElements.Foo).size(20.dp)) + } + } + + rule.setContent { + SceneTransitionLayout(state, Modifier.size(200.dp)) { + scene(SceneA) { Foo() } + scene(SceneB) {} + scene(SceneC) { Foo() } + } + } + + // We have 2 transitions: + // - A => B at 100% + // - B => C at 0% + // So Foo should have a size of (40dp, 60dp) in both A and C given that it is scaling its + // size in B => C. + rule.runOnUiThread { + state.startTransition( + transition(from = SceneA, to = SceneB, progress = { 1f }, onFinish = neverFinish()) + ) + state.startTransition(transition(from = SceneB, to = SceneC, progress = { 0f })) + } + + rule.onNode(hasTestTag("fooParentInSceneA")).assertSizeIsEqualTo(40.dp, 60.dp) + rule.onNode(hasTestTag("fooParentInSceneC")).assertSizeIsEqualTo(40.dp, 60.dp) + } + + @Test + fun interruptionDeltasAreProperlyCleaned() = runTest { + val state = rule.runOnIdle { MutableSceneTransitionLayoutStateImpl(SceneA) } + + @Composable + fun SceneScope.Foo(offset: Dp) { + Box(Modifier.fillMaxSize()) { + Box(Modifier.offset(offset, offset).element(TestElements.Foo).size(20.dp)) + } + } + + rule.setContent { + SceneTransitionLayout(state, Modifier.size(200.dp)) { + scene(SceneA) { Foo(offset = 0.dp) } + scene(SceneB) { Foo(offset = 20.dp) } + scene(SceneC) { Foo(offset = 40.dp) } + } + } + + // Start A => B at 50%. + val aToB = + transition(from = SceneA, to = SceneB, progress = { 0.5f }, onFinish = neverFinish()) + rule.runOnUiThread { state.startTransition(aToB) } + rule.onNode(isElement(TestElements.Foo, SceneB)).assertPositionInRootIsEqualTo(10.dp, 10.dp) + + // Start B => C at 0%. This will compute an interruption delta of (-10dp, -10dp) so that the + // position of Foo is unchanged and converges to (20dp, 20dp). + var interruptionProgress by mutableStateOf(1f) + val bToC = + transition( + from = SceneB, + to = SceneC, + progress = { 0f }, + interruptionProgress = { interruptionProgress }, + onFinish = neverFinish(), + ) + rule.runOnUiThread { state.startTransition(bToC) } + rule.onNode(isElement(TestElements.Foo, SceneC)).assertPositionInRootIsEqualTo(10.dp, 10.dp) + + // Finish the interruption and leave the transition progress at 0f. We should be at the same + // state as in B. + interruptionProgress = 0f + rule.onNode(isElement(TestElements.Foo, SceneC)).assertPositionInRootIsEqualTo(20.dp, 20.dp) + + // Finish both transitions but directly start a new one B => A with interruption progress + // 100%. We should be at (20dp, 20dp), unless the interruption deltas have not been + // correctly cleaned. + rule.runOnUiThread { + state.finishTransition(aToB, idleScene = SceneB) + state.finishTransition(bToC, idleScene = SceneB) + state.startTransition( + transition( + from = SceneB, + to = SceneA, + progress = { 0f }, + interruptionProgress = { 1f }, + ) + ) + } + rule.onNode(isElement(TestElements.Foo, SceneB)).assertPositionInRootIsEqualTo(20.dp, 20.dp) + } + + @Test + fun lastSizeIsUnspecifiedWhenOverscrollingOtherScene() = runTest { + val state = + rule.runOnIdle { + MutableSceneTransitionLayoutStateImpl( + SceneA, + transitions { overscroll(SceneA, Orientation.Horizontal) } + ) + } + + @Composable + fun SceneScope.Foo() { + Box(Modifier.element(TestElements.Foo).size(10.dp)) + } + + lateinit var layoutImpl: SceneTransitionLayoutImpl + rule.setContent { + SceneTransitionLayoutForTesting(state, onLayoutImpl = { layoutImpl = it }) { + scene(SceneA) { Foo() } + scene(SceneB) { Foo() } + } + } + + // Overscroll A => B on A. + rule.runOnUiThread { + state.startTransition( + transition(from = SceneA, to = SceneB, progress = { -1f }, onFinish = neverFinish()) + ) + } + rule.waitForIdle() + + assertThat( + layoutImpl.elements.getValue(TestElements.Foo).sceneStates.getValue(SceneB).lastSize + ) + .isEqualTo(Element.SizeUnspecified) + } } diff --git a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt index 08532bd72e33..a8dd572c4d50 100644 --- a/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt +++ b/packages/SystemUI/compose/scene/tests/src/com/android/compose/animation/scene/SceneTransitionLayoutTest.kt @@ -21,6 +21,7 @@ import androidx.compose.animation.core.FastOutSlowInEasing import androidx.compose.animation.core.LinearEasing import androidx.compose.animation.core.tween import androidx.compose.foundation.background +import androidx.compose.foundation.gestures.Orientation import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.offset @@ -333,6 +334,42 @@ class SceneTransitionLayoutTest { } @Test + fun layoutSizeDoesNotOverscrollWhenOverscrollIsSpecified() { + val state = + rule.runOnUiThread { + MutableSceneTransitionLayoutStateImpl( + SceneA, + transitions { overscroll(SceneB, Orientation.Horizontal) } + ) + } + + val layoutTag = "layout" + rule.setContent { + SceneTransitionLayout(state, Modifier.testTag(layoutTag)) { + scene(SceneA) { Box(Modifier.size(50.dp)) } + scene(SceneB) { Box(Modifier.size(70.dp)) } + } + } + + // Overscroll on A at -100%: size should be interpolated given that there is no overscroll + // defined for scene A. + var progress by mutableStateOf(-1f) + rule.runOnIdle { + state.startTransition(transition(from = SceneA, to = SceneB, progress = { progress })) + } + rule.onNodeWithTag(layoutTag).assertSizeIsEqualTo(30.dp) + + // Middle of the transition. + progress = 0.5f + rule.onNodeWithTag(layoutTag).assertSizeIsEqualTo(60.dp) + + // Overscroll on B at 200%: size should not be interpolated given that there is an + // overscroll defined for scene B. + progress = 2f + rule.onNodeWithTag(layoutTag).assertSizeIsEqualTo(70.dp) + } + + @Test fun multipleTransitionsWillComposeMultipleScenes() { val duration = 10 * 16L diff --git a/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestMatchers.kt b/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestMatchers.kt index e743c7885c14..6d063a0418d6 100644 --- a/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestMatchers.kt +++ b/packages/SystemUI/compose/scene/tests/utils/src/com/android/compose/animation/scene/TestMatchers.kt @@ -17,7 +17,7 @@ package com.android.compose.animation.scene import androidx.compose.ui.test.SemanticsMatcher -import androidx.compose.ui.test.hasParent +import androidx.compose.ui.test.hasAnyAncestor import androidx.compose.ui.test.hasTestTag /** A [SemanticsMatcher] that matches [element], optionally restricted to scene [scene]. */ @@ -25,6 +25,6 @@ fun isElement(element: ElementKey, scene: SceneKey? = null): SemanticsMatcher { return if (scene == null) { hasTestTag(element.testTag) } else { - hasTestTag(element.testTag) and hasParent(hasTestTag(scene.testTag)) + hasTestTag(element.testTag) and hasAnyAncestor(hasTestTag(scene.testTag)) } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/statusbar/ui/AmbientStatusBarViewControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/statusbar/ui/AmbientStatusBarViewControllerTest.java index d84d151ae984..201ed00acae3 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/statusbar/ui/AmbientStatusBarViewControllerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/ambient/statusbar/ui/AmbientStatusBarViewControllerTest.java @@ -146,6 +146,7 @@ public class AmbientStatusBarViewControllerTest extends SysuiTestCase { mDreamOverlayStateController, mUserTracker, mKosmos.getWifiInteractor(), + mKosmos.getCommunalSceneInteractor(), mLogBuffer); } @@ -272,6 +273,7 @@ public class AmbientStatusBarViewControllerTest extends SysuiTestCase { mDreamOverlayStateController, mUserTracker, mKosmos.getWifiInteractor(), + mKosmos.getCommunalSceneInteractor(), mLogBuffer); controller.onViewAttached(); verify(mView, never()).showIcon( diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractorTest.kt index cfc6b3304399..a12b6f8a56c4 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAlternateBouncerTransitionInteractorTest.kt @@ -32,8 +32,11 @@ package com.android.systemui.keyguard.domain.interactor +import android.platform.test.annotations.DisableFlags +import android.platform.test.annotations.EnableFlags import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest +import com.android.systemui.Flags import com.android.systemui.SysuiTestCase import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepository import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository @@ -76,6 +79,7 @@ class FromAlternateBouncerTransitionInteractorTest : SysuiTestCase() { } @Test + @DisableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR) fun transitionToGone_keyguardOccluded_biometricAuthenticated() = testScope.runTest { transitionRepository.sendTransitionSteps( @@ -96,6 +100,25 @@ class FromAlternateBouncerTransitionInteractorTest : SysuiTestCase() { } @Test + @EnableFlags(Flags.FLAG_KEYGUARD_WM_STATE_REFACTOR) + fun transitionToGone_keyguardOccludedThenAltBouncer_authed_wmStateRefactor() = + testScope.runTest { + transitionRepository.sendTransitionSteps( + from = KeyguardState.OCCLUDED, + to = KeyguardState.ALTERNATE_BOUNCER, + testScope + ) + reset(transitionRepository) + + // Authentication results in calling startDismissKeyguardTransition. + kosmos.keyguardTransitionInteractor.startDismissKeyguardTransition() + runCurrent() + + assertThat(transitionRepository) + .startedTransition(from = KeyguardState.ALTERNATE_BOUNCER, to = KeyguardState.GONE) + } + + @Test fun noTransition_keyguardNotOccluded_biometricAuthenticated() = testScope.runTest { transitionRepository.sendTransitionSteps( diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorTest.kt index 6c5001ab9415..6eb9862fb4f1 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/FromAodTransitionInteractorTest.kt @@ -53,6 +53,7 @@ import com.android.systemui.power.domain.interactor.PowerInteractor import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAsleepForTest import com.android.systemui.power.domain.interactor.PowerInteractor.Companion.setAwakeForTest import com.android.systemui.power.domain.interactor.powerInteractor +import com.android.systemui.statusbar.domain.interactor.keyguardOcclusionInteractor import com.android.systemui.testKosmos import junit.framework.Assert.assertEquals import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -299,6 +300,7 @@ class FromAodTransitionInteractorTest : SysuiTestCase() { fun testTransitionToOccluded_onWake() = testScope.runTest { kosmos.fakeKeyguardRepository.setKeyguardOccluded(true) + kosmos.keyguardOcclusionInteractor.setWmNotifiedShowWhenLockedActivityOnTop(true) powerInteractor.setAwakeForTest() advanceTimeBy(100) // account for debouncing @@ -312,6 +314,7 @@ class FromAodTransitionInteractorTest : SysuiTestCase() { testScope.runTest { kosmos.fakeKeyguardRepository.setKeyguardShowing(false) kosmos.fakeKeyguardRepository.setKeyguardDismissible(true) + kosmos.keyguardTransitionInteractor.startDismissKeyguardTransition() powerInteractor.setAwakeForTest() advanceTimeBy(100) // account for debouncing diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt index addbdb664c77..7906a8244c5d 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt @@ -27,6 +27,7 @@ import com.android.systemui.SysuiTestCase import com.android.systemui.bouncer.data.repository.keyguardBouncerRepository import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.coroutines.collectValues import com.android.systemui.flags.EnableSceneContainer import com.android.systemui.keyguard.data.repository.fakeCommandQueue import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository @@ -191,6 +192,7 @@ class KeyguardInteractorTest : SysuiTestCase() { fun dismissAlpha() = testScope.runTest { val dismissAlpha by collectLastValue(underTest.dismissAlpha) + assertThat(dismissAlpha).isEqualTo(1f) keyguardTransitionRepository.sendTransitionSteps( from = KeyguardState.AOD, @@ -202,9 +204,9 @@ class KeyguardInteractorTest : SysuiTestCase() { // User begins to swipe up shadeRepository.setLegacyShadeExpansion(0.99f) - // When not dismissable, no alpha value (null) should emit + // When not dismissable, the last alpha value should still be present repository.setKeyguardDismissible(false) - assertThat(dismissAlpha).isNull() + assertThat(dismissAlpha).isEqualTo(1f) repository.setKeyguardDismissible(true) shadeRepository.setLegacyShadeExpansion(0.98f) @@ -212,9 +214,11 @@ class KeyguardInteractorTest : SysuiTestCase() { } @Test - fun dismissAlpha_whenShadeIsExpandedEmitsNull() = + fun dismissAlpha_whenShadeResetsEmitsOne() = testScope.runTest { - val dismissAlpha by collectLastValue(underTest.dismissAlpha) + val dismissAlpha by collectValues(underTest.dismissAlpha) + assertThat(dismissAlpha[0]).isEqualTo(1f) + assertThat(dismissAlpha.size).isEqualTo(1) keyguardTransitionRepository.sendTransitionSteps( from = KeyguardState.AOD, @@ -222,14 +226,50 @@ class KeyguardInteractorTest : SysuiTestCase() { testScope, ) - repository.setStatusBarState(StatusBarState.SHADE_LOCKED) - shadeRepository.setQsExpansion(1f) + // User begins to swipe up + repository.setStatusBarState(StatusBarState.KEYGUARD) + repository.setKeyguardDismissible(true) + shadeRepository.setLegacyShadeExpansion(0.98f) - repository.setKeyguardDismissible(false) - assertThat(dismissAlpha).isNull() + assertThat(dismissAlpha[1]).isGreaterThan(0.5f) + assertThat(dismissAlpha[1]).isLessThan(1f) + assertThat(dismissAlpha.size).isEqualTo(2) + + // Now reset the shade + shadeRepository.setLegacyShadeExpansion(1f) + assertThat(dismissAlpha[2]).isEqualTo(1f) + assertThat(dismissAlpha.size).isEqualTo(3) + } + + @Test + fun dismissAlpha_doesNotEmitWhileTransitioning() = + testScope.runTest { + val dismissAlpha by collectLastValue(underTest.dismissAlpha) + assertThat(dismissAlpha).isEqualTo(1f) + + keyguardTransitionRepository.sendTransitionSteps( + listOf( + TransitionStep( + from = KeyguardState.AOD, + to = KeyguardState.GONE, + value = 0f, + transitionState = TransitionState.STARTED, + ), + TransitionStep( + from = KeyguardState.AOD, + to = KeyguardState.GONE, + value = 0.1f, + transitionState = TransitionState.RUNNING, + ), + ), + testScope, + ) repository.setKeyguardDismissible(true) - assertThat(dismissAlpha).isNull() + shadeRepository.setLegacyShadeExpansion(0.98f) + + // Should still be one + assertThat(dismissAlpha).isEqualTo(1f) } @Test diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt index 9dc930babc10..6e16705b0739 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractorTest.kt @@ -197,7 +197,7 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() { @Test @EnableSceneContainer - fun surfaceBehindVisibility_fromLockscreenToGone_trueThroughout() = + fun surfaceBehindVisibility_fromLockscreenToGone_noUserInput_trueThroughout() = testScope.runTest { val isSurfaceBehindVisible by collectLastValue(underTest.value.surfaceBehindVisibility) val currentScene by collectLastValue(kosmos.sceneInteractor.currentScene) @@ -249,6 +249,43 @@ class WindowManagerLockscreenVisibilityInteractorTest : SysuiTestCase() { @Test @EnableSceneContainer + fun surfaceBehindVisibility_fromLockscreenToGone_withUserInput_falseUntilInputStops() = + testScope.runTest { + val isSurfaceBehindVisible by collectLastValue(underTest.value.surfaceBehindVisibility) + val currentScene by collectLastValue(kosmos.sceneInteractor.currentScene) + + // Before the transition, we start on Lockscreen so the surface should start invisible. + kosmos.setSceneTransition(ObservableTransitionState.Idle(Scenes.Lockscreen)) + assertThat(currentScene).isEqualTo(Scenes.Lockscreen) + assertThat(isSurfaceBehindVisible).isFalse() + + // Unlocked with fingerprint. + kosmos.deviceEntryFingerprintAuthRepository.setAuthenticationStatus( + SuccessFingerprintAuthenticationStatus(0, true) + ) + + // Start the transition to Gone, the surface should not be visible while + // isUserInputOngoing is true + val isUserInputOngoing = MutableStateFlow(true) + kosmos.setSceneTransition( + ObservableTransitionState.Transition( + fromScene = Scenes.Lockscreen, + toScene = Scenes.Gone, + isInitiatedByUserInput = true, + isUserInputOngoing = isUserInputOngoing, + progress = flowOf(0.51f), + currentScene = flowOf(Scenes.Gone), + ) + ) + assertThat(isSurfaceBehindVisible).isFalse() + + // When isUserInputOngoing becomes false, then the surface should become visible. + isUserInputOngoing.value = false + assertThat(isSurfaceBehindVisible).isTrue() + } + + @Test + @EnableSceneContainer fun surfaceBehindVisibility_fromBouncerToGone_becomesTrue() = testScope.runTest { val isSurfaceBehindVisible by collectLastValue(underTest.value.surfaceBehindVisibility) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/data/repository/MediaFilterRepositoryTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/data/repository/MediaFilterRepositoryTest.kt index 73e6506711f2..bc0512a1468d 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/data/repository/MediaFilterRepositoryTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/media/controls/data/repository/MediaFilterRepositoryTest.kt @@ -152,7 +152,6 @@ class MediaFilterRepositoryTest : SysuiTestCase() { underTest.setRecommendation(mediaRecommendation.copy(isActive = false)) assertThat(smartspaceMediaData).isNotEqualTo(mediaRecommendation) - assertThat(smartspaceMediaData?.isActive).isFalse() assertThat(underTest.isRecommendationActive()).isFalse() } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractorTest.kt index e87c8adc33a7..899122d4dd45 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractorTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractorTest.kt @@ -35,6 +35,7 @@ import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor import com.android.systemui.qs.tiles.base.interactor.QSTileInputTestKtx import com.android.systemui.screenrecord.RecordingController import com.android.systemui.screenrecord.data.model.ScreenRecordModel +import com.android.systemui.screenrecord.data.repository.ScreenRecordRepositoryImpl import com.android.systemui.statusbar.phone.KeyguardDismissUtil import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.argumentCaptor @@ -72,11 +73,18 @@ class ScreenRecordTileUserActionInteractorTest : SysuiTestCase() { .thenReturn(dialog) } + private val screenRecordRepository = + ScreenRecordRepositoryImpl( + bgCoroutineContext = testScope.testScheduler, + recordingController = recordingController, + ) + private val underTest = ScreenRecordTileUserActionInteractor( context, testScope.testScheduler, testScope.testScheduler, + screenRecordRepository, recordingController, keyguardInteractor, keyguardDismissUtil, diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt index ac66e6657a75..e40c8eecca0f 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/domain/startable/SceneContainerStartableTest.kt @@ -14,7 +14,7 @@ * limitations under the License. */ -@file:OptIn(ExperimentalCoroutinesApi::class, ExperimentalCoroutinesApi::class) +@file:OptIn(ExperimentalCoroutinesApi::class) package com.android.systemui.scene.domain.startable @@ -395,6 +395,7 @@ class SceneContainerStartableTest : SysuiTestCase() { ) assertThat(currentSceneKey).isEqualTo(Scenes.Gone) underTest.start() + runCurrent() kosmos.fakePowerRepository.updateWakefulness( rawState = WakefulnessState.STARTING_TO_SLEEP, @@ -1285,6 +1286,42 @@ class SceneContainerStartableTest : SysuiTestCase() { } @Test + fun switchToGone_whenSurfaceBehindLockscreenVisibleMidTransition() = + testScope.runTest { + val currentScene by collectLastValue(sceneInteractor.currentScene) + val transitionStateFlow = + prepareState( + authenticationMethod = AuthenticationMethodModel.None, + ) + underTest.start() + assertThat(currentScene).isEqualTo(Scenes.Lockscreen) + // Swipe to Gone, more than halfway + transitionStateFlow.value = + ObservableTransitionState.Transition( + fromScene = Scenes.Lockscreen, + toScene = Scenes.Gone, + currentScene = flowOf(Scenes.Gone), + progress = flowOf(0.51f), + isInitiatedByUserInput = true, + isUserInputOngoing = flowOf(true), + ) + runCurrent() + // Lift finger + transitionStateFlow.value = + ObservableTransitionState.Transition( + fromScene = Scenes.Lockscreen, + toScene = Scenes.Gone, + currentScene = flowOf(Scenes.Gone), + progress = flowOf(0.51f), + isInitiatedByUserInput = true, + isUserInputOngoing = flowOf(false), + ) + runCurrent() + + assertThat(currentScene).isEqualTo(Scenes.Gone) + } + + @Test fun switchToGone_extendUnlock() = testScope.runTest { val currentScene by collectLastValue(sceneInteractor.currentScene) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt index f89f18ac29f1..3ded8a346ce9 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelTest.kt @@ -6,15 +6,27 @@ import android.provider.Settings import android.telephony.SubscriptionManager.PROFILE_CLASS_UNSET import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest +import com.android.compose.animation.scene.ObservableTransitionState +import com.android.compose.animation.scene.SceneKey import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor +import com.android.systemui.flags.EnableSceneContainer +import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository +import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus import com.android.systemui.kosmos.testScope import com.android.systemui.plugins.activityStarter +import com.android.systemui.scene.domain.interactor.sceneInteractor +import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.fakeMobileIconsInteractor import com.android.systemui.testKosmos import com.android.systemui.util.mockito.argThat import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test @@ -24,12 +36,16 @@ import org.mockito.ArgumentMatchers.anyInt import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations +@OptIn(ExperimentalCoroutinesApi::class) @SmallTest @RunWith(AndroidJUnit4::class) +@EnableSceneContainer class ShadeHeaderViewModelTest : SysuiTestCase() { private val kosmos = testKosmos() private val testScope = kosmos.testScope private val mobileIconsInteractor = kosmos.fakeMobileIconsInteractor + private val sceneInteractor = kosmos.sceneInteractor + private val deviceEntryInteractor = kosmos.deviceEntryInteractor private val underTest: ShadeHeaderViewModel = kosmos.shadeHeaderViewModel @@ -77,6 +93,30 @@ class ShadeHeaderViewModelTest : SysuiTestCase() { ) } + @Test + fun onSystemIconContainerClicked_locked_collapsesShadeToLockscreen() = + testScope.runTest { + setDeviceEntered(false) + setScene(Scenes.Shade) + + underTest.onSystemIconContainerClicked() + runCurrent() + + assertThat(sceneInteractor.currentScene.value).isEqualTo(Scenes.Lockscreen) + } + + @Test + fun onSystemIconContainerClicked_unlocked_collapsesShadeToGone() = + testScope.runTest { + setDeviceEntered(true) + setScene(Scenes.Shade) + + underTest.onSystemIconContainerClicked() + runCurrent() + + assertThat(sceneInteractor.currentScene.value).isEqualTo(Scenes.Gone) + } + companion object { private val SUB_1 = SubscriptionModel( @@ -93,6 +133,32 @@ class ShadeHeaderViewModelTest : SysuiTestCase() { profileClass = PROFILE_CLASS_UNSET, ) } + + private fun setScene(key: SceneKey) { + sceneInteractor.changeScene(key, "test") + sceneInteractor.setTransitionState( + MutableStateFlow<ObservableTransitionState>(ObservableTransitionState.Idle(key)) + ) + testScope.runCurrent() + } + + private fun TestScope.setDeviceEntered(isEntered: Boolean) { + if (isEntered) { + // Unlock the device marking the device has entered. + kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus( + SuccessFingerprintAuthenticationStatus(0, true) + ) + runCurrent() + } + setScene( + if (isEntered) { + Scenes.Gone + } else { + Scenes.Lockscreen + } + ) + assertThat(deviceEntryInteractor.isDeviceEntered.value).isEqualTo(isEntered) + } } private class IntentMatcherAction(private val action: String) : ArgumentMatcher<Intent> { diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt index c35c165ba761..497484f90ca9 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelTest.kt @@ -42,6 +42,7 @@ import com.android.systemui.keyguard.domain.interactor.keyguardInteractor import com.android.systemui.keyguard.shared.model.BurnInModel import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.KeyguardState.AOD +import com.android.systemui.keyguard.shared.model.KeyguardState.GONE import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN import com.android.systemui.keyguard.shared.model.StatusBarState import com.android.systemui.keyguard.shared.model.TransitionState @@ -842,6 +843,30 @@ class SharedNotificationContainerViewModelTest(flags: FlagsParameterization) : S } @Test + @DisableSceneContainer + fun updateBounds_fromGone_withoutTransitions() = + testScope.runTest { + // Start step is already at 1.0 + val runningStep = TransitionStep(GONE, AOD, 1.0f, TransitionState.RUNNING) + val finishStep = TransitionStep(GONE, AOD, 1.0f, TransitionState.FINISHED) + + val bounds by collectLastValue(underTest.bounds) + val top = 123f + val bottom = 456f + + kosmos.fakeKeyguardTransitionRepository.sendTransitionStep(runningStep) + runCurrent() + kosmos.fakeKeyguardTransitionRepository.sendTransitionStep(finishStep) + runCurrent() + keyguardRootViewModel.onNotificationContainerBoundsChanged(top, bottom) + runCurrent() + + assertThat(bounds).isEqualTo( + NotificationContainerBounds(top = top, bottom = bottom) + ) + } + + @Test fun alphaOnFullQsExpansion() = testScope.runTest { val viewState = ViewStateAccessor() diff --git a/packages/SystemUI/res/drawable/android15_patch_adaptive.xml b/packages/SystemUI/res/drawable/android15_patch_adaptive.xml new file mode 100644 index 000000000000..d9492000beeb --- /dev/null +++ b/packages/SystemUI/res/drawable/android15_patch_adaptive.xml @@ -0,0 +1,21 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> + <background android:drawable="@drawable/android15_patch_adaptive_background"/> + <foreground android:drawable="@drawable/android15_patch_adaptive_foreground"/> + <monochrome android:drawable="@drawable/android15_patch_monochrome"/> +</adaptive-icon> diff --git a/packages/SystemUI/res/drawable/android15_patch_adaptive_background.xml b/packages/SystemUI/res/drawable/android15_patch_adaptive_background.xml new file mode 100644 index 000000000000..d4850d3d99c7 --- /dev/null +++ b/packages/SystemUI/res/drawable/android15_patch_adaptive_background.xml @@ -0,0 +1,75 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="108dp" + android:height="108dp" + android:viewportWidth="108" + android:viewportHeight="108"> + <!-- space (themed version) --> + <path + android:pathData="M0,0h108v108h-108z" + android:fillColor="@android:color/system_neutral1_800"/> + <!-- stars (themed version) --> + <group> + <path + android:pathData="M32,34 h1v1h-1z" + android:fillColor="@android:color/system_accent3_200"/> + <path + android:pathData="M33,61 h1v1h-1z" + android:fillColor="@android:color/system_accent3_200"/> + <path + android:pathData="M71,34 h1v1h-1z" + android:fillColor="@android:color/system_accent3_200"/> + <path + android:pathData="M62,56 h1v1h-1z" + android:fillColor="@android:color/system_accent3_200"/> + <path + android:pathData="M68,47 h1v1h-1z" + android:fillColor="@android:color/system_accent3_200"/> + <path + android:pathData="M72,55 h1v1h-1z" + android:fillColor="@android:color/system_accent3_200"/> + <path + android:pathData="M39,36 h1v1h-1z" + android:fillColor="@android:color/system_accent3_200"/> + + <path + android:pathData="M72,49 h2v2h-2z" + android:fillColor="@android:color/system_accent3_200"/> + <path + android:pathData="M46,53 h2v2h-2z" + android:fillColor="@android:color/system_accent3_200"/> + <path + android:pathData="M32,45 h2v2h-2z" + android:fillColor="@android:color/system_accent3_200"/> + <path + android:pathData="M43,37 h2v2h-2z" + android:fillColor="@android:color/system_accent3_200"/> + <path + android:pathData="M78,51 h2v2h-2z" + android:fillColor="@android:color/system_accent3_200"/> + <path + android:pathData="M34,51 h2v2h-2z" + android:fillColor="@android:color/system_accent3_200"/> + <path + android:pathData="M76,41 h2v2h-2z" + android:fillColor="@android:color/system_accent3_200"/> + <path + android:pathData="M39,46 h2v2h-2z" + android:fillColor="@android:color/system_accent3_200"/> + </group> +</vector> diff --git a/packages/SystemUI/res/drawable/android15_patch_adaptive_foreground.xml b/packages/SystemUI/res/drawable/android15_patch_adaptive_foreground.xml new file mode 100644 index 000000000000..34f6ee07a50b --- /dev/null +++ b/packages/SystemUI/res/drawable/android15_patch_adaptive_foreground.xml @@ -0,0 +1,60 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="108dp" + android:height="108dp" + android:viewportWidth="108" + android:viewportHeight="108"> + + <!-- zoomies (themed version) --> + <group> + <path + android:pathData="M53,42C52.21,50.58 46.46,68.95 32.11,74.63C19.22,79.75 5.77,82.32 1.19,83.19C0.68,83.29 0.28,83.36 0,83.42V108H54H108V83.42C107.72,83.36 107.32,83.29 106.81,83.19C102.23,82.32 88.78,79.75 75.89,74.63C61.54,68.95 55.79,50.58 55,42H54H53Z" + android:fillColor="@android:color/system_accent1_100" + android:fillType="evenOdd"/> + <path + android:pathData="M53.25,42C52.88,50.53 50.44,69.01 43.68,74.67C36.91,80.33 32.65,82.86 31.37,83.41L54,102.87L76.63,83.41C75.35,82.86 71.09,80.33 64.32,74.67C57.56,69.01 55.12,50.53 54.75,42H54H53.25Z" + android:fillColor="#ffffff" + android:fillType="evenOdd"/> + <path + android:pathData="M54,42m-1,0a1,1 0,1 1,2 0a1,1 0,1 1,-2 0" + android:fillColor="#ffffff"/> + </group> + <group> + <!-- head! it's like sputnik --> + <path + android:pathData="M54,94.25m-26.25,0a26.25,26.25 0,1 1,52.5 0a26.25,26.25 0,1 1,-52.5 0" + android:fillColor="#34A853"/> + <!-- ant --> + <path + android:pathData="M38,63.5L44.5,74.5" + android:strokeWidth="5" + android:fillColor="#00000000" + android:strokeColor="#34A853" + android:strokeLineCap="round"/> + <path + android:pathData="M70,63.5L63.5,74.5" + android:strokeWidth="5" + android:fillColor="#00000000" + android:strokeColor="#34A853" + android:strokeLineCap="round"/> + </group> + <!-- spaceship --> + <path + android:pathData="M54,34C52.34,34 51,35.29 51,36.88V40.44C51,40.75 51.25,41 51.56,41C51.87,41 52.13,40.75 52.13,40.44V39.48C52.13,38.87 52.63,38.37 53.25,38.37H54.75C55.37,38.37 55.87,38.87 55.87,39.48V40.44C55.87,40.75 56.13,41 56.44,41C56.75,41 57,40.75 57,40.44V36.88C57,35.29 55.66,34 54,34H54Z" + android:fillColor="#E9F3EB"/> +</vector> diff --git a/packages/SystemUI/res/drawable/android15_patch_monochrome.xml b/packages/SystemUI/res/drawable/android15_patch_monochrome.xml new file mode 100644 index 000000000000..a91cc864e7cd --- /dev/null +++ b/packages/SystemUI/res/drawable/android15_patch_monochrome.xml @@ -0,0 +1,119 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +Copyright (C) 2024 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<vector xmlns:android="http://schemas.android.com/apk/res/android" + android:width="108dp" + android:height="108dp" + android:viewportWidth="108" + android:viewportHeight="108"> + <group> + <path + android:pathData=" + M54,94.25 + m-26.25,0 + a26.25,26.25 0,1 1,52.5 0 + a26.25,26.25 0,1 1,-52.5 0 + " + android:fillColor="#000000"/> + <path + android:pathData="M38,63.5L44.5,74.5" + android:strokeWidth="5" + android:fillColor="#00000000" + android:strokeColor="#000000" + android:strokeLineCap="round"/> + <path + android:pathData="M70,63.5L63.5,74.5" + android:strokeWidth="5" + android:fillColor="#00000000" + android:strokeColor="#000000" + android:strokeLineCap="round"/> + + <path + android:pathData=" + M54,34 + C52.34,34 51,35.29 51,36.88 + V40.44 + C51,40.75 51.25,41 51.56,41 + C51.87,41 52.13,40.75 52.13,40.44 + V39.48 + C52.13,38.87 52.63,38.37 53.25,38.37 + H54.75 + C55.37,38.37 55.87,38.87 55.87,39.48 + V40.44 + C55.87,40.75 56.13,41 56.44,41 + C56.75,41 57,40.75 57,40.44 + V36.88 + C57,35.29 55.66,34 54,34 + H54 + Z + " + android:fillColor="#34A853"/> + <path + android:strokeWidth="1" + android:pathData="M54,40V67" + android:fillColor="#00000000" + android:strokeColor="#40FFFFFF" + /> + + <path + android:pathData="M32,34 h1v1h-1z" + android:fillColor="#ffffff"/> + <path + android:pathData="M33,61 h1v1h-1z" + android:fillColor="#ffffff"/> + <path + android:pathData="M71,34 h1v1h-1z" + android:fillColor="#ffffff"/> + <path + android:pathData="M62,56 h1v1h-1z" + android:fillColor="#ffffff"/> + <path + android:pathData="M68,47 h1v1h-1z" + android:fillColor="#ffffff"/> + <path + android:pathData="M72,55 h1v1h-1z" + android:fillColor="#ffffff"/> + <path + android:pathData="M39,36 h1v1h-1z" + android:fillColor="#ffffff"/> + + <path + android:pathData="M72,49 h2v2h-2z" + android:fillColor="#ffffff"/> + <path + android:pathData="M46,53 h2v2h-2z" + android:fillColor="#ffffff"/> + <path + android:pathData="M32,45 h2v2h-2z" + android:fillColor="#ffffff"/> + <path + android:pathData="M43,37 h2v2h-2z" + android:fillColor="#ffffff"/> + <path + android:pathData="M78,51 h2v2h-2z" + android:fillColor="#ffffff"/> + <path + android:pathData="M34,51 h2v2h-2z" + android:fillColor="#ffffff"/> + <path + android:pathData="M76,41 h2v2h-2z" + android:fillColor="#ffffff"/> + <path + android:pathData="M39,46 h2v2h-2z" + android:fillColor="#ffffff"/> + + </group> +</vector> diff --git a/packages/SystemUI/res/layout/super_notification_shade.xml b/packages/SystemUI/res/layout/super_notification_shade.xml index 221b791b8cb5..fbb07bed4b50 100644 --- a/packages/SystemUI/res/layout/super_notification_shade.xml +++ b/packages/SystemUI/res/layout/super_notification_shade.xml @@ -62,7 +62,9 @@ <com.android.systemui.keyguard.ui.view.KeyguardRootView android:id="@id/keyguard_root_view" android:layout_width="match_parent" - android:layout_height="match_parent" /> + android:layout_height="match_parent" + android:clipChildren="false" + /> <!-- Shared container for the notification stack. Can be positioned by either the keyguard_root_view or notification_panel --> diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml index c25d8cb4e3f8..a7dfdaa658d1 100644 --- a/packages/SystemUI/res/values-af/strings.xml +++ b/packages/SystemUI/res/values-af/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Skakel dit môre outomaties weer aan"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Kenmerke soos Kitsdeel en Kry My Toestel gebruik Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth sal môreoggend aanskakel"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Oudiodeling"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Deel tans oudio"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> batterykrag"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Oudio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Kopstuk"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Watter deel van jou toestelervaring is geraak?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Kies soort kwessie"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Skermopname"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"Werkverrigting"</string> + <string name="user_interface" msgid="3712869377953950887">"Gebruikerkoppelvlak"</string> + <string name="thermal" msgid="6758074791325414831">"Termies"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Eenhandmodus"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Gehoortoestelle"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktief"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Ontkoppel"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Gehoortoestelle"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Bind nuwe toestel saam"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Klik om nuwe toestel saam te bind"</string> @@ -1201,7 +1198,7 @@ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-fi sal vir nou nie outomaties koppel nie"</string> <string name="see_all_networks" msgid="3773666844913168122">"Sien alles"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Ontkoppel Ethernet om netwerke te wissel"</string> - <string name="wifi_scan_notify_message" msgid="3753839537448621794">"Om toestelervaring te verbeter, kan programme en dienste steeds enige tyd na wi‑fi-netwerke soek, selfs wanneer wi‑fi af is. Jy kan dit in Wi-fi-opsporinginstellings verander. "<annotation id="link">"Verander"</annotation></string> + <string name="wifi_scan_notify_message" msgid="3753839537448621794">"Om toestelervaring te verbeter, kan apps en dienste steeds enige tyd na wi‑fi-netwerke soek, selfs wanneer wi‑fi af is. Jy kan dit in Wi-fi-opsporinginstellings verander. "<annotation id="link">"Verander"</annotation></string> <string name="turn_off_airplane_mode" msgid="8425587763226548579">"Skakel vliegtuigmodus af"</string> <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> wil die volgende teël by Kitsinstellings voeg"</string> <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Voeg teël by"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"gevou"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"oopgevou"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Stilusbattery <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Koppel jou stilus aan ’n laaier"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Stilus se battery is amper pap"</string> <string name="video_camera" msgid="7654002575156149298">"Videokamera"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Soekkortpaaie"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Vou ikoon in"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Vou ikoon uit"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"of"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Sleutelbordlig"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Vlak %1$d van %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Huiskontroles"</string> diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml index ed6a3e16355e..396837e405ee 100644 --- a/packages/SystemUI/res/values-am/strings.xml +++ b/packages/SystemUI/res/values-am/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"ነገ እንደገና በራስ-ሰር አስጀምር"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"እንደ ፈጣን ማጋራት እና የእኔን መሣሪያ አግኝ ያሉ ባህሪዎች ብሉቱዝን ይጠቀማሉ"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"ብሉቱዝ ነገ ጠዋት ይበራል"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"የድምጽ ማጋራት"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"ድምጽን በማጋራት ላይ"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ባትሪ"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ኦዲዮ"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ማዳመጫ"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"የትኛው የመሣሪያዎ ተሞክሮ ክፍል ተጎድቷል?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"የችግሩን አይነት ይምረጡ"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"የማያ መቅረጫ"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"አፈጻጸም"</string> + <string name="user_interface" msgid="3712869377953950887">"የተጠቃሚ በይነገፅ"</string> + <string name="thermal" msgid="6758074791325414831">"ተርማል"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"የአንድ እጅ ሁነታ"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"የመስሚያ መሣሪያዎች"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"ገቢር"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"ግንኙነት ተቋርጧል"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"የመስማት ችሎታ መሣሪያ"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"አዲስ መሣሪያ ያጣምሩ"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"አዲስ መሣሪያ ለማጣመር ጠቅ ያድርጉ"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"የታጠፈ"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"የተዘረጋ"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"የብሮስፌ ባትሪ <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"ብሮስፌዎን ከኃይል መሙያ ጋር ያገናኙ"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"የብሮስፌ ባትሪ ዝቅተኛ ነው"</string> <string name="video_camera" msgid="7654002575156149298">"የቪድዮ ካሜራ"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"የፍለጋ አቋራጮች"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"መሰብሰቢያ አዶ"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"መዘርጊያ አዶ"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ወይም"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"የቁልፍ ሰሌዳ የጀርባ ብርሃን"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"ደረጃ %1$d ከ %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"የቤት ውስጥ ቁጥጥሮች"</string> diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml index 1502765e8fbf..254549fdc1ba 100644 --- a/packages/SystemUI/res/values-ar/strings.xml +++ b/packages/SystemUI/res/values-ar/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"تفعيل البلوتوث تلقائيًا مرة أخرى غدًا"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"يُستخدَم البلوتوث في ميزات مثل Quick Share و\"العثور على جهازي\""</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"سيتم تفعيل البلوتوث صباح الغد"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"مشاركة الصوت"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"مشاركة الصوت"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"مستوى طاقة البطارية <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"صوت"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"سماعة الرأس"</string> @@ -301,11 +303,11 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"المستخدم"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> <string name="quick_settings_internet_label" msgid="6603068555872455463">"الإنترنت"</string> - <string name="quick_settings_networks_available" msgid="1875138606855420438">"الشبكات متوفرة"</string> + <string name="quick_settings_networks_available" msgid="1875138606855420438">"تتوفّر شبكات"</string> <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"الشبكات غير متوفرة"</string> <string name="quick_settings_wifi_detail_empty_text" msgid="483130889414601732">"لا تتوفر أي شبكة Wi-Fi"</string> <string name="quick_settings_wifi_secondary_label_transient" msgid="7501659015509357887">"جارٍ التفعيل…"</string> - <string name="quick_settings_cast_title" msgid="3033553249449938182">"الإرسال"</string> + <string name="quick_settings_cast_title" msgid="3033553249449938182">"البث"</string> <string name="quick_settings_casting" msgid="1435880708719268055">"جارٍ الإرسال"</string> <string name="quick_settings_cast_device_default_name" msgid="6988469571141331700">"جهاز لا يحمل اسمًا"</string> <string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"لا يتوفر أي جهاز"</string> @@ -366,10 +368,8 @@ <string name="thermal" msgid="6758074791325414831">"الأداء الحراري"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"وضع \"التصفح بيد واحدة\""</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"سماعات الأذن الطبية"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"متّصلة حاليًا"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"غير متّصلة"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"سماعات الأذن الطبية"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"إقران جهاز جديد"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"انقر لإقران جهاز جديد"</string> @@ -1324,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"اختصارات طلبات البحث"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"رمز التصغير"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"رمز التوسيع"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"أو"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"الإضاءة الخلفية للوحة المفاتيح"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"مستوى الإضاءة: %1$d من %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"إدارة المنزل آليًّا"</string> diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml index 00d673ea7231..dd7102382983 100644 --- a/packages/SystemUI/res/values-as/strings.xml +++ b/packages/SystemUI/res/values-as/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"কাইলৈ পুনৰ স্বয়ংক্ৰিয়ভাৱে অন কৰক"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Quick Share আৰু Find My Deviceৰ দৰে সুবিধাসমূহে ব্লুটুথ ব্যৱহাৰ কৰে"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"কাইলৈ পুৱা ব্লুটুথ অন হ’ব"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"অডিঅ’ শ্বেয়াৰ কৰা"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"অডিঅ’ শ্বেয়াৰ কৰি থকা হৈছে"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"বেটাৰী <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"অডিঅ’"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"হেডছেট"</string> @@ -366,10 +368,8 @@ <string name="thermal" msgid="6758074791325414831">"থাৰ্মেল"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"এখন হাতেৰে ব্যৱহাৰ কৰা ম’ড"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"শুনাৰ ডিভাইচ"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"সক্ৰিয় হৈ আছে"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"সংযোগ বিচ্ছিন্ন কৰা হ’ল"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"শুনাৰ ডিভাইচ"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"নতুন ডিভাইচ পেয়াৰ কৰক"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"নতুন ডিভাইচ পেয়াৰ কৰিবলৈ ক্লিক কৰক"</string> @@ -1265,8 +1265,8 @@ <string name="rear_display_bottom_sheet_confirm" msgid="1507591562761552899">"এতিয়াই স্ক্ৰীন সলনি কৰক"</string> <string name="rear_display_folded_bottom_sheet_title" msgid="3930008746560711990">"ফ’নটো আনফ’ল্ড কৰক"</string> <string name="rear_display_unfolded_bottom_sheet_title" msgid="6291111173057304055">"স্ক্ৰীন সলনি কৰিবনে?"</string> - <string name="rear_display_folded_bottom_sheet_description" msgid="6842767125783222695">"অধিক ৰিজ’লিউছনৰ বাবে, পিছফালৰ কেমেৰাটো ব্যৱহাৰ কৰক"</string> - <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"অধিক ৰিজ’লিউছনৰ বাবে, ফ’নটো লুটিয়াই দিয়ক"</string> + <string name="rear_display_folded_bottom_sheet_description" msgid="6842767125783222695">"অধিক ৰিজ’লিউশ্বনৰ বাবে, পিছফালৰ কেমেৰাটো ব্যৱহাৰ কৰক"</string> + <string name="rear_display_unfolded_bottom_sheet_description" msgid="7229961336309960201">"অধিক ৰিজ’লিউশ্বনৰ বাবে, ফ’নটো লুটিয়াই দিয়ক"</string> <string name="rear_display_accessibility_folded_animation" msgid="1538121649587978179">"জপাব পৰা ডিভাইচৰ জাপ খুলি থকা হৈছে"</string> <string name="rear_display_accessibility_unfolded_animation" msgid="1946153682258289040">"জপাব পৰা ডিভাইচৰ ওলোটাই থকা হৈছে"</string> <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"ফ’ল্ড কৰা"</string> @@ -1324,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"সন্ধানৰ শ্বৰ্টকাট"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"সংকোচন কৰাৰ চিহ্ন"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"বিস্তাৰ কৰাৰ চিহ্ন"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"অথবা"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"কীব’ৰ্ডৰ বেকলাইট"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$dৰ %1$d স্তৰ"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"ঘৰৰ সা-সৰঞ্জামৰ নিয়ন্ত্ৰণ"</string> diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml index b3c0058d8368..592bc06de2fd 100644 --- a/packages/SystemUI/res/values-az/strings.xml +++ b/packages/SystemUI/res/values-az/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Sabah avtomatik aktiv edin"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Cəld Paylaşım və Cihazın Tapılması kimi funksiyalar Bluetooth istifadə edir"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth sabah səhər aktiv ediləcək"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Audio paylaşma"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Audio paylaşılır"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> batareya"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Qulaqlıq"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Cihaz istifadəsinə necə təsir etdi?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Problem növü seçin"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Ekran qeydəalma"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"Performans"</string> + <string name="user_interface" msgid="3712869377953950887">"İstifadəçi interfeysi"</string> + <string name="thermal" msgid="6758074791325414831">"Termal"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Birəlli rejim"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Eşitmə cihazları"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktiv"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Bağlantı kəsildi"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Eşitmə cihazları"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Yeni cihaz birləşdirin"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Yeni cihaz birləşdirmək üçün klikləyin"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"qatlanmış"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"açıq"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Stilusun enerjisi: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Qələmi adapterə qoşun"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Qələm enerjisi azdır"</string> <string name="video_camera" msgid="7654002575156149298">"Videokamera"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Axtarış qısayolları"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"İkonanı yığcamlaşdırın"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"İkonanı genişləndirin"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"və ya"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Klaviatura işığı"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Səviyyə %1$d/%2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Ev nizamlayıcıları"</string> diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml index 22afb40404dd..9b27943f5484 100644 --- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml +++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatski ponovo uključi sutra"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Funkcije kao što su Quick Share i Pronađi moj uređaj koriste Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth će se uključiti sutra ujutru"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Deljenje zvuka"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Deli se zvuk"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Nivo baterije je <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Slušalice"</string> @@ -366,10 +368,8 @@ <string name="thermal" msgid="6758074791325414831">"Termalna kamera"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Režim jednom rukom"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Slušni aparati"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktivno"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Veza je prekinuta"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Slušni aparati"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Upari novi uređaj"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kliknite da biste uparili nov uređaj"</string> @@ -1324,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Prečice pretrage"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona za skupljanje"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona za proširivanje"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ili"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Pozadinsko osvetljenje tastature"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"%1$d. nivo od %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Kontrole za dom"</string> diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml index 4fe7c5a0d09f..ca763cfac724 100644 --- a/packages/SystemUI/res/values-be/strings.xml +++ b/packages/SystemUI/res/values-be/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Аўтаматычнае ўключэнне заўтра"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Bluetooth выкарыстоўваецца такімі функцыямі і сэрвісамі, як Хуткае абагульванне і Знайсці прыладу"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth уключыцца заўтра раніцай"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Абагульванне аўдыя"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Ідзе абагульванне аўдыя"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Узровень зараду: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Гук"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Гарнітура"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"З чым была звязана праблема, якая вам сустрэлася?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Выберыце тып праблемы"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Запіс экрана"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"Прадукцыйнасць"</string> + <string name="user_interface" msgid="3712869377953950887">"Карыстальніцкі інтэрфейс"</string> + <string name="thermal" msgid="6758074791325414831">"Тэрмальныя паказчыкі"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Рэжым кіравання адной рукой"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Слыхавыя апараты"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Актыўныя"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Адключаны"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Слыхавыя апараты"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Спалучыць новую прыладу"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Націсніце, каб спалучыць новую прыладу"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"складзена"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"раскладзена"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Зарад акумулятара пяра – <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Падключыце пяро да зараднай прылады"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Нізкі ўзровень зараду пяра"</string> <string name="video_camera" msgid="7654002575156149298">"Відэакамера"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Пошук спалучэнняў клавіш"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Значок \"Згарнуць\""</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Значок \"Разгарнуць\""</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"або"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Падсветка клавіятуры"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Узровень %1$d з %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Кіраванне домам"</string> diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml index 2cd65db11d54..d50b11605743 100644 --- a/packages/SystemUI/res/values-bg/strings.xml +++ b/packages/SystemUI/res/values-bg/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Автоматично включване отново утре"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Функции като „Бързо споделяне“ и „Намиране на устройството ми“ използват Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth ще се включи утре сутрин"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Споделяне на звука"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Звукът се споделя"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Батерия: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудио"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Слушалки"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"С какво имахте проблем?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Изберете тип проблем"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Запис на екрана"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"Ефективност"</string> + <string name="user_interface" msgid="3712869377953950887">"Потребителски интерфейс"</string> + <string name="thermal" msgid="6758074791325414831">"Термално"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Режим за работа с една ръка"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Слухови апарати"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Активно"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Няма връзка"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Слухови апарати"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Сдвояване на ново устройство"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Кликнете за сдвояване на ново устройство"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"затворено"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"отворено"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Батерия на писалката: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Свържете писалката към зарядно устройство"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Батерията на писалката е изтощена"</string> <string name="video_camera" msgid="7654002575156149298">"Видеокамера"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Търсете клавишни комбинации"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Икона за свиване"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Икона за разгъване"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"или"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Подсветка на клавиатурата"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Ниво %1$d от %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Контроли за дома"</string> diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml index f8e350a3d6ad..bb41b1b14b29 100644 --- a/packages/SystemUI/res/values-bn/strings.xml +++ b/packages/SystemUI/res/values-bn/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"আগামীকাল আবার অটোমেটিক চালু হবে"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"দ্রুত শেয়ার ও Find My Device-এর মতো ফিচার ব্লুটুথ ব্যবহার করে"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"ব্লুটুথ আগামীকাল সকালে চালু হয়ে যাবে"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"অডিও শেয়ারিং"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"অডিও শেয়ার করা হচ্ছে"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"চার্জ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"অডিও"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"হেডসেট"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"ডিভাইস ব্যবহার করার সময় কোথায় অসুবিধা হয়েছিল?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"সমস্যার প্রকার বেছে নিন"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"স্ক্রিন রেকর্ড"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"পারফর্ম্যান্স"</string> + <string name="user_interface" msgid="3712869377953950887">"ইউজার ইন্টারফেস"</string> + <string name="thermal" msgid="6758074791325414831">"থার্মাল"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"এক হাতে ব্যবহার করার মোড"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"হিয়ারিং ডিভাইস"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"অ্যাক্টিভ"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"ডিসকানেক্ট হয়ে গেছে"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"হিয়ারিং ডিভাইস"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"নতুন ডিভাইস পেয়ার করুন"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"নতুন ডিভাইস পেয়ার করতে ক্লিক করুন"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"ফোল্ড করা রয়েছে"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"ফোল্ড করা নেই"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"স্টাইলাস ব্যাটারি <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"কোনও চার্জারের সাথে আপনার স্টাইলাস কানেক্ট করুন"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"স্টাইলাস ব্যাটারিতে চার্জ কম আছে"</string> <string name="video_camera" msgid="7654002575156149298">"ভিডিও ক্যামেরা"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"সার্চ শর্টকাট"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"আইকন আড়াল করুন"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"আইকন বড় করুন"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"অথবা"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"কীবোর্ড ব্যাকলাইট"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d-এর মধ্যে %1$d লেভেল"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"হোম কন্ট্রোল"</string> diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml index 60c936e04d18..4916d80f5245 100644 --- a/packages/SystemUI/res/values-bs/strings.xml +++ b/packages/SystemUI/res/values-bs/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatski uključi ponovo sutra"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Funkcije kao što su Quick Share i Pronađi moj uređaj koriste Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth će se uključiti sutra ujutro"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Dijeljenje zvuka"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Dijeljenje zvuka"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> baterije"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Zvuk"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Slušalice"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Koji dio uređaja je imao problem?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Odaberite vrstu problema"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Snimanje ekrana"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"Performanse"</string> + <string name="user_interface" msgid="3712869377953950887">"Korisnički interfejs"</string> + <string name="thermal" msgid="6758074791325414831">"Termalno"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Način rada jednom rukom"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Slušni aparati"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktivno"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Veza je prekinuta"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Slušni aparati"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Uparite novi uređaj"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kliknite da uparite novi uređaj"</string> @@ -1201,7 +1198,7 @@ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"WiFi se trenutno ne može automatski povezati"</string> <string name="see_all_networks" msgid="3773666844913168122">"Prikaži sve"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"Da promijenite mrežu, isključite ethernet"</string> - <string name="wifi_scan_notify_message" msgid="3753839537448621794">"Radi poboljšanja iskustva s uređajem aplikacije i usluge i dalje mogu tražiti WiFi mreže bilo kada, čak i kada je WiFi isključen. Ovo možete promijeniti u Postavkama traženja WiFi mreže. "<annotation id="link">"Promijeni"</annotation></string> + <string name="wifi_scan_notify_message" msgid="3753839537448621794">"Radi poboljšanja iskustva s uređajem aplikacije i usluge i dalje mogu tražiti WiFi mreže bilo kada, čak i kada je WiFi isključen. Ovo možete promijeniti u postavkama traženja WiFi-ja. "<annotation id="link">"Promijeni"</annotation></string> <string name="turn_off_airplane_mode" msgid="8425587763226548579">"Isključi način rada u avionu"</string> <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> želi dodati sljedeću karticu u Brze postavke"</string> <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"Dodaj karticu"</string> @@ -1210,7 +1207,7 @@ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# aplikacija je aktivna}one{# aplikacija je aktivna}few{# aplikacije su aktivne}other{# aplikacija je aktivno}}"</string> <string name="fgs_dot_content_description" msgid="2865071539464777240">"Nove informacije"</string> <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Aktivne aplikacije"</string> - <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Ove aplikacije su aktivne i pokrenute, čak i kada ih ne koristite. Ovim se poboljšava njihova funkcionalnost, ali može uticati i na vijek trajanja baterije."</string> + <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Ove aplikacije su aktivne i pokrenute, čak i kada ih ne koristite. Ovim se poboljšava njihova funkcionalnost, ali to može uticati i na vijek trajanja baterije."</string> <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Zaustavi"</string> <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Zaustavljeno"</string> <string name="clipboard_edit_text_done" msgid="4551887727694022409">"Gotovo"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"sklopljeno"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"otklopljeno"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Baterija pisaljke <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Priključite pisaljku na punjač"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Baterija pisaljke je slaba"</string> <string name="video_camera" msgid="7654002575156149298">"Video kamera"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Prečica pretraživanja"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona sužavanja"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona proširivanja"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ili"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Pozadinsko osvjetljenje tastature"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"%1$d. nivo od %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Kontrole za dom"</string> diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml index a82d6f10a7d8..5afad1b93805 100644 --- a/packages/SystemUI/res/values-ca/strings.xml +++ b/packages/SystemUI/res/values-ca/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Torna\'l a activar automàticament demà"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Les funcions com Quick Share i Troba el meu dispositiu utilitzen el Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"El Bluetooth s\'activarà demà al matí"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Compartició d\'àudio"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"S\'està compartint l\'àudio"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> de bateria"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Àudio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Auriculars"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"L\'experiència amb el dispositiu s\'ha vist afectada?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Selecciona el tipus de problema"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Gravació de pantalla"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"Rendiment"</string> + <string name="user_interface" msgid="3712869377953950887">"Interfície d\'usuari"</string> + <string name="thermal" msgid="6758074791325414831">"Tèrmic"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Mode d\'una mà"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Audiòfons"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Actiu"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Desconnectat"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Audiòfons"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Vincula un dispositiu nou"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Fes clic per vincular un dispositiu nou"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"plegat"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"desplegat"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Bateria del llapis òptic: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Connecta el llapis òptic a un carregador"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Bateria del llapis òptic baixa"</string> <string name="video_camera" msgid="7654002575156149298">"Càmera de vídeo"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Dreceres de cerca"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Replega la icona"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Desplega la icona"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"o"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Retroil·luminació del teclat"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Nivell %1$d de %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Controls de la llar"</string> diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml index 9b540ff06f63..b5293c0057a9 100644 --- a/packages/SystemUI/res/values-cs/strings.xml +++ b/packages/SystemUI/res/values-cs/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Zítra znovu automaticky zapnout"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Bluetooth využívají funkce jako Quick Share a Najdi moje zařízení."</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth se zapne zítra ráno."</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Sdílení zvuku"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Sdílení zvuku"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Baterie: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Zvuk"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Sluchátka"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Co v zařízení bylo ovlivněno?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Vyberte druh problém"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Záznam obrazovky"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"Výkon"</string> + <string name="user_interface" msgid="3712869377953950887">"Uživatelské rozhraní"</string> + <string name="thermal" msgid="6758074791325414831">"Termovize"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Režim jedné ruky"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Naslouchátka"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktivní"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Odpojeno"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Naslouchátka"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Spárovat nové zařízení"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kliknutím spárujete nové zařízení"</string> @@ -923,7 +920,7 @@ <string name="mobile_data" msgid="4564407557775397216">"Mobilní data"</string> <string name="mobile_data_text_format" msgid="6806501540022589786">"<xliff:g id="ID_1">%1$s</xliff:g> – <xliff:g id="ID_2">%2$s</xliff:g>"</string> <string name="mobile_carrier_text_format" msgid="8912204177152950766">"<xliff:g id="CARRIER_NAME">%1$s</xliff:g>, <xliff:g id="MOBILE_DATA_TYPE">%2$s</xliff:g>"</string> - <string name="wifi_is_off" msgid="5389597396308001471">"Wi-Fi je vypnuta"</string> + <string name="wifi_is_off" msgid="5389597396308001471">"Wi-Fi je vypnutá"</string> <string name="bt_is_off" msgid="7436344904889461591">"Bluetooth je vypnuto"</string> <string name="dnd_is_off" msgid="3185706903793094463">"Režim Nerušit je vypnut"</string> <string name="dnd_is_on" msgid="7009368176361546279">"Režim Nerušit je zapnutý"</string> @@ -933,7 +930,7 @@ <string name="running_foreground_services_title" msgid="5137313173431186685">"Aplikace běžící na pozadí"</string> <string name="running_foreground_services_msg" msgid="3009459259222695385">"Klepnutím zobrazíte podrobnosti o využití baterie a dat"</string> <string name="mobile_data_disable_title" msgid="5366476131671617790">"Vypnout mobilní data?"</string> - <string name="mobile_data_disable_message" msgid="8604966027899770415">"Prostřednictvím <xliff:g id="CARRIER">%s</xliff:g> nebudete moci používat data ani internet. Internet bude dostupný pouze přes Wi-Fi."</string> + <string name="mobile_data_disable_message" msgid="8604966027899770415">"Prostřednictvím operátora <xliff:g id="CARRIER">%s</xliff:g> nebudete moct používat data ani internet. Internet bude dostupný pouze přes Wi-Fi."</string> <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"vašeho operátora"</string> <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"Přepnout zpět na operátora <xliff:g id="CARRIER">%s</xliff:g>?"</string> <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"Mobilní data se nebudou automaticky přepínat podle dostupnosti"</string> @@ -1211,7 +1208,7 @@ <string name="fgs_dot_content_description" msgid="2865071539464777240">"Nové informace"</string> <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Aktivní aplikace"</string> <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Tyto aplikace jsou spuštěné a aktivní, i když je nepoužíváte. Zlepšuje to jejich funkčnost, ale může to mít dopad na výdrž baterie."</string> - <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Konec"</string> + <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Zastavit"</string> <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Zastaveno"</string> <string name="clipboard_edit_text_done" msgid="4551887727694022409">"Hotovo"</string> <string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"Zkopírováno"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"složené"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"rozložené"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Baterie dotykového pera <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Připojte dotykové pero k nabíječce"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Slabá baterie dotykového pera"</string> <string name="video_camera" msgid="7654002575156149298">"Videokamera"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Vyhledat zkratky"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona sbalení"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona rozbalení"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"nebo"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Podsvícení klávesnice"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Úroveň %1$d z %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Ovládání domácnosti"</string> diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml index 57353f7f785d..6e2323be3921 100644 --- a/packages/SystemUI/res/values-da/strings.xml +++ b/packages/SystemUI/res/values-da/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Aktivér automatisk igen i morgen"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Funktioner som f.eks. Quick Share og Find min enhed anvender Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth aktiveres i morgen tidlig"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Lyddeling"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Deler lyd"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> batteri"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Lyd"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Hvilken del af din enhedsoplevelse blev påvirket?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Vælg problemtype"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Skærmoptagelse"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"Ydeevne"</string> + <string name="user_interface" msgid="3712869377953950887">"Brugerflade"</string> + <string name="thermal" msgid="6758074791325414831">"Termisk"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Enhåndstilstand"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Høreapparater"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktivt"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Afbrudt"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Høreapparater"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Par ny enhed"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Klik for at parre en ny enhed"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"foldet"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"foldet ud"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Batteriniveau på styluspen: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Slut din styluspen til en oplader"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Lavt batteriniveau på styluspen"</string> <string name="video_camera" msgid="7654002575156149298">"Videokamera"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Genveje til søgning"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikon for Skjul"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikon for Udvid"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"eller"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Tastaturets baggrundslys"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Niveau %1$d af %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Hjemmestyring"</string> diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml index e7fa9b18b5e6..bf28a88250a6 100644 --- a/packages/SystemUI/res/values-de/strings.xml +++ b/packages/SystemUI/res/values-de/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Morgen automatisch wieder aktivieren"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Funktionen wie „Quick Share“ und „Mein Gerät finden“ verwenden Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth wird morgen früh aktiviert"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Audiofreigabe"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Audioinhalte werden freigegeben"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Akkustand: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Welche Bereiche des Geräts waren betroffen?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Art des Problems auswählen"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Bildschirmaufnahme"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"Leistung"</string> + <string name="user_interface" msgid="3712869377953950887">"Benutzeroberfläche"</string> + <string name="thermal" msgid="6758074791325414831">"Überhitzung"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Einhandmodus"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Hörgeräte"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktiv"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Nicht verbunden"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Hörgeräte"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Neues Gerät koppeln"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Klicken, um neues Gerät zu koppeln"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"zugeklappt"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"aufgeklappt"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Akkustand des Eingabestifts: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Schließe deinen Eingabestift an ein Ladegerät an"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Stylus-Akkustand niedrig"</string> <string name="video_camera" msgid="7654002575156149298">"Videokamera"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Tastenkürzel suchen"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Symbol „Minimieren“"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Symbol „Maximieren“"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"oder"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Tastaturbeleuchtung"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Level %1$d von %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Smart-Home-Steuerung"</string> diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml index 23d704da4f3b..3dd5fc0f1c8c 100644 --- a/packages/SystemUI/res/values-el/strings.xml +++ b/packages/SystemUI/res/values-el/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Αυτόματη ενεργοποίηση ξανά αύριο"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Λειτουργίες όπως το Quick Share και η Εύρεση συσκευής χρησιμοποιούν το Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Το Bluetooth θα ενεργοποιηθεί αύριο το πρωί"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Κοινή χρήση ήχου"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Κοινή χρήση ήχου"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Μπαταρία <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Ήχος"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Ακουστικά"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Ποιο κομμάτι της εμπειρίας συσκευής επηρεάστηκε;"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Επιλογή τύπου προβλήματος"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Εγγραφή οθόνης"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"Απόδοση"</string> + <string name="user_interface" msgid="3712869377953950887">"Διεπαφή χρήστη"</string> + <string name="thermal" msgid="6758074791325414831">"Θερμικό"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Λειτουργία ενός χεριού"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Συσκευές ακοής"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Ενεργά"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Αποσυνδεδεμένα"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Συσκευές ακοής"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Σύζευξη νέας συσκευής"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Κάντε κλικ για σύζευξη νέας συσκευής"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"διπλωμένη"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"ξεδιπλωμένη"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Μπαταρία γραφίδας <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Συνδέστε τη γραφίδα σε έναν φορτιστή"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Χαμηλή στάθμη μπαταρίας γραφίδας"</string> <string name="video_camera" msgid="7654002575156149298">"Βιντεοκάμερα"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Συντομεύσεις αναζήτησης"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Εικονίδιο σύμπτυξης"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Εικονίδιο ανάπτυξης"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ή"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Οπίσθιος φωτισμός πληκτρολογίου"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Επίπεδο %1$d από %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Οικιακοί έλεγχοι"</string> diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml index d71ff1cc42be..e830f3c2a8cd 100644 --- a/packages/SystemUI/res/values-en-rAU/strings.xml +++ b/packages/SystemUI/res/values-en-rAU/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatically turn on again tomorrow"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Features like Quick Share and Find My Device use Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth will turn on tomorrow morning"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Audio sharing"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Sharing audio"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> battery"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string> @@ -366,10 +368,8 @@ <string name="thermal" msgid="6758074791325414831">"Thermal"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"One-handed mode"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Hearing devices"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Active"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Disconnected"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Hearing devices"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Pair new device"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Click to pair new device"</string> @@ -1324,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Search shortcuts"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Collapse icon"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Expand icon"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"or"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Keyboard backlight"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Level %1$d of %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Home controls"</string> diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml index 44e9d25a4d7f..046ce28b6488 100644 --- a/packages/SystemUI/res/values-en-rCA/strings.xml +++ b/packages/SystemUI/res/values-en-rCA/strings.xml @@ -281,8 +281,8 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatically turn on again tomorrow"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Features like Quick Share and Find My Device use Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth will turn on tomorrow morning"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Audio Sharing"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Sharing Audio"</string> + <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Share audio"</string> + <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Sharing audio"</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> battery"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string> @@ -1322,8 +1322,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Search shortcuts"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Collapse icon"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Expand icon"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"or"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Keyboard backlight"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Level %1$d of %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Home Controls"</string> diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml index d71ff1cc42be..e830f3c2a8cd 100644 --- a/packages/SystemUI/res/values-en-rGB/strings.xml +++ b/packages/SystemUI/res/values-en-rGB/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatically turn on again tomorrow"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Features like Quick Share and Find My Device use Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth will turn on tomorrow morning"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Audio sharing"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Sharing audio"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> battery"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string> @@ -366,10 +368,8 @@ <string name="thermal" msgid="6758074791325414831">"Thermal"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"One-handed mode"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Hearing devices"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Active"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Disconnected"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Hearing devices"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Pair new device"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Click to pair new device"</string> @@ -1324,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Search shortcuts"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Collapse icon"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Expand icon"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"or"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Keyboard backlight"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Level %1$d of %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Home controls"</string> diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml index d71ff1cc42be..e830f3c2a8cd 100644 --- a/packages/SystemUI/res/values-en-rIN/strings.xml +++ b/packages/SystemUI/res/values-en-rIN/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatically turn on again tomorrow"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Features like Quick Share and Find My Device use Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth will turn on tomorrow morning"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Audio sharing"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Sharing audio"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> battery"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string> @@ -366,10 +368,8 @@ <string name="thermal" msgid="6758074791325414831">"Thermal"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"One-handed mode"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Hearing devices"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Active"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Disconnected"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Hearing devices"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Pair new device"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Click to pair new device"</string> @@ -1324,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Search shortcuts"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Collapse icon"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Expand icon"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"or"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Keyboard backlight"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Level %1$d of %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Home controls"</string> diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml index c2f709878881..07907a640da4 100644 --- a/packages/SystemUI/res/values-en-rXC/strings.xml +++ b/packages/SystemUI/res/values-en-rXC/strings.xml @@ -281,8 +281,8 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatically turn on again tomorrow"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Features like Quick Share and Find My Device use Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth will turn on tomorrow morning"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Audio Sharing"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Sharing Audio"</string> + <string name="quick_settings_bluetooth_audio_sharing_button" msgid="7545274861795853838">"Share audio"</string> + <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="3069309588231072128">"Sharing audio"</string> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> battery"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string> @@ -1322,8 +1322,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Search shortcuts"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Collapse icon"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Expand icon"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"or"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Keyboard backlight"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Level %1$d of %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Home Controls"</string> diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml index fd800ad30ee6..c622ebec0c84 100644 --- a/packages/SystemUI/res/values-es-rUS/strings.xml +++ b/packages/SystemUI/res/values-es-rUS/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Activar automáticamente mañana"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Las funciones como Quick Share y Encontrar mi dispositivo usan Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"El Bluetooth se activará mañana a la mañana"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Uso compartido de audio"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Compartiendo audio"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> de batería"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Auriculares"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"¿Qué parte de tu exp. del disp. se vio afectada?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Seleccionar tipo de problema"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Grabadora de pant."</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"Rendimiento"</string> + <string name="user_interface" msgid="3712869377953950887">"Interfaz de usuario"</string> + <string name="thermal" msgid="6758074791325414831">"Térmico"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modo una mano"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Dispositivos auditivos"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Activos"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Desconectados"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Dispositivos auditivos"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Vincular dispositivo nuevo"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Haz clic para vincular un dispositivo nuevo"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"plegado"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"desplegado"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Batería de la pluma stylus: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Conecta tu pluma stylus a un cargador"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"La pluma stylus tiene poca batería"</string> <string name="video_camera" msgid="7654002575156149298">"Videocámara"</string> @@ -1328,10 +1324,9 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Buscar combinaciones de teclas"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ícono de contraer"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ícono de expandir"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"o"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Retroiluminación del teclado"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Nivel %1$d de %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Controles de la casa"</string> - <string name="home_controls_dream_description" msgid="4644150952104035789">"Accede rápidamente a controles de la casa como prot. de pantalla"</string> + <string name="home_controls_dream_description" msgid="4644150952104035789">"Usa rápidamente los controles de la casa como protector de pantalla"</string> </resources> diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml index 5bcf582afb13..c021b456e2fc 100644 --- a/packages/SystemUI/res/values-es/strings.xml +++ b/packages/SystemUI/res/values-es/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Volver a activar automáticamente mañana"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Las funciones como Quick Share y Encontrar mi dispositivo usan Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"El Bluetooth se activará mañana por la mañana"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Compartir audio"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Compartiendo audio"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> de batería"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Auriculares"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"¿Qué parte de tu experiencia se ha visto afectada?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Selecciona el tipo de problema"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Grabar pantalla"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"Rendimiento"</string> + <string name="user_interface" msgid="3712869377953950887">"Interfaz de usuario"</string> + <string name="thermal" msgid="6758074791325414831">"Térmico"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modo Una mano"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Audífonos"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Activos"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Desconectados"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Audífonos"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Emparejar nuevo dispositivo"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Haz clic para emparejar un nuevo dispositivo"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"plegado"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"desplegado"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Batería del lápiz óptico al <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Conecta tu lápiz óptico a un cargador"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Batería del lápiz óptico baja"</string> <string name="video_camera" msgid="7654002575156149298">"Videocámara"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Atajos de búsqueda"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Icono de contraer"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Icono de desplegar"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"o"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Retroiluminación del teclado"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Nivel %1$d de %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Controles de la casa"</string> diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml index e7d5e2f42797..003c92577d80 100644 --- a/packages/SystemUI/res/values-et/strings.xml +++ b/packages/SystemUI/res/values-et/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Lülita automaatselt homme uuesti sisse"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Sellised funktsioonid nagu Kiirjagamine ja Leia mu seade kasutavad Bluetoothi."</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth lülitub sisse homme hommikul"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Heli jagamine"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Heli jagamine"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> akut"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Heli"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Peakomplekt"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Millist seadme kasutuskogemuse osa see mõjutas?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Valige probleemi tüüp"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Ekraanisalvestus"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"Jõudlus"</string> + <string name="user_interface" msgid="3712869377953950887">"Kasutajaliides"</string> + <string name="thermal" msgid="6758074791325414831">"Soojus"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Ühekäerežiim"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Kuuldeseadmed"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Ühendatud"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Ühendus on katkestatud"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Kuuldeseadmed"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Uue seadme sidumine"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Uue seadme sidumiseks klõpsake"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"kokku volditud"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"lahti volditud"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Elektronpliiatsi aku <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Ühendage elektronpliiats laadijaga"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Elektronpliiatsi akutase on madal"</string> <string name="video_camera" msgid="7654002575156149298">"Videokaamera"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Otsingu otseteed"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ahendamisikoon"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Laiendamisikoon"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"või"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Klaviatuuri taustavalgustus"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Tase %1$d/%2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Kodu juhtelemendid"</string> diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml index 5e4c5acdfc12..66b79b80fcbf 100644 --- a/packages/SystemUI/res/values-eu/strings.xml +++ b/packages/SystemUI/res/values-eu/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Aktibatu automatikoki berriro bihar"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Quick Share, Bilatu nire gailua eta beste eginbide batzuek Bluetootha erabiltzen dute"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bihar goizean aktibatuko da Bluetootha"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Audioa partekatzea"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Audioa partekatzen"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Bateria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audioa"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Entzungailua"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Gailuaren erabileraren zer alderdiri eragin dio?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Hautatu arazo mota"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Pantaila-grabaketa"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"Errendimendua"</string> + <string name="user_interface" msgid="3712869377953950887">"Erabiltzaile-interfazea"</string> + <string name="thermal" msgid="6758074791325414831">"Termikoa"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Esku bakarreko modua"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Entzumen-gailuak"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktibo"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Deskonektatuta"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Entzumen-gailuak"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Parekatu beste gailu bat"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Egin klik beste gailu bat parekatzeko"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"tolestuta"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"tolestu gabe"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Arkatzaren bateria: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Konektatu arkatza kargagailu batera"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Arkatzak bateria gutxi du"</string> <string name="video_camera" msgid="7654002575156149298">"Bideokamera"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Bilatu lasterbideak"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Tolesteko ikonoa"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Zabaltzeko ikonoa"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"edo"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Teklatuaren hondoko argia"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"%1$d/%2$d maila"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Etxeko gailuen kontrola"</string> diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml index ad24c1b45a24..cdbb6851acc0 100644 --- a/packages/SystemUI/res/values-fa/strings.xml +++ b/packages/SystemUI/res/values-fa/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"فردا دوباره بهطور خودکار روشن شود"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"ویژگیهایی مثل «همرسانی سریع» و «پیدا کردن دستگاهم» از بلوتوث استفاده میکنند"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"بلوتوث فردا صبح روشن خواهد شد"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"اشتراک صدا"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"درحال اشتراکگذاری صدا"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"شارژ باتری <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"صوت"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"هدست"</string> @@ -361,24 +363,19 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"کدام بخش تجربه استفاده از دستگاه تحتتأثیر قرار گرفت؟"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"انتخاب نوع مشکل"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"ضبط صفحهنمایش"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"عملکرد"</string> + <string name="user_interface" msgid="3712869377953950887">"میانای کاربر"</string> + <string name="thermal" msgid="6758074791325414831">"حرارتی"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"حالت یکدستی"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"سمعک"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"فعال"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"اتصال قطع شد"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"سمعک"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"جفت کردن دستگاه جدید"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"برای جفت کردن دستگاه جدید، کلیک کنید"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"پیشتنظیم بهروزرسانی نشد"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"پیشتنظیم"</string> - <string name="live_caption_title" msgid="8916875614623730005">"زیرنویس زنده"</string> + <string name="live_caption_title" msgid="8916875614623730005">"زیرنویس ناشنوایان زنده"</string> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"میکروفون دستگاه لغو انسداد شود؟"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"دوربین دستگاه لغو انسداد شود؟"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"دوربین و میکروفون دستگاه لغو انسداد شود؟"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"تاشده"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"تانشده"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"شارژ باتری قلم <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"قلم را به شارژر وصل کنید"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"باتری قلم ضعیف است"</string> <string name="video_camera" msgid="7654002575156149298">"دوربین ویدیویی"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"جستجوی میانبرها"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"نماد جمع کردن"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"نماد ازهم بازکردن"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"یا"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"نور پسزمینه صفحهکلید"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"سطح %1$d از %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"کنترل خانه هوشمند"</string> diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml index 0fed3d546eae..bea39cb6e704 100644 --- a/packages/SystemUI/res/values-fi/strings.xml +++ b/packages/SystemUI/res/values-fi/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Laita automaattisesti päälle taas huomenna"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Quick Share, Paikanna laite ja tietyt muut ominaisuudet käyttävät Bluetoothia"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth menee päälle huomisaamuna"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Audionjako"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Audiota jaetaan"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Akun taso <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Ääni"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Mitä osaa käyttökokemuksesta ongelma koski?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Valitse ongelman tyyppi"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Näytön tallentaja"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"Suorituskyky"</string> + <string name="user_interface" msgid="3712869377953950887">"Käyttöliittymä"</string> + <string name="thermal" msgid="6758074791325414831">"Lämpökamera"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Yhden käden moodi"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Kuulolaitteet"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktiivinen"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Yhteys katkaistu"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Kuulolaitteet"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Muodosta uusi laitepari"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Muodosta uusi laitepari klikkaamalla"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"taitettu"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"taittamaton"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Näyttökynän akku <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Yhdistä näyttökynä laturiin"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Näyttökynän akku vähissä"</string> <string name="video_camera" msgid="7654002575156149298">"Videokamera"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Pikahaut"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Tiivistyskuvake"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Laajennuskuvake"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"tai"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Näppämistön taustavalo"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Taso %1$d/%2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Kodin ohjaus"</string> diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml index 95c788aab6e7..95b25936f8a4 100644 --- a/packages/SystemUI/res/values-fr-rCA/strings.xml +++ b/packages/SystemUI/res/values-fr-rCA/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Activer le Bluetooth automatiquement demain"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Les fonctionnalités comme Partage rapide et Localiser mon appareil utilisent le Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Le Bluetooth s\'activera demain matin"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Partage audio"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Partage de l\'audio en cours…"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Pile : <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Écouteurs"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Quelle composante de l\'appareil a été affectée?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Sélectionner un type"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Enregistrement écran"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"Performance"</string> + <string name="user_interface" msgid="3712869377953950887">"Interface utilisateur"</string> + <string name="thermal" msgid="6758074791325414831">"Thermique"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Mode Une main"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Appareils auditifs"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Actives"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Déconnectées"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Appareils auditifs"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Associer un nouvel appareil"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Cliquez ici pour associer un nouvel appareil"</string> @@ -1209,8 +1206,8 @@ <string name="qs_user_switch_dialog_title" msgid="3045189293587781366">"Choisir l\'utilisateur"</string> <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# application est active}one{# application est active}many{# d\'applications sont actives}other{# applications sont actives}}"</string> <string name="fgs_dot_content_description" msgid="2865071539464777240">"Nouvelle information"</string> - <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Applications actives"</string> - <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Ces applications sont actives et s\'exécutent même lorsque vous ne les utilisez pas. Cela améliore leur fonctionnalité, mais peut également affecter l\'autonomie de la pile."</string> + <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Applis actives"</string> + <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Ces applis sont actives et s\'exécutent même lorsque vous ne les utilisez pas. Cela améliore leur fonctionnalité, mais peut également affecter l\'autonomie de la pile."</string> <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Arrêter"</string> <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Arrêtée"</string> <string name="clipboard_edit_text_done" msgid="4551887727694022409">"OK"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"plié"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"déplié"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Pile du stylet <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Connectez votre stylet à un chargeur"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Pile du stylet faible"</string> <string name="video_camera" msgid="7654002575156149298">"Caméra"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Recherchez des raccourcis"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Icône Réduire"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Icône Développer"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ou"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Rétroéclairage du clavier"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Niveau %1$d de %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Domotique"</string> diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml index b7abd0ec5a5b..1b8ddc102220 100644 --- a/packages/SystemUI/res/values-fr/strings.xml +++ b/packages/SystemUI/res/values-fr/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Réactiver automatiquement demain"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Certaines fonctionnalités, telles que Quick Share et Localiser mon appareil, utilisent le Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Le Bluetooth sera activé demain matin"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Partage audio"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Audio partagé"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> de batterie"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Casque"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Quel problème avez-vous rencontré avec votre appareil ?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Sélectionnez un type de problème"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Enregistrement de l\'écran"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"Performances"</string> + <string name="user_interface" msgid="3712869377953950887">"Interface utilisateur"</string> + <string name="thermal" msgid="6758074791325414831">"Thermique"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Mode une main"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Appareils auditifs"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Actifs"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Déconnectés"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Appareils auditifs"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Associer un nouvel appareil"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Cliquer pour associer un nouvel appareil"</string> @@ -1212,7 +1209,7 @@ <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Applis actives"</string> <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Ces applis sont actives et s\'exécutent même lorsque vous ne les utilisez pas. Cela améliore leur fonctionnement, mais peut également affecter l\'autonomie de la batterie."</string> <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Arrêter"</string> - <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Arrêtée"</string> + <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Arrêté"</string> <string name="clipboard_edit_text_done" msgid="4551887727694022409">"OK"</string> <string name="clipboard_overlay_text_copied" msgid="1872624400464891363">"Copié"</string> <string name="clipboard_edit_source" msgid="9156488177277788029">"De <xliff:g id="APPNAME">%1$s</xliff:g>"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"plié"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"déplié"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Batterie du stylet à <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Connectez votre stylet à un chargeur"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"La batterie du stylet est faible"</string> <string name="video_camera" msgid="7654002575156149298">"Caméra"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Raccourcis de recherche"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Icône Réduire"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Icône Développer"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ou"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Rétroéclairage du clavier"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Niveau %1$d sur %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Contrôle de la maison"</string> diff --git a/packages/SystemUI/res/values-fr/tiles_states_strings.xml b/packages/SystemUI/res/values-fr/tiles_states_strings.xml index 23c124cce37a..fcdd9f0e6e3a 100644 --- a/packages/SystemUI/res/values-fr/tiles_states_strings.xml +++ b/packages/SystemUI/res/values-fr/tiles_states_strings.xml @@ -58,8 +58,8 @@ </string-array> <string-array name="tile_states_flashlight"> <item msgid="3465257127433353857">"Indisponible"</item> - <item msgid="5044688398303285224">"Désactivée"</item> - <item msgid="8527389108867454098">"Activée"</item> + <item msgid="5044688398303285224">"Désactivé"</item> + <item msgid="8527389108867454098">"Activé"</item> </string-array> <string-array name="tile_states_rotation"> <item msgid="4578491772376121579">"Indisponible"</item> diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml index 55dd05a58bb3..1d0f4c476f1e 100644 --- a/packages/SystemUI/res/values-gl/strings.xml +++ b/packages/SystemUI/res/values-gl/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Volver activar automaticamente mañá"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"As funcións como Quick Share e Localizar o meu dispositivo utilizan o Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"O Bluetooth activarase mañá á mañá"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Audio compartido"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Compartindo audio"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> de batería"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Auriculares"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Cal foi o problema na experiencia co dispositivo?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Selecciona o tipo de problema"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Gravación de pant."</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"Rendemento"</string> + <string name="user_interface" msgid="3712869377953950887">"Interface de usuario"</string> + <string name="thermal" msgid="6758074791325414831">"Térmico"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modo dunha soa man"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Dispositivos auditivos"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Activos"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Desconectados"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Dispositivos auditivos"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Vincular dispositivo novo"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Fai clic para vincular un novo dispositivo"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"dispositivo pregado"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"dispositivo despregado"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Batería do lapis óptico: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Conecta o lapis óptico a un cargador"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"O lapis óptico ten pouca batería"</string> <string name="video_camera" msgid="7654002575156149298">"Videocámara"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Atallos de busca"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Icona de contraer"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Icona de despregar"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ou"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Retroiluminación do teclado"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Nivel %1$d de %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Controis domóticos"</string> diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml index 425a76869587..7a4cfd1c91ce 100644 --- a/packages/SystemUI/res/values-gu/strings.xml +++ b/packages/SystemUI/res/values-gu/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"આવતીકાલે ફરીથી ઑટોમૅટિક રીતે ચાલુ કરો"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"ક્વિક શેર અને Find My Device જેવી સુવિધાઓ બ્લૂટૂથનો ઉપયોગ કરે છે"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"બ્લૂટૂથ આવતીકાલે સવારે ચાલુ થશે"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"ઑડિયો શેરિંગ"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"ઑડિયો શેર કરી રહ્યાં છીએ"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> બૅટરી"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ઑડિયો"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"હૅડસેટ"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"ડિવાઇસ સંબંધી તમારા અનુભવના કયા ભાગને અસર થઈ હતી?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"સમસ્યાનો પ્રકાર પસંદ કરો"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"સ્ક્રીન રેકોર્ડ કરો"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"પર્ફોર્મન્સ"</string> + <string name="user_interface" msgid="3712869377953950887">"યૂઝર ઇન્ટરફેસ"</string> + <string name="thermal" msgid="6758074791325414831">"થર્મલ"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"એક-હાથે વાપરો મોડ"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"સાંભળવામાં મદદ આપતા ડિવાઇસ"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"સક્રિય"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"ડિસ્કનેક્ટેડ છે"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"સાંભળવામાં મદદ આપતા ડિવાઇસ"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"નવા ડિવાઇસ સાથે જોડાણ કરો"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"નવા ડિવાઇસ સાથે જોડાણ કરવા માટે ક્લિક કરો"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"ફોલ્ડ કરેલું"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"અનફોલ્ડ કરેલું"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"સ્ટાઇલસની બૅટરી <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"તમારા સ્ટાઇલસને ચાર્જર સાથે કનેક્ટ કરો"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"સ્ટાઇલસની બૅટરીમાં ચાર્જ ઓછો છે"</string> <string name="video_camera" msgid="7654002575156149298">"વીડિયો કૅમેરા"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"શૉર્ટકટ શોધો"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"\'નાનું કરો\'નું આઇકન"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"\'મોટું કરો\'નું આઇકન"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"અથવા"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"કીબોર્ડની બૅકલાઇટ"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$dમાંથી %1$d લેવલ"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"ઘરેલું સાધનોના નિયંત્રણો"</string> diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml index 8f7b87cbc23e..896af2487564 100644 --- a/packages/SystemUI/res/values-hi/strings.xml +++ b/packages/SystemUI/res/values-hi/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"कल फिर से अपने-आप चालू हो जाए"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"क्विक शेयर और Find My Device जैसी सुविधाएं, ब्लूटूथ का इस्तेमाल करती हैं"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"ब्लूटूथ कल सुबह चालू होगा"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"ऑडियो शेयर करने की सुविधा"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"ऑडियो शेयर किया जा रहा है"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> बैटरी"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ऑडियो"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"हेडसेट"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"आपके डिवाइस की कौनसी सुविधा पर असर पड़ा था?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"समस्या का टाइप चुनें"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"स्क्रीन रिकॉर्डर"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"परफ़ॉर्मेंस"</string> + <string name="user_interface" msgid="3712869377953950887">"यूज़र इंटरफ़ेस"</string> + <string name="thermal" msgid="6758074791325414831">"थर्मल"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"वन-हैंडेड मोड"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"कान की मशीनें"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"ऐक्टिव"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"डिसकनेक्ट हो गया"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"कान की मशीनें"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"नया डिवाइस जोड़ें"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"नया डिवाइस जोड़ने के लिए क्लिक करें"</string> @@ -1112,7 +1109,7 @@ <string name="media_output_dialog_multiple_devices" msgid="1093771040315422350">"<xliff:g id="COUNT">%1$d</xliff:g> डिवाइस चुने गए"</string> <string name="media_output_dialog_disconnected" msgid="7090512852817111185">"(डिसकनेक्ट हो गया)"</string> <string name="media_output_dialog_connect_failed" msgid="3080972621975339387">"स्विच नहीं किया जा सकता. फिर से कोशिश करने के लिए टैप करें."</string> - <string name="media_output_dialog_pairing_new" msgid="5098212763195577270">"किसी डिवाइस को कनेक्ट करें"</string> + <string name="media_output_dialog_pairing_new" msgid="5098212763195577270">"कोई डिवाइस कनेक्ट करें"</string> <string name="media_output_dialog_launch_app_text" msgid="1527413319632586259">"इस सेशन को कास्ट करने के लिए, कृपया ऐप्लिकेशन खोलें."</string> <string name="media_output_dialog_unknown_launch_app_name" msgid="1084899329829371336">"अनजान ऐप्लिकेशन"</string> <string name="media_output_dialog_button_stop_casting" msgid="6581379537930199189">"कास्ट करना बंद करें"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"डिवाइस फ़ोल्ड किया गया"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"डिवाइस अनफ़ोल्ड किया गया"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"स्टाइलस की बैटरी <xliff:g id="PERCENTAGE">%s</xliff:g> है"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"अपने स्टाइलस को चार्ज करें"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"स्टाइलस की बैटरी कम है"</string> <string name="video_camera" msgid="7654002575156149298">"वीडियो कैमरा"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"सर्च शॉर्टकट"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"छोटा करने का आइकॉन"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"बड़ा करने का आइकॉन"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"या"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"कीबोर्ड की बैकलाइट"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d में से %1$d लेवल"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"होम कंट्रोल"</string> diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml index bc84c1d0bc65..acf253dd00a5 100644 --- a/packages/SystemUI/res/values-hr/strings.xml +++ b/packages/SystemUI/res/values-hr/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatski uključi ponovno sutra"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Značajke kao što su brzo dijeljenje i Pronađi moj uređaj koriste Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth će se uključiti sutra ujutro"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Zajedničko slušanje"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Zajedničko slušanje"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> baterije"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Slušalice"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Na koji dio doživljaja na uređaju to utjecalo?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Odaberite vrstu problema"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Snimanje zaslona"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"Izvedba"</string> + <string name="user_interface" msgid="3712869377953950887">"Korisničko sučelje"</string> + <string name="thermal" msgid="6758074791325414831">"Termalno"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Način rada jednom rukom"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Slušna pomagala"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktivno"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Nije povezano"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Slušna pomagala"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Uparite novi uređaj"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kliknite da biste uparili novi uređaj"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"zatvoreno"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"otvoreno"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Baterija olovke <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Priključite pisaljku na punjač"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Slaba baterija pisaljke"</string> <string name="video_camera" msgid="7654002575156149298">"Videokamera"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Prečaci za pretraživanje"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona za sažimanje"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona za proširivanje"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ili"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Pozadinsko osvjetljenje tipkovnice"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Razina %1$d od %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Upravljanje uređajima"</string> diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml index 4b36026b5f06..c73511e18677 100644 --- a/packages/SystemUI/res/values-hu/strings.xml +++ b/packages/SystemUI/res/values-hu/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatikus visszakapcsolás holnap"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Egyes funkciók (például a Quick Share és a Készülékkereső) Bluetootht használnak"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"A Bluetooth holnap reggel bekapcsol"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Hang megosztása"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Hang megosztása…"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Akkumulátor: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Hang"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string> @@ -292,7 +294,7 @@ <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Automatikus elforgatás"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Automatikus képernyőforgatás"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Tartózkodási hely"</string> - <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Képernyővédő"</string> + <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Képernyőkímélő"</string> <string name="quick_settings_camera_label" msgid="5612076679385269339">"Hozzáférés a kamerához"</string> <string name="quick_settings_mic_label" msgid="8392773746295266375">"Mikrofonelérés"</string> <string name="quick_settings_camera_mic_available" msgid="1453719768420394314">"Rendelkezésre áll"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Az eszközhasználati élmény mely része érintett?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Problématípus kiválasztása"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Képernyőrögzítés"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"Teljesítmény"</string> + <string name="user_interface" msgid="3712869377953950887">"Kezelőfelület"</string> + <string name="thermal" msgid="6758074791325414831">"Termikus"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Egykezes mód"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Hallókészülékek"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktív"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Leválasztva"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Hallókészülékek"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Új eszköz párosítása"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kattintson új eszköz párosításához"</string> @@ -601,7 +598,7 @@ <string name="screen_pinning_exit" msgid="4553787518387346893">"Alkalmazás kitűzése megszüntetve"</string> <string name="stream_voice_call" msgid="7468348170702375660">"Telefonhívás"</string> <string name="stream_system" msgid="7663148785370565134">"Rendszer"</string> - <string name="stream_ring" msgid="7550670036738697526">"Csörgetés"</string> + <string name="stream_ring" msgid="7550670036738697526">"Csörgés"</string> <string name="stream_music" msgid="2188224742361847580">"Média"</string> <string name="stream_alarm" msgid="16058075093011694">"Ébresztő"</string> <string name="stream_notification" msgid="7930294049046243939">"Értesítés"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"összehajtva"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"kihajtva"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Érintőceruza töltöttségi szintje: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Tegye töltőre az érintőceruzát"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Az érintőceruza töltöttsége alacsony"</string> <string name="video_camera" msgid="7654002575156149298">"Videokamera"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Billentyűparancsok keresése"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Összecsukás ikon"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Kibontás ikon"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"vagy"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"A billentyűzet háttérvilágítása"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Fényerő: %2$d/%1$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Otthon vezérlése"</string> diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml index d8e36ad93c04..f1462be4261a 100644 --- a/packages/SystemUI/res/values-hy/strings.xml +++ b/packages/SystemUI/res/values-hy/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Վաղը նորից ավտոմատ միացնել"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Bluetooth-ն օգտագործում են, օրինակ, Quick Share և «Գտնել իմ սարքը» գործառույթները"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth-ը կմիանա վաղն առավոտյան"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Աուդիոյի փոխանցում"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Աուդիոն փոխանցվում է"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Մարտկոցի լիցքը՝ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Աուդիո"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Ականջակալ"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Սարքի ո՞ր մասի հետ է կապված խնդիրը։"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Ընտրեք խնդրի տեսակը"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Էկրանի տեսագրում"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"Արդյունավետություն"</string> + <string name="user_interface" msgid="3712869377953950887">"Օգտատիրական ինտերֆեյս"</string> + <string name="thermal" msgid="6758074791325414831">"Ջերմատեսիլ"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Մեկ ձեռքի ռեժիմ"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Լսողական սարքեր"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Ակտիվ է"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Անջատված է"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Լսողական սարքեր"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Նոր սարքի զուգակցում"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Սեղմեք՝ նոր սարք զուգակցելու համար"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"ծալված"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"բացված"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Ստիլուսի մարտկոցի լիցքը՝ <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Ձեր ստիլուսը միացրեք լիցքավորիչի"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Ստիլուսի մարտկոցի լիցքի ցածր մակարդակ"</string> <string name="video_camera" msgid="7654002575156149298">"Տեսախցիկ"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Դյուրանցումների որոնում"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ծալել պատկերակը"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ծավալել պատկերակը"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"կամ"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Հետին լուսավորությամբ ստեղնաշար"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"%1$d՝ %2$d-ից"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Տան կառավարման տարրեր"</string> diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml index 22d0de5ecc0c..7525e9aea79a 100644 --- a/packages/SystemUI/res/values-in/strings.xml +++ b/packages/SystemUI/res/values-in/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Otomatis aktifkan lagi besok"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Fitur seperti Quick Share dan Temukan Perangkat Saya menggunakan Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth akan dinyalakan besok pagi"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Berbagi Audio"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Berbagi Audio"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Baterai <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Bagian pengalaman perangkat mana yang terpengaruh?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Pilih jenis masalah"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Perekaman layar"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"Performa"</string> + <string name="user_interface" msgid="3712869377953950887">"Antarmuka Pengguna"</string> + <string name="thermal" msgid="6758074791325414831">"Termal"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Mode satu tangan"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Alat bantu dengar"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktif"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Terputus"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Alat bantu dengar"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Sambungkan perangkat baru"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Klik untuk menyambungkan perangkat baru"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"ditutup"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"dibuka"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Baterai stilus <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Hubungkan stilus ke pengisi daya"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Baterai stilus lemah"</string> <string name="video_camera" msgid="7654002575156149298">"Kamera video"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Pintasan penelusuran"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikon ciutkan"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikon luaskan"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"atau"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Lampu latar keyboard"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Tingkat %1$d dari %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Kontrol Rumah"</string> diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml index b631c47363d4..38507900ebac 100644 --- a/packages/SystemUI/res/values-is/strings.xml +++ b/packages/SystemUI/res/values-is/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Kveikja sjálfkrafa aftur á morgun"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Eiginleikar eins og Flýtideiling og Finna tækið mitt nota Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Kveikt verður á Bluetooth í fyrramálið"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Hljóði deilt"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Deilir hljóði"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> rafhlöðuhleðsla"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Hljóð"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Höfuðtól"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Hvað í tækjaupplifuninni varð fyrir áhrifum?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Veldu gerð vandamáls"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Skjáupptaka"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"Afköst"</string> + <string name="user_interface" msgid="3712869377953950887">"Notandaviðmót"</string> + <string name="thermal" msgid="6758074791325414831">"Varmi"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Einhent stilling"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Heyrnartæki"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Virk"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Aftengd"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Heyrnartæki"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Para nýtt tæki"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Smelltu til að para nýtt tæki"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"samanbrotið"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"opið"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Pennarafhlaða <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Tengdu pennann við hleðslutæki"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Rafhlaða pennans er að tæmast"</string> <string name="video_camera" msgid="7654002575156149298">"Kvikmyndatökuvél"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Leitarflýtileiðir"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Minnka tákn"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Stækka tákn"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"eða"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Baklýsing lyklaborðs"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Stig %1$d af %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Heimastýringar"</string> diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml index 5494b165bda3..a0d54c267c7b 100644 --- a/packages/SystemUI/res/values-it/strings.xml +++ b/packages/SystemUI/res/values-it/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Riattiva automaticamente domani"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Funzionalità come Quick Share e Trova il mio dispositivo usano il Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Il Bluetooth verrà attivato domani mattina"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Condivisione audio"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Condivisione audio in corso…"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Batteria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Auricolare"</string> @@ -366,10 +368,8 @@ <string name="thermal" msgid="6758074791325414831">"Termico"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modalità a una mano"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Apparecchi acustici"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Attivi"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Disconnessi"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Protesi uditive"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Accoppia nuovo dispositivo"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Fai clic per accoppiare un nuovo dispositivo"</string> @@ -1324,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Scorciatoie per la ricerca"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Icona Comprimi"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Icona Espandi"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"oppure"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Retroilluminazione della tastiera"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Livello %1$d di %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Controlli della casa"</string> diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml index 2ad95fb93e82..f773b84a0e67 100644 --- a/packages/SystemUI/res/values-iw/strings.xml +++ b/packages/SystemUI/res/values-iw/strings.xml @@ -278,11 +278,13 @@ <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"נשמר"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ניתוק"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"הפעלה"</string> - <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"החיבור יופעל שוב אוטומטית מחר"</string> + <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"יופעל שוב אוטומטית מחר"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"תכונות כמו \'שיתוף מהיר\' ו\'איפה המכשיר שלי\' משתמשות ב-Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"חיבור ה-Bluetooth יופעל מחר בבוקר"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"שיתוף אודיו"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"מתבצע שיתוף של האודיו"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> סוללה"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"אודיו"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"אוזניות"</string> @@ -366,10 +368,8 @@ <string name="thermal" msgid="6758074791325414831">"תרמי"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"מצב שימוש ביד אחת"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"מכשירי שמיעה"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"פעיל"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"מנותק"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"מכשירי שמיעה"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"התאמה של מכשיר חדש"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"צריך ללחוץ כדי להתאים מכשיר חדש"</string> @@ -620,7 +620,7 @@ <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"בקרת רעש"</string> <string name="volume_panel_spatial_audio_title" msgid="3367048857932040660">"אודיו מרחבי"</string> <string name="volume_panel_spatial_audio_off" msgid="4177490084606772989">"השבתה"</string> - <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"מצב קבוע"</string> + <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"מצב סטטי"</string> <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"מעקב אחר תנועות הראש"</string> <string name="volume_ringer_change" msgid="3574969197796055532">"יש להקיש כדי לשנות את מצב תוכנת הצלצול"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"השתקה"</string> @@ -1198,7 +1198,7 @@ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"ה-Wi-Fi לא יתחבר באופן אוטומטי בינתיים"</string> <string name="see_all_networks" msgid="3773666844913168122">"הצגת הכול"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"כדי לעבור בין רשתות, צריך לנתק את האתרנט"</string> - <string name="wifi_scan_notify_message" msgid="3753839537448621794">"כדי לשפר את חוויית השימוש במכשיר, אפליקציות ושירותים יוכלו לחפש רשתות Wi-Fi בכל שלב, גם כאשר ה-Wi-Fi כבוי. אפשר לשנות זאת בהגדרות של חיפוש נקודות Wi-Fi. "<annotation id="link">"שינוי"</annotation></string> + <string name="wifi_scan_notify_message" msgid="3753839537448621794">"כדי לשפר את חוויית השימוש במכשיר, אפליקציות ושירותים יוכלו לחפש רשתות Wi-Fi בכל שלב, גם כאשר ה-Wi-Fi כבוי. אפשר לשנות את זה בהגדרות של חיפוש נקודות Wi-Fi. "<annotation id="link">"שינוי"</annotation></string> <string name="turn_off_airplane_mode" msgid="8425587763226548579">"השבתה של מצב טיסה"</string> <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"אפליקציית <xliff:g id="APPNAME">%1$s</xliff:g> מבקשת להוסיף להגדרות המהירות את הלחצן הבא"</string> <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"להוסיף לחצן"</string> @@ -1324,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"קיצורי דרך לחיפוש"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"סמל הכיווץ"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"סמל ההרחבה"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"או"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"התאורה האחורית במקלדת"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"רמה %1$d מתוך %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"שליטה במכשירים"</string> diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml index 15c1f8b4ca77..6b5d08241582 100644 --- a/packages/SystemUI/res/values-ja/strings.xml +++ b/packages/SystemUI/res/values-ja/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"明日自動的に ON に戻す"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Quick Share や「デバイスを探す」などの機能は Bluetooth を使用します"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"明日の朝に Bluetooth が ON になります"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"音声の共有"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"音声を共有中"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"バッテリー <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"オーディオ"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ヘッドセット"</string> @@ -366,10 +368,8 @@ <string name="thermal" msgid="6758074791325414831">"温度"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"片手モード"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"補聴器"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"アクティブ"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"未接続"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"補聴器"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"新しいデバイスとペア設定"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"クリックすると、新しいデバイスをペア設定できます"</string> @@ -1324,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"検索ショートカット"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"閉じるアイコン"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"開くアイコン"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"または"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"キーボード バックライト"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"レベル %1$d/%2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"ホーム コントロール"</string> diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml index 4d17179f9ead..399c1f0dc4c4 100644 --- a/packages/SystemUI/res/values-ka/strings.xml +++ b/packages/SystemUI/res/values-ka/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"ხელახლა ავტომატურად ჩართვა ხვალ"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"ისეთი ფუნქციები, როგორიცაა სწრაფი გაზიარება და ჩემი მოწყობილობის პოვნა, იყენებს Bluetooth-ს"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth ჩაირთვება ხვალ დილით"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"აუდიოს გაზიარება"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"აუდიოს გაზიარება"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ბატარეა"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"აუდიო"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ყურსაცვამი"</string> @@ -366,16 +368,14 @@ <string name="thermal" msgid="6758074791325414831">"თერმული"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"ცალი ხელის რეჟიმი"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"სმენის აპარატები"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"აქტიური"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"კავშირი გაწყვეტილია"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"სმენის აპარატები"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"ახალი მოწყობილობის დაწყვილება"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"დააწკაპუნეთ ახალი მოწყობილობის დასაწყვილებლად"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"წინასწარ დაყენებული პარამეტრების განახლება ვერ მოხერხდა"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"წინასწარ დაყენებული"</string> - <string name="live_caption_title" msgid="8916875614623730005">"პირდაპირი სუბტიტრები"</string> + <string name="live_caption_title" msgid="8916875614623730005">"ავტოსუბტიტრები"</string> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"გსურთ მოწყობილობის მიკროფონის განბლოკვა?"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"გსურთ მოწყობილობის კამერის განბლოკვა?"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"გსურთ მოწყობილობის კამერის და მიკროფონის განბლოკვა?"</string> @@ -1324,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ძიების მალსახმობები"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"ხატულის ჩაკეცვა"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ხატულის გაფართოება"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ან"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"კლავიატურის შენათება"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"დონე: %1$d %2$d-დან"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"სახლის კონტროლი"</string> diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml index 08e9fa203463..a3e61790692a 100644 --- a/packages/SystemUI/res/values-kk/strings.xml +++ b/packages/SystemUI/res/values-kk/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Ертең автоматты түрде қосылсын"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Quick Share және Find My Device сияқты функциялар Bluetooth-ты пайдаланады."</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth ертең таңертең қосылады."</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Аудио бөлісу"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Аудио бөлісу әрекеті орындалып жатыр."</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Батарея деңгейі: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Aудио"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Гарнитура"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Құрылғы қызметінің қандай түріне әсер етті?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Мәселе түрін таңдаңыз."</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Экранды жазу"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"Өнімділік режимі"</string> + <string name="user_interface" msgid="3712869377953950887">"Пайдаланушы интерфейсі"</string> + <string name="thermal" msgid="6758074791325414831">"Термовизия"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Бір қолмен басқару режимі"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Есту құрылғылары"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Қосулы"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Ажыратулы"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Есту құрылғылары"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Жаңа құрылғыны жұптау"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Жаңа құрылғыны жұптау үшін басыңыз."</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"жабық"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"ашық"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Стилус батареясы: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Стилусты зарядтағышқа жалғаңыз."</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Стилус батареясының заряды аз"</string> <string name="video_camera" msgid="7654002575156149298">"Бейнекамера"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Іздеу жылдам пәрмендері"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Жию белгішесі"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Жаю белгішесі"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"немесе"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Пернетақта жарығы"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Деңгей: %1$d/%2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Үй басқару элементтері"</string> diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml index d6538bdf9bfe..7c6f12720cd6 100644 --- a/packages/SystemUI/res/values-km/strings.xml +++ b/packages/SystemUI/res/values-km/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"បើកដោយស្វ័យប្រវត្តិម្ដងទៀតនៅថ្ងៃស្អែក"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"មុខងារដូចជា Quick Share និង \"រកឧបករណ៍របស់ខ្ញុំ\" ប្រើប៊្លូធូស"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"ប៊្លូធូសនឹងបើកនៅព្រឹកស្អែក"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"ការស្ដាប់សំឡេងរួមគ្នា"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"កំពុងស្ដាប់សំឡេងរួមគ្នា"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"ថ្ម <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"សំឡេង"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"កាស"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"តើផ្នែកអ្វីនៃបទពិសោធប្រើប្រាស់ឧបករណ៍របស់អ្នកបានរងការប៉ះពាល់?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"ជ្រើសរើសប្រភេទបញ្ហា"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"ការថតវីដេអូអេក្រង់"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"ប្រតិបត្តិការ"</string> + <string name="user_interface" msgid="3712869377953950887">"ផ្ទៃប៉ះ"</string> + <string name="thermal" msgid="6758074791325414831">"កម្ដៅ"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"មុខងារប្រើដៃម្ខាង"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"ឧបករណ៍ជំនួយការស្ដាប់"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"សកម្ម"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"បានផ្ដាច់"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"ឧបករណ៍ជំនួយការស្ដាប់"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"ផ្គូផ្គងឧបករណ៍ថ្មី"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"ចុច ដើម្បីផ្គូផ្គងឧបករណ៍ថ្មី"</string> @@ -1201,7 +1198,7 @@ <string name="wifi_wont_autoconnect_for_now" msgid="5782282612749867762">"Wi-Fi នឹងមិនភ្ជាប់ដោយស្វ័យប្រវត្តិក្នុងពេលនេះទេ"</string> <string name="see_all_networks" msgid="3773666844913168122">"មើលទាំងអស់"</string> <string name="to_switch_networks_disconnect_ethernet" msgid="6698111101156951955">"ដើម្បីប្ដូរបណ្ដាញ សូមផ្ដាច់អ៊ីសឺរណិត"</string> - <string name="wifi_scan_notify_message" msgid="3753839537448621794">"ដើម្បីធ្វើឱ្យបទពិសោធន៍ប្រើប្រាស់ឧបករណ៍ប្រសើរឡើង កម្មវិធី និងសេវាកម្មនៅតែអាចស្កេនរកបណ្ដាញ Wi‑Fi បានគ្រប់ពេល ទោះបីជានៅពេលដែលបិទ Wi‑Fi ក៏ដោយ។ អ្នកអាចប្ដូរវាបាននៅក្នុងការកំណត់ការស្កេន Wi‑Fi។ "<annotation id="link">"ប្ដូរ"</annotation></string> + <string name="wifi_scan_notify_message" msgid="3753839537448621794">"ដើម្បីធ្វើឱ្យបទពិសោធប្រើប្រាស់ឧបករណ៍ប្រសើរឡើង កម្មវិធី និងសេវាកម្មនៅតែអាចស្កេនរកបណ្ដាញ Wi‑Fi បានគ្រប់ពេល ទោះបីជានៅពេលដែលបិទ Wi‑Fi ក៏ដោយ។ អ្នកអាចប្ដូរវាបាននៅក្នុងការកំណត់ការស្កេន Wi‑Fi។ "<annotation id="link">"ប្ដូរ"</annotation></string> <string name="turn_off_airplane_mode" msgid="8425587763226548579">"បិទមុខងារពេលជិះយន្តហោះ"</string> <string name="qs_tile_request_dialog_text" msgid="3501359944139877694">"<xliff:g id="APPNAME">%1$s</xliff:g> ចង់បញ្ចូលប្រអប់ខាងក្រោមទៅក្នុងការកំណត់រហ័ស"</string> <string name="qs_tile_request_dialog_add" msgid="4888460910694986304">"បញ្ចូលប្រអប់"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"បត់"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"លា"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"ថ្មប៊ិក <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"ភ្ជាប់ប៊ិករបស់អ្នកជាមួយឆ្នាំងសាក"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"ថ្មប៊ិកនៅសល់តិច"</string> <string name="video_camera" msgid="7654002575156149298">"កាមេរ៉ាវីដេអូ"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ផ្លូវកាត់ការស្វែងរក"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"រូបតំណាង \"បង្រួម\""</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"រូបតំណាង \"ពង្រីក\""</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ឬ"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"ពន្លឺក្រោយក្ដារចុច"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"កម្រិតទី %1$d នៃ %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"ការគ្រប់គ្រងផ្ទះ"</string> diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml index 92a8b6be128b..31aa87504ca3 100644 --- a/packages/SystemUI/res/values-kn/strings.xml +++ b/packages/SystemUI/res/values-kn/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"ನಾಳೆ ಪುನಃ ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಆನ್ ಮಾಡಿ"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"ಕ್ವಿಕ್ ಶೇರ್ ಮತ್ತು Find My Device ನಂತಹ ಫೀಚರ್ಗಳು ಬ್ಲೂಟೂತ್ ಅನ್ನು ಬಳಸುತ್ತವೆ"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"ಬ್ಲೂಟೂತ್ ನಾಳೆ ಬೆಳಗ್ಗೆ ಆನ್ ಆಗುತ್ತದೆ"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"ಆಡಿಯೋ ಹಂಚಿಕೊಳ್ಳುವಿಕೆ"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"ಆಡಿಯೋವನ್ನು ಹಂಚಿಕೊಳ್ಳಲಾಗುತ್ತಿದೆ"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ಬ್ಯಾಟರಿ"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ಆಡಿಯೋ"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ಹೆಡ್ಸೆಟ್"</string> @@ -361,24 +363,19 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"ಸಾಧನ ಬಳಸುವಾಗ ನೀವು ಯಾವ ರೀತಿಯ ಸಮಸ್ಯೆ ಎದುರಿಸುತ್ತೀರಿ?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"ಸಮಸ್ಯೆಯ ಪ್ರಕಾರವನ್ನು ಆಯ್ಕೆಮಾಡಿ"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"ಸ್ಕ್ರೀನ್ ರೆಕಾರ್ಡ್"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"ಪರ್ಫಾರ್ಮೆನ್ಸ್"</string> + <string name="user_interface" msgid="3712869377953950887">"ಬಳಕೆದಾರ ಇಂಟರ್ಫೇಸ್"</string> + <string name="thermal" msgid="6758074791325414831">"ಥರ್ಮಲ್"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"ಒಂದು ಕೈ ಮೋಡ್"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"ಶ್ರವಣ ಸಾಧನಗಳು"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"ಸಕ್ರಿಯವಾಗಿದೆ"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"ಡಿಸ್ಕನೆಕ್ಟ್ ಆಗಿದೆ"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"ಶ್ರವಣ ಸಾಧನಗಳು"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"ಹೊಸ ಸಾಧನವನ್ನು ಪೇರ್ ಮಾಡಿ"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"ಹೊಸ ಸಾಧನವನ್ನು ಜೋಡಿಸಲು ಕ್ಲಿಕ್ ಮಾಡಿ"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"ಪ್ರಿಸೆಟ್ ಅನ್ನು ಅಪ್ಡೇಟ್ ಮಾಡಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"ಪ್ರಿಸೆಟ್"</string> - <string name="live_caption_title" msgid="8916875614623730005">"ಲೈವ್ ಶೀರ್ಷಿಕೆ"</string> + <string name="live_caption_title" msgid="8916875614623730005">"ಲೈವ್ ಕ್ಯಾಪ್ಶನ್"</string> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"ಸಾಧನದ ಮೈಕ್ರೋಫೋನ್ ನಿರ್ಬಂಧವನ್ನು ತೆಗೆಯಬೇಕೆ?"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"ಸಾಧನದ ಕ್ಯಾಮರಾ ನಿರ್ಬಂಧವನ್ನು ತೆಗೆಯಬೇಕೆ?"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"ಸಾಧನದ ಕ್ಯಾಮರಾ ಮತ್ತು ಮೈಕ್ರೋಫೋನ್ ಅನ್ನು ಅನ್ಬ್ಲಾಕ್ ಮಾಡಬೇಕೇ?"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"ಫೋಲ್ಡ್ ಮಾಡಿರುವುದು"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"ಅನ್ಫೋಲ್ಡ್ ಮಾಡಿರುವುದು"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"ಸ್ಟೈಲಸ್ ಬ್ಯಾಟರಿ <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"ನಿಮ್ಮ ಸ್ಟೈಲಸ್ ಅನ್ನು ಚಾರ್ಜರ್ಗೆ ಕನೆಕ್ಟ್ ಮಾಡಿ"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"ಸ್ಟೈಲಸ್ ಬ್ಯಾಟರಿ ಕಡಿಮೆಯಿದೆ"</string> <string name="video_camera" msgid="7654002575156149298">"ವೀಡಿಯೊ ಕ್ಯಾಮರಾ"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ಹುಡುಕಾಟದ ಶಾರ್ಟ್ಕಟ್ಗಳು"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"ಕುಗ್ಗಿಸುವ ಐಕಾನ್"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ವಿಸ್ತೃತಗೊಳಿಸುವ ಐಕಾನ್"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ಅಥವಾ"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"ಕೀಬೋರ್ಡ್ ಬ್ಯಾಕ್ಲೈಟ್"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d ರಲ್ಲಿ %1$d ಮಟ್ಟ"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"ಮನೆ ನಿಯಂತ್ರಣಗಳು"</string> diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml index b141b48de023..91266b7aea10 100644 --- a/packages/SystemUI/res/values-ko/strings.xml +++ b/packages/SystemUI/res/values-ko/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"내일 다시 자동으로 사용 설정"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Quick Share, 내 기기 찾기 등의 기능에서 블루투스를 사용합니다."</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"블루투스가 내일 아침에 켜집니다."</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"오디오 공유"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"오디오 공유 중"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"배터리 <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"오디오"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"헤드셋"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"기기 경험의 어떤 부분에 영향이 있었나요?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"문제 유형 선택"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"화면 녹화"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"성능"</string> + <string name="user_interface" msgid="3712869377953950887">"사용자 인터페이스"</string> + <string name="thermal" msgid="6758074791325414831">"열화상"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"한 손 사용 모드"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"청각 보조 기기"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"활성"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"연결 끊김"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"청각 보조 기기"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"새 기기와 페어링"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"새 기기와 페어링하려면 클릭하세요"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"접은 상태"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"펼친 상태"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"스타일러스 배터리 <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"스타일러스를 충전기에 연결하세요"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"스타일러스 배터리 부족"</string> <string name="video_camera" msgid="7654002575156149298">"비디오 카메라"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"검색 바로가기"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"접기 아이콘"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"확장 아이콘"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"또는"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"키보드 백라이트"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d단계 중 %1$d단계"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"홈 컨트롤"</string> diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml index dc7227ee4779..a3fd0a2e1f1f 100644 --- a/packages/SystemUI/res/values-ky/strings.xml +++ b/packages/SystemUI/res/values-ky/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Эртең автоматтык түрдө кайра күйгүзүү"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Bluetooth Тез бөлүшүү жана Түзмөгүм кайда? сыяктуу функцияларда колдонулат"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth эртең таңда күйөт"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Чогуу угуу"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Чогуу угулууда"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Батареянын деңгээли <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудио"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Гарнитура"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Түзмөгүңүздүн кайсы бөлүгүнө кедергиси тийди?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Маселенин түрүн тандоо"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Экрандан видео жаздырып алуу"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"Иштин майнаптуулугу"</string> + <string name="user_interface" msgid="3712869377953950887">"Колдонуучунун интерфейси"</string> + <string name="thermal" msgid="6758074791325414831">"Жылуулук"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Бир кол режими"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Угуу аппараттары"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Жигердүү"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Ажыратылды"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Угуу аппараттары"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Жаңы түзмөк кошуу"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Жаңы түзмөк кошуу үчүн басыңыз"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"бүктөлгөн"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"ачылган"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Стилустун батареясы: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Стилусту кубаттаңыз"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Стилустун батареясы отурайын деп калды"</string> <string name="video_camera" msgid="7654002575156149298">"Видео камера"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Ыкчам баскычтарды издөө"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Жыйыштыруу сүрөтчөсү"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Жайып көрсөтүү сүрөтчөсү"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"же"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Баскычтоптун жарыгы"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d ичинен %1$d-деңгээл"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Үйдөгү түзмөктөрдү тескөө"</string> diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml index 58940842729b..31182fd7f740 100644 --- a/packages/SystemUI/res/values-lo/strings.xml +++ b/packages/SystemUI/res/values-lo/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"ເປີດໃຊ້ໂດຍອັດຕະໂນມັດອີກຄັ້ງມື້ອື່ນ"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"ຄຸນສົມບັດຕ່າງໆໃຊ້ Bluetooth ເຊັ່ນ: ການແຊຣ໌ດ່ວນ ແລະ ຊອກຫາອຸປະກອນຂອງຂ້ອຍ"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth ຈະເປີດມື້ອື່ນເຊົ້າ"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"ການແບ່ງປັນສຽງ"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"ກຳລັງແບ່ງປັນສຽງ"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"ແບັດເຕີຣີ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ສຽງ"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ຊຸດຫູຟັງ"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"ສ່ວນໃດຂອງປະສົບການອຸປະກອນຂອງທ່ານໄດ້ຮັບຜົນກະທົບ?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"ເລືອກປະເພດບັນຫາ"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"ບັນທຶກໜ້າຈໍ"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"ປະສິດທິພາບ"</string> + <string name="user_interface" msgid="3712869377953950887">"ສ່ວນຕິດຕໍ່ຜູ້ໃຊ້"</string> + <string name="thermal" msgid="6758074791325414831">"ຄວາມຮ້ອນ"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"ໂໝດມືດຽວ"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"ອຸປະກອນຊ່ວຍຟັງ"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"ນຳໃຊ້ຢູ່"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"ຕັດການເຊື່ອມຕໍ່ແລ້ວ"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"ອຸປະກອນຊ່ວຍຟັງ"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"ຈັບຄູ່ອຸປະກອນໃໝ່"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"ຄລິກເພື່ອຈັບຄູ່ອຸປະກອນໃໝ່"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"ພັບແລ້ວ"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"ກາງອອກແລ້ວ"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"ແບັດເຕີຣີປາກກາ <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"ເຊື່ອມຕໍ່ປາກກາຂອງທ່ານກັບສາຍສາກ"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"ແບັດເຕີຣີປາກກາເຫຼືອໜ້ອຍ"</string> <string name="video_camera" msgid="7654002575156149298">"ກ້ອງວິດີໂອ"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ທາງລັດການຊອກຫາ"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"ໄອຄອນຫຍໍ້ລົງ"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ໄອຄອນຂະຫຍາຍ"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ຫຼື"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"ໄຟປຸ່ມແປ້ນພິມ"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"ລະດັບທີ %1$d ຈາກ %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"ການຄວບຄຸມເຮືອນ"</string> diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml index 2c9eed8e6d53..62c291135b07 100644 --- a/packages/SystemUI/res/values-lt/strings.xml +++ b/packages/SystemUI/res/values-lt/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatiškai vėl įjungti rytoj"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Tokioms funkcijoms kaip „Spartusis bendrinimas“ ir „Rasti įrenginį“ naudojamas „Bluetooth“ ryšys"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"„Bluetooth“ ryšys bus įjungtas rytoj ryte"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Garso įrašų bendrinimas"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Bendrinamas garso įrašas"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Akumuliatorius: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Garsas"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Virtualiosios realybės įrenginys"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Kuri įrenginio funkcija buvo paveikta?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Pasirinkite problemos tipą"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Ekrano įrašas"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"Našumas"</string> + <string name="user_interface" msgid="3712869377953950887">"Naudotojo sąsaja"</string> + <string name="thermal" msgid="6758074791325414831">"Šiluminis"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Vienos rankos režimas"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Klausos įrenginiai"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktyvus"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Atjungtas"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Klausos įrenginiai"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Susieti naują įrenginį"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Spustelėkite, kad susietumėte naują įrenginį"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"sulenkta"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"nesulenkta"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Liko rašiklio akumuliatoriaus energijos: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Prijunkite rašiklį prie kroviklio"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Senka rašiklio akumuliatorius"</string> <string name="video_camera" msgid="7654002575156149298">"Vaizdo kamera"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Paieškos šaukiniai"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Sutraukimo piktograma"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Išskleidimo piktograma"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"arba"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Klaviatūros foninis apšvietimas"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"%1$d lygis iš %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Namų sistemos valdymas"</string> diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml index 6a5d0681d7df..8d0b43c9a925 100644 --- a/packages/SystemUI/res/values-lv/strings.xml +++ b/packages/SystemUI/res/values-lv/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automātiski atkal ieslēgt rīt"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Tādas funkcijas kā “Ātrā kopīgošana” un “Atrast ierīci” izmanto Bluetooth savienojumu"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth savienojums tiks ieslēgts rīt no rīta"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Kopīgot audio"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Notiek audio kopīgošana"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Akumulators: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Austiņas"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Kuras ierīces funkcijas tika ietekmētas?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Atlasiet problēmas veidu"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Ekrāna ierakstīšana"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"Veiktspēja"</string> + <string name="user_interface" msgid="3712869377953950887">"Lietotāja saskarne"</string> + <string name="thermal" msgid="6758074791325414831">"Ierīces temperatūra"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Vienas rokas režīms"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Dzirdes aparāti"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktīvs"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Atvienots"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Dzirdes aparāti"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Savienot pārī jaunu ierīci"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Noklikšķiniet, lai savienotu pārī jaunu ierīci"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"aizvērta"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"atvērta"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Skārienekrāna pildspalvas akumulatora uzlādes līmenis: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Pievienojiet skārienekrāna pildspalvu lādētājam"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Zems skārienekrāna pildspalvas akumulatora līmenis"</string> <string name="video_camera" msgid="7654002575156149298">"Videokamera"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Meklēšanas saīsnes"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Sakļaušanas ikona"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Izvēršanas ikona"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"vai"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Tastatūras fona apgaismojums"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Līmenis numur %1$d, kopā ir %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Mājas kontrolierīces"</string> diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml index 7c410965bfa2..1c889e6b6b68 100644 --- a/packages/SystemUI/res/values-mk/strings.xml +++ b/packages/SystemUI/res/values-mk/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Автоматски вклучи повторно утре"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Функциите како „Брзо споделување“ и „Најди го мојот уред“ користат Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth ќе се вклучи утре наутро"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Споделување аудио"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Се споделува аудио"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Батерија: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудио"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Слушалки"</string> @@ -301,7 +303,7 @@ <string name="quick_settings_user_title" msgid="8673045967216204537">"Корисник"</string> <string name="quick_settings_wifi_label" msgid="2879507532983487244">"Wi-Fi"</string> <string name="quick_settings_internet_label" msgid="6603068555872455463">"Интернет"</string> - <string name="quick_settings_networks_available" msgid="1875138606855420438">"Мрежите се достапни"</string> + <string name="quick_settings_networks_available" msgid="1875138606855420438">"Достапни се мрежи"</string> <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"Не се достапни мрежи"</string> <string name="quick_settings_wifi_detail_empty_text" msgid="483130889414601732">"Нема достапни Wi-Fi мрежи"</string> <string name="quick_settings_wifi_secondary_label_transient" msgid="7501659015509357887">"Се вклучува…"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Кој дел од доживувањето на уредот беше засегнат?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Изберете тип проблем"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Снимање екран"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"Изведба"</string> + <string name="user_interface" msgid="3712869377953950887">"Кориснички интерфејс"</string> + <string name="thermal" msgid="6758074791325414831">"Термално"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Режим со една рака"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Слушни апарати"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Активно"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Не е поврзано"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Слушни апарати"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Спари нов уред"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Кликнете за да спарите нов уред"</string> @@ -689,8 +686,8 @@ <string name="notification_automatic_title" msgid="3745465364578762652">"Автоматски"</string> <string name="notification_channel_summary_low" msgid="4860617986908931158">"Без звук или вибрации"</string> <string name="notification_conversation_summary_low" msgid="1734433426085468009">"Без звук или вибрации и се појавува подолу во делот со разговори"</string> - <string name="notification_channel_summary_default" msgid="777294388712200605">"Може да ѕвони или вибрира во зависност од поставките за уредот"</string> - <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Може да ѕвони или вибрира во зависност од поставките на уредот. Стандардно, разговорите од <xliff:g id="APP_NAME">%1$s</xliff:g> се во балончиња."</string> + <string name="notification_channel_summary_default" msgid="777294388712200605">"Може да ѕвони или да вибрира во зависност од поставките за уредот"</string> + <string name="notification_channel_summary_default_with_bubbles" msgid="3482483084451555344">"Може да ѕвони или да вибрира во зависност од поставките за уредот. Стандардно, разговорите од <xliff:g id="APP_NAME">%1$s</xliff:g> се во балончиња."</string> <string name="notification_channel_summary_automatic" msgid="5813109268050235275">"Дозволете системот да определи дали известувањево треба да испушти звук или да вибрира"</string> <string name="notification_channel_summary_automatic_alerted" msgid="954166812246932240">"<b>Статус:</b> поставено на „Стандардно“"</string> <string name="notification_channel_summary_automatic_silenced" msgid="7403004439649872047">"<b>Статус:</b> намалено на „Тивко“"</string> @@ -1210,7 +1207,7 @@ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{Активна е # апликација}one{Активни се # апликација}other{Активни се # апликации}}"</string> <string name="fgs_dot_content_description" msgid="2865071539464777240">"Нови информации"</string> <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Активни апликации"</string> - <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Овие апликации се активни и работат, дури и кога не ги користите. Ова ја подобрува нивната функционалност, но може да влијае и на траењето на батеријата."</string> + <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Овие апликации се активни и работат дури и кога не ги користите. Ова ја подобрува нивната функционалност, но може и да влијае на траењето на батеријата."</string> <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Запри"</string> <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Запрено"</string> <string name="clipboard_edit_text_done" msgid="4551887727694022409">"Готово"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"затворен"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"отворен"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Батерија на пенкало: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Поврзете го пенкалото со полнач"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Слаба батерија на пенкало"</string> <string name="video_camera" msgid="7654002575156149298">"Видеокамера"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Кратенки за пребарување"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Икона за собирање"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Икона за проширување"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"или"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Осветлување на тастатура"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Ниво %1$d од %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Контроли за домот"</string> diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml index 540358c359d7..3dd91f7cfd2b 100644 --- a/packages/SystemUI/res/values-ml/strings.xml +++ b/packages/SystemUI/res/values-ml/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"നാളെ വീണ്ടും സ്വയമേവ ഓണാക്കുക"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"ക്വിക്ക് ഷെയർ, Find My Device പോലുള്ള ഫീച്ചറുകൾ Bluetooth ഉപയോഗിക്കുന്നു"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth നാളെ രാവിലെ ഓണാകും"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"ഓഡിയോ പങ്കിടൽ"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"ഓഡിയോ പങ്കിടുന്നു"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ബാറ്ററി"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ഓഡിയോ"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ഹെഡ്സെറ്റ്"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"നിങ്ങളുടെ ഉപകരണ അനുഭവത്തിന്റെ ഏത് ഭാഗമാണ് ബാധിച്ചത്?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"പ്രശ്ന തരം തിരഞ്ഞെടുക്കുക"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"സ്ക്രീൻ റെക്കോർഡ്"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"പ്രകടനം"</string> + <string name="user_interface" msgid="3712869377953950887">"ഉപയോക്തൃ ഇന്റർഫേസ്"</string> + <string name="thermal" msgid="6758074791325414831">"തെർമൽ"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"ഒറ്റക്കൈ മോഡ്"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"കേൾവിക്കുള്ള ഉപകരണങ്ങൾ"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"സജീവം"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"വിച്ഛേദിച്ചിരിക്കുന്നു"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"കേൾവിക്കുള്ള ഉപകരണങ്ങൾ"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"പുതിയ ഉപകരണം ജോടിയാക്കുക"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"പുതിയ ഉപകരണം ജോടിയാക്കാൻ ക്ലിക്ക് ചെയ്യുക"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"ഫോൾഡ് ചെയ്തത്"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"അൺഫോൾഡ് ചെയ്തത്"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"സ്റ്റൈലസ് ബാറ്ററി <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"നിങ്ങളുടെ സ്റ്റൈലസ് ചാർജറുമായി കണക്റ്റ് ചെയ്യുക"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"സ്റ്റൈലസിന്റെ ബാറ്ററി ചാർജ് കുറവാണ്"</string> <string name="video_camera" msgid="7654002575156149298">"വീഡിയോ ക്യാമറ"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"തിരയൽ കുറുക്കുവഴികൾ"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"ചുരുക്കൽ ഐക്കൺ"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"വികസിപ്പിക്കൽ ഐക്കൺ"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"അല്ലെങ്കിൽ"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"കീബോഡ് ബാക്ക്ലൈറ്റ്"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d-ൽ %1$d-ാമത്തെ ലെവൽ"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"ഹോം കൺട്രോളുകൾ"</string> diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml index 47e6936ea5f1..553926383476 100644 --- a/packages/SystemUI/res/values-mn/strings.xml +++ b/packages/SystemUI/res/values-mn/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Маргааш автоматаар дахин асаах"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Түргэн хуваалцах, Миний төхөөрөмжийг олох зэрэг онцлогууд Bluetooth-г ашигладаг"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth-г маргааш өглөө асаана"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Аудио хуваалцах"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Аудио хуваалцаж байна"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> батарей"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудио"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Чихэвч"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Таны төхөөрөмжийн хэрэглээний аль хэсэгт нөлөөлсөн бэ?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Асуудлын төрөл сонгоно уу"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Дэлгэцийн бичлэг"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"Гүйцэтгэл"</string> + <string name="user_interface" msgid="3712869377953950887">"Хэрэглэгчийн интерфейс"</string> + <string name="thermal" msgid="6758074791325414831">"Дулааны"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Нэг гарын горим"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Сонсголын төхөөрөмжүүд"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Идэвхтэй"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Салсан"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Сонсголын төхөөрөмжүүд"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Шинэ төхөөрөмж хослуулах"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Шинэ төхөөрөмж хослуулахын тулд товшино уу"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"эвхсэн"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"дэлгэсэн"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Мэдрэгч үзгийн батарей <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Мэдрэгч үзгээ цэнэглэгчтэй холбоорой"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Мэдрэгч үзэгний батарей бага байна"</string> <string name="video_camera" msgid="7654002575156149298">"Видео камер"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Товчлолууд хайх"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Хураах дүрс тэмдэг"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Дэлгэх дүрс тэмдэг"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"эсвэл"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Гарын арын гэрэл"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d-с %1$d-р түвшин"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Гэрийн удирдлага"</string> diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml index 39f14133eb2b..aea4d5a4cf50 100644 --- a/packages/SystemUI/res/values-mr/strings.xml +++ b/packages/SystemUI/res/values-mr/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"उद्या पुन्हा आपोआप सुरू करा"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"क्विक शेअर आणि Find My Device यांसारखी वैशिष्ट्ये ब्लूटूथ वापरतात"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"ब्लूटूथ उद्या सकाळी सुरू होईल"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"ऑडिओ शेअरिंग"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"ऑडिओ शेअर करत आहे"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> बॅटरी"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ऑडिओ"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"हेडसेट"</string> @@ -366,10 +368,8 @@ <string name="thermal" msgid="6758074791325414831">"थर्मल"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"एकहाती मोड"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"श्रवणयंत्रे"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"ॲक्टिव्ह आहे"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"डिस्कनेक्ट केले आहे"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"श्रवणयंत्रे"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"नवीन डिव्हाइस पेअर करा"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"नवीन डिव्हाइस पेअर करण्यासाठी क्लिक करा"</string> @@ -1324,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"शोधण्यासाठी शॉर्टकट"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"कोलॅप्स करा आयकन"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"विस्तार करा आयकन"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"किंवा"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"कीबोर्ड बॅकलाइट"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d पैकी %1$d पातळी"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"होम कंट्रोल"</string> diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml index 8fce993b6d74..01310982f331 100644 --- a/packages/SystemUI/res/values-ms/strings.xml +++ b/packages/SystemUI/res/values-ms/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Dihidupkan lagi esok secara automatik"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Ciri seperti Quick Share dan Find My Device menggunakan Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth akan dihidupkan esok pagi"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Perkongsian Audio"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Berkongsi Audio"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> bateri"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Set Kepala"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Pengalaman peranti yang manakah yang terjejas?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Pilih jenis masalah"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Rakam skrin"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"Prestasi"</string> + <string name="user_interface" msgid="3712869377953950887">"Antara Muka Pengguna"</string> + <string name="thermal" msgid="6758074791325414831">"Terma"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Mod sebelah tangan"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Peranti pendengaran"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktif"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Diputuskan sambungan"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Peranti pendengaran"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Gandingkan peranti baharu"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Klik untuk menggandingkan peranti baharu"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"terlipat"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"tidak terlipat"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Bateri stilus <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Sambungkan stilus anda kepada pengecas"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Bateri stilus lemah"</string> <string name="video_camera" msgid="7654002575156149298">"Kamera video"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Pintasan carian"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Kuncupkan ikon"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Kembangkan ikon"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"atau"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Cahaya latar papan kekunci"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Tahap %1$d daripada %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Kawalan Rumah"</string> diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml index 926426120720..9b19b62c73db 100644 --- a/packages/SystemUI/res/values-my/strings.xml +++ b/packages/SystemUI/res/values-my/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"မနက်ဖြန် အလိုအလျောက် ပြန်ဖွင့်ရန်"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"‘အမြန် မျှဝေပါ’ နှင့် Find My Device ကဲ့သို့ တူးလ်များသည် ဘလူးတုသ်သုံးသည်"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"မနက်ဖြန်နံနက်တွင် ဘလူးတုသ် ပွင့်ပါမည်"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"အော်ဒီယို မျှဝေခြင်း"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"အော်ဒီယို မျှဝေနေသည်"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ဘက်ထရီ"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"အသံ"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"မိုက်ခွက်ပါနားကြပ်"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"စက်အသုံးပြုမှု၏ မည်သည့်အပိုင်းကို သက်ရောက်သလဲ။"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"ပြဿနာအမျိုးအစား ရွေးရန်"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"ဖန်သားပြင်ရိုက်ကူးရန်"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"စွမ်းဆောင်ရည်"</string> + <string name="user_interface" msgid="3712869377953950887">"သုံးသူအတွက် ကြားခံစနစ်"</string> + <string name="thermal" msgid="6758074791325414831">"အပူဓာတ်"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"လက်တစ်ဖက်သုံးမုဒ်"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"နားကြားကိရိယာ"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"သုံးနေသည်"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"ချိတ်ဆက်မထားပါ"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"နားကြားကိရိယာ"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"စက်အသစ်တွဲချိတ်ရန်"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"စက်အသစ် တွဲချိတ်ရန် နှိပ်ပါ"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"ခေါက်ထားသည်"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"ဖြန့်ထားသည်"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"စတိုင်လပ်စ် ဘက်ထရီ <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"စတိုင်လပ်စ်ကို အားသွင်းကိရိယာနှင့် ချိတ်ဆက်ခြင်း"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"စတိုင်လပ်စ် ဘက်ထရီ အားနည်းနေသည်"</string> <string name="video_camera" msgid="7654002575156149298">"ဗီဒီယိုကင်မရာ"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ရှာဖွေစာလုံး ဖြတ်လမ်း"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"လျှော့ပြရန် သင်္ကေတ"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ပိုပြရန် သင်္ကေတ"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"သို့မဟုတ်"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"ကီးဘုတ်နောက်မီး"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"အဆင့် %2$d အနက် %1$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"အိမ်ထိန်းချုပ်မှုများ"</string> diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml index 9c2193ef1a13..a8befb36d6c8 100644 --- a/packages/SystemUI/res/values-nb/strings.xml +++ b/packages/SystemUI/res/values-nb/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Slå på igjen i morgen automatisk"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Funksjoner som Quick Share og Finn enheten min bruker Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth slås på i morgen tidlig"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Lyddeling"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Deler lyd"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> batteri"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Lyd"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Hodetelefoner"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Hvilken del av enhetsopplevelsen din ble påvirket?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Velg problemtype"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Skjermopptak"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"Ytelse"</string> + <string name="user_interface" msgid="3712869377953950887">"Brukergrensesnitt"</string> + <string name="thermal" msgid="6758074791325414831">"Termisk"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Enhåndsmodus"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Høreapparater"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktiv"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Frakoblet"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Høreapparater"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Koble til en ny enhet"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Klikk for å koble til en ny enhet"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"lagt sammen"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"åpen"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Batteri i pekepennen: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Koble pekepennen til en lader"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Det er lite batteri i pekepennen"</string> <string name="video_camera" msgid="7654002575156149298">"Videokamera"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Snarveier til søk"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Skjul-ikon"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Vis-ikon"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"eller"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Bakgrunnslys for tastatur"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Nivå %1$d av %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Hjemkontroller"</string> diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml index 94b197b3f238..c26ea3e039dd 100644 --- a/packages/SystemUI/res/values-ne/strings.xml +++ b/packages/SystemUI/res/values-ne/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"भोलि फेरि स्वतः अन गर्नुहोस्"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"क्विक सेयर र Find My Device जस्ता सुविधाहरू प्रयोग गर्न ब्लुटुथ चाहिन्छ"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"ब्लुटुथ भोलि बिहान अन हुने छ"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"अडियो सेयरिङ"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"अडियो सेयर गरिँदै छ"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ब्याट्री"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"अडियो"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"हेडसेट"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"तपाईंको डिभाइसको कुन चाहिँ सुविधा प्रभावित भएको छ?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"समस्याको प्रकार चयन गर्नुहोस्"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"स्क्रिन रेकर्ड"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"पर्फर्मेन्स"</string> + <string name="user_interface" msgid="3712869377953950887">"युजर इन्टरफेस"</string> + <string name="thermal" msgid="6758074791325414831">"थर्मल"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"एक हाते मोड"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"हियरिङ डिभाइसहरू"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"सक्रिय छ"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"डिस्कनेक्ट गरिएको छ"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"हियरिङ डिभाइसहरू"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"नयाँ डिभाइस कनेक्ट गर्नुहोस्"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"नयाँ डिभाइसमा कनेक्ट गर्न क्लिक गर्नुहोस्"</string> @@ -382,9 +379,9 @@ <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"डिभाइसको माइक्रोफोन अनब्लक गर्ने हो?"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"डिभाइसको क्यामेरा अनब्लक गर्ने हो?"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"डिभाइसको क्यामेरा र माइक्रोफोन अनब्लक गर्ने हो?"</string> - <string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"यसो गर्नुभयो भने माइक्रोफोन प्रयोग गर्ने अनुमति दिइएका सबै एप तथा सेवाहरूका लागि सो अनुमति अनब्लक गरिन्छ।"</string> - <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"यसो गर्नुभयो भने क्यामेरा प्रयोग गर्ने अनुमति दिइएका सबै एप तथा सेवाहरूका लागि सो अनुमति अनब्लक गरिन्छ।"</string> - <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"यसो गर्नुभयो भने क्यामेरा वा माइक्रोफोन प्रयोग गर्ने अनुमति दिइएका सबै एप तथा सेवाहरूका लागि सो अनुमति अनब्लक गरिन्छ।"</string> + <string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"यसो गर्नुभयो भने माइक्रोफोन प्रयोग गर्ने अनुमति दिइएका सबै एप तथा सेवाको हकमा यो अनुमति अनब्लक गरिन्छ।"</string> + <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"यसो गर्नुभयो भने क्यामेरा प्रयोग गर्ने अनुमति दिइएका सबै एप तथा सेवाको हकमा यो अनुमति अनब्लक गरिन्छ।"</string> + <string name="sensor_privacy_start_use_mic_camera_dialog_content" msgid="3577642558418404919">"यसो गर्नुभयो भने क्यामेरा वा माइक्रोफोन प्रयोग गर्ने अनुमति दिइएका सबै एप तथा सेवाको हकमा यो अनुमति अनब्लक गरिन्छ।"</string> <string name="sensor_privacy_start_use_mic_blocked_dialog_title" msgid="2640140287496469689">"माइक्रोफोन ब्लक गरिएको छ"</string> <string name="sensor_privacy_start_use_camera_blocked_dialog_title" msgid="7398084286822440384">"क्यामेरा ब्लक गरिएको छ"</string> <string name="sensor_privacy_start_use_mic_camera_blocked_dialog_title" msgid="195236134743281973">"माइक र क्यामेरा ब्लक गरिएको छ"</string> @@ -932,7 +929,7 @@ <string name="qs_dnd_prompt_auto_rule_app" msgid="1841469944118486580">"कुनै स्वचालित नियम वा एपले बाधा नपुऱ्याउनुहोस् नामक विकल्पलाई सक्रिय गऱ्यो।"</string> <string name="running_foreground_services_title" msgid="5137313173431186685">"पृष्ठभूमिमा चल्ने एपहरू"</string> <string name="running_foreground_services_msg" msgid="3009459259222695385">"ब्याट्री र डेटाका प्रयोग सम्बन्धी विवरणहरूका लागि ट्याप गर्नुहोस्"</string> - <string name="mobile_data_disable_title" msgid="5366476131671617790">"मोबाइल डेटा निष्क्रिय पार्ने हो?"</string> + <string name="mobile_data_disable_title" msgid="5366476131671617790">"मोबाइल डेटा अफ गर्ने हो?"</string> <string name="mobile_data_disable_message" msgid="8604966027899770415">"तपाईं <xliff:g id="CARRIER">%s</xliff:g> मार्फत डेटा वा इन्टरनेट प्रयोग गर्न सक्नुहुने छैन। Wi-Fi मार्फत मात्र इन्टरनेट उपलब्ध हुने छ।"</string> <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"तपाईंको सेवा प्रदायक"</string> <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"फेरि <xliff:g id="CARRIER">%s</xliff:g> को मोबाइल डेटा अन गर्ने हो?"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"फोल्ड गरिएको"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"अनफोल्ड गरिएको"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"स्टाइलसको ब्याट्री <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"आफ्नो स्टाइलस चार्जरमा कनेक्ट गर्नुहोस्"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"स्टाइलसको ब्याट्री लो छ"</string> <string name="video_camera" msgid="7654002575156149298">"भिडियो क्यामेरा"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"खोजका सर्टकटहरू"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"\"कोल्याप्स गर्नुहोस्\" आइकन"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"\"एक्स्पान्ड गर्नुहोस्\" आइकन"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"वा"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"किबोर्ड ब्याकलाइट"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d मध्ये %1$d औँ स्तर"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"होम कन्ट्रोलहरू"</string> diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml index 8cdbf6139a7d..3eba8a8a1324 100644 --- a/packages/SystemUI/res/values-nl/strings.xml +++ b/packages/SystemUI/res/values-nl/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Morgen weer automatisch aanzetten"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Functies zoals Quick Share en Vind mijn apparaat gebruiken bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth wordt morgenochtend aangezet"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Audio delen"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Audio delen…"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> batterijniveau"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string> @@ -366,10 +368,8 @@ <string name="thermal" msgid="6758074791325414831">"Thermisch"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Bediening met 1 hand"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Hoortoestellen"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Actief"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Ontkoppeld"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Hoortoestellen"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Nieuw apparaat koppelen"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Klik om nieuw apparaat te koppelen"</string> @@ -1324,10 +1324,9 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Snelkoppelingen voor zoekopdrachten"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Icoon voor samenvouwen"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Icoon voor uitvouwen"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"of"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Achtergrondverlichting van toetsenbord"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Niveau %1$d van %2$d"</string> - <string name="home_controls_dream_label" msgid="6567105701292324257">"Huisbediening"</string> - <string name="home_controls_dream_description" msgid="4644150952104035789">"Snel toegang tot je huisbediening als screensaver"</string> + <string name="home_controls_dream_label" msgid="6567105701292324257">"Bediening voor in huis"</string> + <string name="home_controls_dream_description" msgid="4644150952104035789">"Gebruik bediening voor in huis als screensaver"</string> </resources> diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml index 287d1a71dd04..1e3ebab86b77 100644 --- a/packages/SystemUI/res/values-or/strings.xml +++ b/packages/SystemUI/res/values-or/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"ଆସନ୍ତାକାଲି ସ୍ୱତଃ ପୁଣି ଚାଲୁ ହେବ"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Quick Share ଏବଂ Find My Device ପରି ଫିଚରଗୁଡ଼ିକ ବ୍ଲୁଟୁଥ ବ୍ୟବହାର କରେ"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"ବ୍ଲୁଟୁଥ ଆସନ୍ତା କାଲି ସକାଳେ ଚାଲୁ ହେବ"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"ଅଡିଓ ସେୟାରିଂ"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"ଅଡିଓ ସେୟାର କରାଯାଉଛି"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ବ୍ୟାଟେରୀ"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ଅଡିଓ"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ହେଡସେଟ୍"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"ଆପଣଙ୍କ ଡିଭାଇସ ଅନୁଭୂତିର କେଉଁ ଅଂଶ ପ୍ରଭାବିତ ହୋଇଛି?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"ସମସ୍ୟାର ପ୍ରକାର ଚୟନ କରନ୍ତୁ"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"ସ୍କ୍ରିନ ରେକର୍ଡ"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"ପରଫରମାନ୍ସ"</string> + <string name="user_interface" msgid="3712869377953950887">"ୟୁଜର ଇଣ୍ଟରଫେସ"</string> + <string name="thermal" msgid="6758074791325414831">"ଥର୍ମାଲ"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"ଏକ-ହାତ ମୋଡ"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"ଶ୍ରବଣ ଡିଭାଇସଗୁଡ଼ିକ"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"ସକ୍ରିୟ"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"ଡିସକନେକ୍ଟ ହୋଇଛି"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"ଶ୍ରବଣ ଡିଭାଇସଗୁଡ଼ିକ"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"ନୂଆ ଡିଭାଇସ ପେୟାର କରନ୍ତୁ"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"ନୂଆ ଡିଭାଇସ ପେୟାର କରିବାକୁ କ୍ଲିକ କରନ୍ତୁ"</string> @@ -933,7 +930,7 @@ <string name="running_foreground_services_title" msgid="5137313173431186685">"ବ୍ୟାକଗ୍ରାଉଣ୍ଡରେ ଆପ୍ ଚାଲୁଛି"</string> <string name="running_foreground_services_msg" msgid="3009459259222695385">"ବ୍ୟାଟେରୀ ଏବଂ ଡାଟା ବ୍ୟବହାର ଉପରେ ବିବରଣୀ ପାଇଁ ଟାପ୍ କରନ୍ତୁ"</string> <string name="mobile_data_disable_title" msgid="5366476131671617790">"ମୋବାଇଲ୍ ଡାଟା ବନ୍ଦ କରିବେ?"</string> - <string name="mobile_data_disable_message" msgid="8604966027899770415">"ଡାଟା କିମ୍ବା ଇଣ୍ଟରନେଟ୍କୁ <xliff:g id="CARRIER">%s</xliff:g> ଦ୍ଵାରା ଆପଣଙ୍କର ଆକ୍ସେସ୍ ରହିବ ନାହିଁ। ଇଣ୍ଟରନେଟ୍ କେବଳ ୱାଇ-ଫାଇ ମାଧ୍ୟମରେ ଉପଲବ୍ଧ ହେବ।"</string> + <string name="mobile_data_disable_message" msgid="8604966027899770415">"ଡାଟା କିମ୍ବା ଇଣ୍ଟର୍ନେଟକୁ <xliff:g id="CARRIER">%s</xliff:g> ଦ୍ଵାରା ଆପଣଙ୍କର ଆକ୍ସେସ ରହିବ ନାହିଁ। ଇଣ୍ଟର୍ନେଟ କେବଳ ୱାଇ-ଫାଇ ମାଧ୍ୟମରେ ଉପଲବ୍ଧ ହେବ।"</string> <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"ଆପଣଙ୍କ କେରିଅର୍"</string> <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"<xliff:g id="CARRIER">%s</xliff:g>କୁ ପୁଣି ସ୍ୱିଚ କରିବେ?"</string> <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"ଉପଲବ୍ଧତା ଆଧାରରେ ମୋବାଇଲ ଡାଟା ସ୍ୱତଃ ସ୍ୱିଚ ହେବ ନାହିଁ"</string> @@ -1210,7 +1207,7 @@ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{#ଟି ଆପ ସକ୍ରିୟ ଅଛି}other{#ଟି ଆପ ସକ୍ରିୟ ଅଛି}}"</string> <string name="fgs_dot_content_description" msgid="2865071539464777240">"ନୂଆ ସୂଚନା"</string> <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"ସକ୍ରିୟ ଆପ୍ସ"</string> - <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"ଆପଣ ଏହି ଆପ୍ସକୁ ବ୍ୟବହାର କରୁନଥିଲେ ମଧ୍ୟ ସେଗୁଡ଼ିକ ସକ୍ରିୟ ରହିଥାଏ ଏବଂ ଚାଲୁଥାଏ। ଏହା ସେଗୁଡ଼ିକର କାର୍ଯ୍ୟକ୍ଷମତାକୁ ଉନ୍ନତ କରେ, କିନ୍ତୁ ଏହା ମଧ୍ୟ ବ୍ୟାଟେରୀ ଲାଇଫକୁ ପ୍ରଭାବିତ କରିପାରେ।"</string> + <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"ଆପଣ ଏହି ଆପ୍ସକୁ ବ୍ୟବହାର କରୁନଥିଲେ ମଧ୍ୟ ସେଗୁଡ଼ିକ ସକ୍ରିୟ ରହିଥାଏ ଏବଂ ଚାଲୁଥାଏ। ଏହା ସେଗୁଡ଼ିକର କାର୍ଯ୍ୟକ୍ଷମତାକୁ ଉନ୍ନତ କରେ, କିନ୍ତୁ ଏହା ମଧ୍ୟ ବେଟେରୀ ଲାଇଫକୁ ପ୍ରଭାବିତ କରିପାରେ।"</string> <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"ବନ୍ଦ କରନ୍ତୁ"</string> <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"ବନ୍ଦ ହୋଇଛି"</string> <string name="clipboard_edit_text_done" msgid="4551887727694022409">"ହୋଇଗଲା"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"ଫୋଲ୍ଡେଡ"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"ଅନଫୋଲ୍ଡେଡ"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"ଷ୍ଟାଇଲସ ବେଟେରୀ <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"ଏକ ଚାର୍ଜର ସହ ଆପଣଙ୍କ ଷ୍ଟାଇଲସକୁ କନେକ୍ଟ କରନ୍ତୁ"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"ଷ୍ଟାଇଲସ ବେଟେରୀର ଚାର୍ଜ କମ ଅଛି"</string> <string name="video_camera" msgid="7654002575156149298">"ଭିଡିଓ କେମେରା"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ସର୍ଚ୍ଚ ସର୍ଟକଟ"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"ଆଇକନକୁ ସଙ୍କୁଚିତ କରନ୍ତୁ"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ଆଇକନକୁ ବିସ୍ତାର କରନ୍ତୁ"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"କିମ୍ବା"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"କୀବୋର୍ଡ ବେକଲାଇଟ"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$dରୁ %1$d ନମ୍ବର ଲେଭେଲ"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"ହୋମ କଣ୍ଟ୍ରୋଲ୍ସ"</string> diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml index a16a8a5cbc8c..dddda72d53e3 100644 --- a/packages/SystemUI/res/values-pa/strings.xml +++ b/packages/SystemUI/res/values-pa/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"ਕੱਲ੍ਹ ਨੂੰ ਆਪਣੇ ਆਪ ਚਾਲੂ ਹੋ ਜਾਵੇਗਾ"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"ਕਵਿੱਕ ਸ਼ੇਅਰ ਅਤੇ Find My Device ਵਰਗੀਆਂ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਬਲੂਟੁੱਥ ਵਰਤਦੀਆਂ ਹਨ"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"ਬਲੂਟੁੱਥ ਕੱਲ੍ਹ ਸਵੇਰੇ ਚਾਲੂ ਹੋ ਜਾਵੇਗਾ"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"ਆਡੀਓ ਸਾਂਝਾਕਰਨ"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"ਆਡੀਓ ਨੂੰ ਸਾਂਝਾ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ਬੈਟਰੀ"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ਆਡੀਓ"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ਹੈੱਡਸੈੱਟ"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"ਤੁਹਾਡੇ ਡੀਵਾਈਸ ਦੀ ਕਿਹੜੀ ਸੁਵਿਧਾ ਪ੍ਰਭਾਵਿਤ ਹੋਈ ਸੀ?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"ਸਮੱਸਿਆ ਦੀ ਕਿਸਮ ਚੁਣੋ"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"ਸਕ੍ਰੀਨ ਰਿਕਾਰਡ"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"ਕਾਰਗੁਜ਼ਾਰੀ"</string> + <string name="user_interface" msgid="3712869377953950887">"ਯੂਜ਼ਰ ਇੰਟਰਫ਼ੇਸ"</string> + <string name="thermal" msgid="6758074791325414831">"ਥਰਮਲ"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"ਇੱਕ ਹੱਥ ਮੋਡ"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"ਸੁਣਨ ਵਾਲੇ ਡੀਵਾਈਸ"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"ਕਿਰਿਆਸ਼ੀਲ"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"ਡਿਸਕਨੈਕਟ ਹੈ"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"ਸੁਣਨ ਵਾਲੇ ਡੀਵਾਈਸ"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"ਨਵਾਂ ਡੀਵਾਈਸ ਜੋੜਾਬੱਧ ਕਰੋ"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"\'ਨਵਾਂ ਡੀਵਾਈਸ ਜੋੜਾਬੱਧ ਕਰੋ\' \'ਤੇ ਕਲਿੱਕ ਕਰੋ"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"ਫੋਲਡਯੋਗ ਡੀਵਾਈਸ"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"ਅਣਫੋਲਡਯੋਗ ਡੀਵਾਈਸ"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"ਸਟਾਈਲਸ ਦੀ ਬੈਟਰੀ <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"ਆਪਣੇ ਸਟਾਈਲਸ ਨੂੰ ਚਾਰਜਰ ਨਾਲ ਕਨੈਕਟ ਕਰੋ"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"ਸਟਾਈਲਸ ਦੀ ਬੈਟਰੀ ਘੱਟ ਹੈ"</string> <string name="video_camera" msgid="7654002575156149298">"ਵੀਡੀਓ ਕੈਮਰਾ"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ਖੋਜ ਸੰਬੰਧੀ ਸ਼ਾਰਟਕੱਟ"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"ਪ੍ਰਤੀਕ ਨੂੰ ਸਮੇਟੋ"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ਪ੍ਰਤੀਕ ਦਾ ਵਿਸਤਾਰ ਕਰੋ"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ਜਾਂ"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"ਕੀ-ਬੋਰਡ ਬੈਕਲਾਈਟ"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d ਵਿੱਚੋਂ %1$d ਪੱਧਰ"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"ਹੋਮ ਕੰਟਰੋਲ"</string> diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml index 58561f112456..2dbcf2028c50 100644 --- a/packages/SystemUI/res/values-pl/strings.xml +++ b/packages/SystemUI/res/values-pl/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatycznie włącz ponownie jutro"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Bluetootha używają funkcje takie jak szybkie udostępnianie czy Znajdź moje urządzenie"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth włączy się jutro rano"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Udostępnianie dźwięku"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Udostępniam dźwięk"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> naładowania baterii"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Dźwięk"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Zestaw słuchawkowy"</string> @@ -366,10 +368,8 @@ <string name="thermal" msgid="6758074791325414831">"Termografia"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Tryb jednej ręki"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Urządzenia słuchowe"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktywny"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Rozłączono"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Urządzenia słuchowe"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Sparuj nowe urządzenie"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kliknij, aby sparować nowe urządzenie"</string> @@ -1324,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Skróty do wyszukiwania"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona zwijania"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona rozwijania"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"lub"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Podświetlenie klawiatury"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Poziom %1$d z %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Sterowanie domem"</string> diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml index 8f5c5192133c..992db40d04a6 100644 --- a/packages/SystemUI/res/values-pt-rBR/strings.xml +++ b/packages/SystemUI/res/values-pt-rBR/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Ativar automaticamente de novo amanhã"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Recursos como o Quick Share e o Encontre Meu Dispositivo usam Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"O Bluetooth será ativado amanhã de manhã"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Compartilhamento de áudio"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Compartilhando áudio"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Bateria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Áudio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Fone de ouvido"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Que parte da sua experiência no dispositivo foi afetada?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Selecionar tipo de problema"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Gravação de tela"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"Desempenho"</string> + <string name="user_interface" msgid="3712869377953950887">"Interface do usuário"</string> + <string name="thermal" msgid="6758074791325414831">"Térmico"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modo uma mão"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Aparelhos auditivos"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Ativos"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Desconectados"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Aparelhos auditivos"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Parear novo dispositivo"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Clique para parear o novo dispositivo"</string> @@ -639,7 +636,7 @@ <string name="volume_panel_hint_muted" msgid="1124844870181285320">"som desativado"</string> <string name="volume_panel_hint_vibrate" msgid="4136223145435914132">"vibrar"</string> <string name="media_output_label_title" msgid="872824698593182505">"Tocando <xliff:g id="LABEL">%s</xliff:g> em"</string> - <string name="media_output_title_without_playing" msgid="3825663683169305013">"Onde o áudio vai tocar?"</string> + <string name="media_output_title_without_playing" msgid="3825663683169305013">"Áudio definido para"</string> <string name="media_output_title_ongoing_call" msgid="208426888064112006">"Ligando"</string> <string name="system_ui_tuner" msgid="1471348823289954729">"Sintonizador System UI"</string> <string name="status_bar" msgid="4357390266055077437">"Barra de status"</string> @@ -1210,7 +1207,7 @@ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# app está ativo}one{# apps está ativo}many{# de apps estão ativos}other{# apps estão ativos}}"</string> <string name="fgs_dot_content_description" msgid="2865071539464777240">"Nova informação"</string> <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Apps ativos"</string> - <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Esses apps ficam ativos e em execução mesmo quando não estão em uso. Isso melhora a funcionalidade deles, mas também pode afetar a duração da bateria."</string> + <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Esses apps ficam ativos e em execução mesmo quando não estão em uso. Isso melhora a funcionalidade deles, mas também afeta a duração da bateria."</string> <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Parar"</string> <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Parado"</string> <string name="clipboard_edit_text_done" msgid="4551887727694022409">"Concluído"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"fechado"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"aberto"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Bateria da stylus em <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Conecte sua stylus a um carregador"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Bateria da stylus fraca"</string> <string name="video_camera" msgid="7654002575156149298">"Filmadora"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Atalhos de pesquisa"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ícone \"Fechar\""</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ícone \"Abrir\""</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ou"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Luz de fundo do teclado"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Nível %1$d de %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Automação residencial"</string> diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml index df735727b893..fb0bbfa88850 100644 --- a/packages/SystemUI/res/values-pt-rPT/strings.xml +++ b/packages/SystemUI/res/values-pt-rPT/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Reativar amanhã automaticamente"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Funcionalidades como a Partilha rápida e o serviço Localizar o meu dispositivo usam o Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"O Bluetooth vai ser ativado amanhã de manhã"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Partilha de áudio"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"A partilhar áudio"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> de bateria"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Áudio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Ausc. c/ mic. integ."</string> @@ -366,10 +368,8 @@ <string name="thermal" msgid="6758074791325414831">"Térmico"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modo para uma mão"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Dispositivos auditivos"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Ativos"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Desligados"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Dispositivos auditivos"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Sincronizar novo dispositivo"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Clique para sincronizar um novo dispositivo"</string> @@ -930,7 +930,7 @@ <string name="running_foreground_services_title" msgid="5137313173431186685">"Apps em execução em segundo plano"</string> <string name="running_foreground_services_msg" msgid="3009459259222695385">"Toque para obter detalhes acerca da utilização da bateria e dos dados"</string> <string name="mobile_data_disable_title" msgid="5366476131671617790">"Desativar os dados móveis?"</string> - <string name="mobile_data_disable_message" msgid="8604966027899770415">"Não terá acesso a dados ou à Internet através do operador <xliff:g id="CARRIER">%s</xliff:g>. A Internet estará disponível apenas por Wi-Fi."</string> + <string name="mobile_data_disable_message" msgid="8604966027899770415">"Não vai ter acesso aos dados nem à Internet através do operador <xliff:g id="CARRIER">%s</xliff:g>. A Internet vai estar disponível só por Wi-Fi."</string> <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"o seu operador"</string> <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"Mudar de novo para <xliff:g id="CARRIER">%s</xliff:g>?"</string> <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"Os dados móveis não vão mudar automaticamente com base na disponibilidade"</string> @@ -1324,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Atalhos de pesquisa"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ícone de reduzir"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ícone de expandir"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ou"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Luz do teclado"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Nível %1$d de %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Controlos domésticos"</string> diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml index 8f5c5192133c..992db40d04a6 100644 --- a/packages/SystemUI/res/values-pt/strings.xml +++ b/packages/SystemUI/res/values-pt/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Ativar automaticamente de novo amanhã"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Recursos como o Quick Share e o Encontre Meu Dispositivo usam Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"O Bluetooth será ativado amanhã de manhã"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Compartilhamento de áudio"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Compartilhando áudio"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Bateria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Áudio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Fone de ouvido"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Que parte da sua experiência no dispositivo foi afetada?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Selecionar tipo de problema"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Gravação de tela"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"Desempenho"</string> + <string name="user_interface" msgid="3712869377953950887">"Interface do usuário"</string> + <string name="thermal" msgid="6758074791325414831">"Térmico"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modo uma mão"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Aparelhos auditivos"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Ativos"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Desconectados"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Aparelhos auditivos"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Parear novo dispositivo"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Clique para parear o novo dispositivo"</string> @@ -639,7 +636,7 @@ <string name="volume_panel_hint_muted" msgid="1124844870181285320">"som desativado"</string> <string name="volume_panel_hint_vibrate" msgid="4136223145435914132">"vibrar"</string> <string name="media_output_label_title" msgid="872824698593182505">"Tocando <xliff:g id="LABEL">%s</xliff:g> em"</string> - <string name="media_output_title_without_playing" msgid="3825663683169305013">"Onde o áudio vai tocar?"</string> + <string name="media_output_title_without_playing" msgid="3825663683169305013">"Áudio definido para"</string> <string name="media_output_title_ongoing_call" msgid="208426888064112006">"Ligando"</string> <string name="system_ui_tuner" msgid="1471348823289954729">"Sintonizador System UI"</string> <string name="status_bar" msgid="4357390266055077437">"Barra de status"</string> @@ -1210,7 +1207,7 @@ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# app está ativo}one{# apps está ativo}many{# de apps estão ativos}other{# apps estão ativos}}"</string> <string name="fgs_dot_content_description" msgid="2865071539464777240">"Nova informação"</string> <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"Apps ativos"</string> - <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Esses apps ficam ativos e em execução mesmo quando não estão em uso. Isso melhora a funcionalidade deles, mas também pode afetar a duração da bateria."</string> + <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"Esses apps ficam ativos e em execução mesmo quando não estão em uso. Isso melhora a funcionalidade deles, mas também afeta a duração da bateria."</string> <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"Parar"</string> <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"Parado"</string> <string name="clipboard_edit_text_done" msgid="4551887727694022409">"Concluído"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"fechado"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"aberto"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Bateria da stylus em <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Conecte sua stylus a um carregador"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Bateria da stylus fraca"</string> <string name="video_camera" msgid="7654002575156149298">"Filmadora"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Atalhos de pesquisa"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ícone \"Fechar\""</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ícone \"Abrir\""</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ou"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Luz de fundo do teclado"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Nível %1$d de %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Automação residencial"</string> diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml index 366480ed96db..ff07fe9efedb 100644 --- a/packages/SystemUI/res/values-ro/strings.xml +++ b/packages/SystemUI/res/values-ro/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Activează din nou automat mâine"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Funcții precum Quick Share și Găsește-mi dispozitivul folosesc Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth se va activa mâine dimineață"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Permiterea accesului la audio"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Se permite accesul la conținutul audio"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Nivelul bateriei: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Căști"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Ce parte a experienței pe dispozitiv a fost afectată?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Selectează tipul problemei"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Înregistrarea ecranului"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"Performanță"</string> + <string name="user_interface" msgid="3712869377953950887">"Interfața de utilizare"</string> + <string name="thermal" msgid="6758074791325414831">"Termal"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modul cu o mână"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Aparate auditive"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Activ"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Deconectat"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Aparate auditive"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Asociază un nou dispozitiv"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Dă clic pentru a asocia un nou dispozitiv"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"închis"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"deschis"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Bateria creionului: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Conectează-ți creionul la un încărcător"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Nivelul bateriei creionului este scăzut"</string> <string name="video_camera" msgid="7654002575156149298">"Cameră video"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Comenzi directe de căutare"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Pictograma de restrângere"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Pictograma de extindere"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"sau"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Iluminarea din spate a tastaturii"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Nivelul %1$d din %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Comenzi pentru locuință"</string> diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml index f48e42823f0d..1900a8eb9f67 100644 --- a/packages/SystemUI/res/values-ru/strings.xml +++ b/packages/SystemUI/res/values-ru/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Включить завтра автоматически"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Bluetooth используется в таких функциях и сервисах, как \"Быстрая отправка\" и \"Найти устройство\""</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth включится завтра утром"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Отправка аудио"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Включена отправка аудио"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Заряд: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудиоустройство"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Гарнитура"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"С чем связана проблема, с которой вы столкнулись?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Выберите тип проблемы"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Запись экрана"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"Производительность"</string> + <string name="user_interface" msgid="3712869377953950887">"Интерфейс"</string> + <string name="thermal" msgid="6758074791325414831">"Тепловизор"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Режим управления одной рукой"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Слуховые аппараты"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Активно"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Не подключено"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Слуховые аппараты"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Подключить новое устройство"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Нажмите, чтобы подключить новое устройство"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"устройство сложено"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"устройство разложено"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Батарея стилуса: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Поставьте стилус на зарядку."</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Низкий заряд батареи стилуса"</string> <string name="video_camera" msgid="7654002575156149298">"Видеокамера"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Найти быстрые клавиши"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Значок \"Свернуть\""</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Значок \"Развернуть\""</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"или"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Подсветка клавиатуры"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Уровень %1$d из %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Управление домом"</string> diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml index 0e621c6eae5f..5d41eed4cbdd 100644 --- a/packages/SystemUI/res/values-si/strings.xml +++ b/packages/SystemUI/res/values-si/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"හෙට ස්වයංක්රීයව නැවත ක්රියාත්මක කරන්න"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"ඉක්මන් බෙදා ගැනීම සහ මගේ උපාංගය සෙවීම වැනි විශේෂාංග බ්ලූටූත් භාවිත කරයි"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"බ්ලූටූත් හෙට උදේ සක්රීය වෙයි"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"ශ්රව්ය බෙදා ගැනීම"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"ශ්රව්යය බෙදා ගැනීම"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"බැටරිය <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ශ්රව්ය"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"හෙඩ්සෙටය"</string> @@ -361,24 +363,19 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"ඔබේ උපාංග අත්දැකීමේ කුමන කොටසට බලපෑවේ ද?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"ගැටලු වර්ගය තෝරන්න"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"තිර පටිගත කිරීම"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"කාර්ය සාධනය"</string> + <string name="user_interface" msgid="3712869377953950887">"පරිශීලක අතුරු මුහුණත"</string> + <string name="thermal" msgid="6758074791325414831">"තාප"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"තනි අත් ප්රකාරය"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"ශ්රවණ උපාංග"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"ක්රියාකාරී"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"විසන්ධි විය"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"ශ්රවණ උපාංග"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"නව උපාංගය යුගල කරන්න"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"නව උපාංගය යුගල කිරීමට ක්ලික් කරන්න"</string> <string name="hearing_devices_presets_error" msgid="350363093458408536">"පෙර සැකසීම යාවත්කාලීන කළ නොහැකි විය"</string> <string name="hearing_devices_preset_label" msgid="7878267405046232358">"පෙරසැකසුම"</string> - <string name="live_caption_title" msgid="8916875614623730005">"සජීවී සිරස්තලය"</string> + <string name="live_caption_title" msgid="8916875614623730005">"සජීවී සිරස්තල"</string> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"උපාංග මයික්රෆෝනය අවහිර කිරීම ඉවත් කරන්නද?"</string> <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"උපාංග කැමරාව අවහිර කිරීම ඉවත් කරන්නද?"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"උපාංග කැමරාව සහ මයික්රෆෝනය අවහිර කිරීම ඉවත් කරන්නද?"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"නැවූ"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"නොනැවූ"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"පන්හිඳ බැටරිය <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"ඔබේ පන්හිඳ චාජරයකට සම්බන්ධ කරන්න"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"පන්හිඳ බැටරිය අඩුයි"</string> <string name="video_camera" msgid="7654002575156149298">"වීඩියෝ කැමරාව"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"කෙටි මං සොයන්න"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"හැකුළුම් නිරූපකය"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"දිගහැරීම් නිරූපකය"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"හෝ"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"යතුරු පුවරු පසු ආලෝකය"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$dන් %1$d වැනි මට්ටම"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"නිවෙස් පාලන"</string> diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml index ba72fb491ac9..eef2a01aa55c 100644 --- a/packages/SystemUI/res/values-sk/strings.xml +++ b/packages/SystemUI/res/values-sk/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automaticky zajtra znova zapnúť"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Funkcie ako Quick Share a Nájdi moje zariadenie používajú Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth sa zapne zajtra ráno"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Zdieľanie zvuku"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Zdieľa sa zvuk"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Batéria: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Zvuk"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Náhlavná súprava"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Čo v zariadení bolo ovplyvnené?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Vyberte typ problému"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Rekordér obrazovky"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"Výkon"</string> + <string name="user_interface" msgid="3712869377953950887">"Používateľské rozhranie"</string> + <string name="thermal" msgid="6758074791325414831">"Termálne"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Režim jednej ruky"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Načúvacie zariadenia"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktívne"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Odpojené"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Načúvacie zariadenia"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Spárovať nové zariadenie"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kliknutím spárujete nové zariadenie"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"zložené"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"rozložené"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Batéria dotykového pera: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Pripojte dotykové pero k nabíjačke"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Stav batérie dotykového pera je nízky"</string> <string name="video_camera" msgid="7654002575156149298">"Videokamera"</string> @@ -1328,10 +1324,9 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Vyhľadávacie odkazy"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona zbalenia"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona rozbalenia"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"alebo"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Podsvietenie klávesnice"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"%1$d. úroveň z %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Ovládanie domácnosti"</string> - <string name="home_controls_dream_description" msgid="4644150952104035789">"Rýchlejšie ovládanie domácnosti v šetriči obrazov."</string> + <string name="home_controls_dream_description" msgid="4644150952104035789">"Rýchly prístup k ovládaniu domácnosti z šetriča obrazovky"</string> </resources> diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml index c6c1439f284d..facf72bfcda9 100644 --- a/packages/SystemUI/res/values-sl/strings.xml +++ b/packages/SystemUI/res/values-sl/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Samodejno znova vklopi jutri"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Funkcije, kot sta Hitro deljenje in Poišči mojo napravo, uporabljajo Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth se bo vklopil jutri zjutraj"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Deljenje zvoka"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Poteka deljenje zvoka"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Baterija na <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Zvok"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Slušalke z mikrofonom"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Na kateri del izkušnje z napravo je to vplivalo?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Izberite vrsto težave"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Snemanje zaslona"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"Učinkovitost delovanja"</string> + <string name="user_interface" msgid="3712869377953950887">"Uporabniški vmesnik"</string> + <string name="thermal" msgid="6758074791325414831">"Toplotno"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Enoročni način"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Slušni pripomočki"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktivno"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Brez povezave"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Slušni pripomočki"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Seznanitev nove naprave"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Kliknite za seznanitev nove naprave"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"zaprto"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"razprto"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Napolnjenost baterije pisala: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Povežite pisalo s polnilnikom."</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Skoraj prazna baterija pisala"</string> <string name="video_camera" msgid="7654002575156149298">"Videokamera"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Bližnjice za iskanje"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona za strnitev"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona za razširitev"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ali"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Osvetlitev tipkovnice"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Stopnja %1$d od %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Kontrolniki za dom"</string> diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml index dc9407f8fd31..533363240fa5 100644 --- a/packages/SystemUI/res/values-sq/strings.xml +++ b/packages/SystemUI/res/values-sq/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Aktivizoje automatikisht sërish nesër"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Veçoritë e tilla si \"Ndarja e shpejtë\" dhe \"Gjej pajisjen time\" përdorin Bluetooth-in"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth-i do të aktivizohet nesër në mëngjes"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Ndarja e audios"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Audioja po ndahet"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> bateri"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Kufje me mikrofon"</string> @@ -361,12 +363,9 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Cila pjesë e përvojës me pajisjen është prekur?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Zgjidh llojin e problemit"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Regjistrim i ekranit"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"Performanca"</string> + <string name="user_interface" msgid="3712869377953950887">"Ndërfaqja e përdoruesit"</string> + <string name="thermal" msgid="6758074791325414831">"Termike"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Modaliteti i përdorimit me një dorë"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Pajisje ndihmëse për dëgjimin"</string> <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> @@ -1275,8 +1274,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"palosur"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"shpalosur"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Bateria e stilolapsit: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Lidhe stilolapsin me një karikues"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Bateria e stilolapsit në nivel të ulët"</string> <string name="video_camera" msgid="7654002575156149298">"Videokamera"</string> @@ -1328,8 +1326,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Kërko për shkurtoret"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikona e palosjes"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikona e zgjerimit"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"ose"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Drita e sfondit e tastierës"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Niveli: %1$d nga %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Kontrollet e shtëpisë"</string> diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml index 1228eef8c88b..f35d7d86085c 100644 --- a/packages/SystemUI/res/values-sr/strings.xml +++ b/packages/SystemUI/res/values-sr/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Аутоматски поново укључи сутра"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Функције као што су Quick Share и Пронађи мој уређај користе Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth ће се укључити сутра ујутру"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Дељење звука"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Дели се звук"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Ниво батерије је <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудио"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Слушалице"</string> @@ -366,10 +368,8 @@ <string name="thermal" msgid="6758074791325414831">"Термална камера"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Режим једном руком"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Слушни апарати"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Активно"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Веза је прекинута"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Слушни апарати"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Упари нови уређај"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Кликните да бисте упарили нов уређај"</string> @@ -1324,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Пречице претраге"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Икона за скупљање"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Икона за проширивање"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"или"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Позадинско осветљење тастатуре"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"%1$d. ниво од %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Контроле за дом"</string> diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml index cf2b93cabe75..535a4aa6c162 100644 --- a/packages/SystemUI/res/values-sv/strings.xml +++ b/packages/SystemUI/res/values-sv/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Aktivera automatiskt igen i morgon"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Funktioner som Snabbdelning och Hitta min enhet använder Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth aktiveras i morgon bitti"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Ljuddelning"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Delar ljud"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> batteri"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Ljud"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string> @@ -366,10 +368,8 @@ <string name="thermal" msgid="6758074791325414831">"Termisk"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Enhandsläge"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Hörhjälpmedel"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktiva"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Frånkopplade"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Hörhjälpmedel"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Parkoppla en ny enhet"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Klicka för att parkoppla en ny enhet"</string> @@ -1324,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Sökgenvägar"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Ikonen Komprimera"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Ikonen Utöka"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"eller"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Bakgrundsbelysning för tangentbord"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Nivå %1$d av %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Hemstyrning"</string> diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml index 1ee1d0cf6165..98a7e03873ba 100644 --- a/packages/SystemUI/res/values-sw/strings.xml +++ b/packages/SystemUI/res/values-sw/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Iwashe tena kesho kiotomatiki"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Vipengele kama vile Kutuma Haraka na Tafuta Kifaa Changu hutumia Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth itawaka kesho asubuhi"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Kusikiliza Pamoja"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Mnasikiliza Pamoja"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Chaji ya betri ni <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Sauti"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Vifaa vya sauti"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Ni sehemu gani ya matumizi ya kifaa iliathiriwa?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Chagua aina ya tatizo"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Rekodi ya skrini"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"Utendaji"</string> + <string name="user_interface" msgid="3712869377953950887">"Kiolesura"</string> + <string name="thermal" msgid="6758074791325414831">"Joto"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Hali ya kutumia kwa mkono mmoja"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Vifaa vya kusikilizia"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Vimeunganishwa"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Havijaunganishwa"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Vifaa vya kusikilizia"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Unganisha kifaa kipya"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Bofya ili uunganishe kifaa kipya"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"kimekunjwa"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"kimefunguliwa"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Betri ya stylus <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Unganisha stylus yako kwenye chaja"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Chaji ya betri ya Stylus imepungua"</string> <string name="video_camera" msgid="7654002575156149298">"Kamera ya video"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Njia mkato za kutafutia"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Kunja aikoni"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Panua aikoni"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"au"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Mwanga chini ya kibodi"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Kiwango cha %1$d kati ya %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Dhibiti Vifaa Nyumbani"</string> diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml index 6e95c3b5e2dc..896b55f3ac41 100644 --- a/packages/SystemUI/res/values-ta/strings.xml +++ b/packages/SystemUI/res/values-ta/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"நாளைக்குத் தானாகவே மீண்டும் இயக்கப்படுதல்"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"விரைவுப் பகிர்தல், Find My Device போன்ற அம்சங்கள் புளூடூத்தைப் பயன்படுத்துகின்றன"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"நாளை காலை புளூடூத் இயக்கப்படும்"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"ஆடியோ பகிர்வு"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"ஆடியோ பகிரப்படுகிறது"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> பேட்டரி"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ஆடியோ"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ஹெட்செட்"</string> @@ -305,7 +307,7 @@ <string name="quick_settings_networks_unavailable" msgid="1167847013337940082">"நெட்வொர்க்குகள் கிடைக்கவில்லை"</string> <string name="quick_settings_wifi_detail_empty_text" msgid="483130889414601732">"வைஃபை நெட்வொர்க்குகள் இல்லை"</string> <string name="quick_settings_wifi_secondary_label_transient" msgid="7501659015509357887">"ஆன் செய்கிறது…"</string> - <string name="quick_settings_cast_title" msgid="3033553249449938182">"Cast"</string> + <string name="quick_settings_cast_title" msgid="3033553249449938182">"அலைபரப்பு"</string> <string name="quick_settings_casting" msgid="1435880708719268055">"அனுப்புகிறது"</string> <string name="quick_settings_cast_device_default_name" msgid="6988469571141331700">"பெயரிடப்படாத சாதனம்"</string> <string name="quick_settings_cast_detail_empty_text" msgid="2846282280014617785">"சாதனங்கள் இல்லை"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"சாதன அனுபவத்தின் எந்தப் பகுதி பாதிக்கப்பட்டது?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"சிக்கல் வகையைத் தேர்வுசெய்க"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"ஸ்கிரீன் ரெக்கார்டு"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"செயல்திறன்"</string> + <string name="user_interface" msgid="3712869377953950887">"பயனர் இடைமுகம்"</string> + <string name="thermal" msgid="6758074791325414831">"தெர்மல்"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"ஒற்றைக் கைப் பயன்முறை"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"செவித்துணைக் கருவிகள்"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"செயலில் உள்ளது"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"இணைக்கப்படவில்லை"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"செவித்துணைக் கருவிகள்"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"புதிய சாதனத்தை இணை"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"புதிய சாதனத்தை இணைக்க கிளிக் செய்யலாம்"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"மடக்கப்பட்டது"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"விரிக்கப்பட்டது"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"ஸ்டைலஸ் பேட்டரி <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"உங்கள் ஸ்டைலஸைச் சார்ஜருடன் இணையுங்கள்"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"ஸ்டைலஸின் பேட்டரி குறைவாக உள்ளது"</string> <string name="video_camera" msgid="7654002575156149298">"வீடியோ கேமரா"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"தேடல் ஷார்ட்கட்கள்"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"சுருக்குவதற்கான ஐகான்"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"விரிவாக்குவதற்கான ஐகான்"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"அல்லது"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"கீபோர்டு பேக்லைட்"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"நிலை, %2$d இல் %1$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"ஹோம் கன்ட்ரோல்கள்"</string> diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml index 64d6f9e2419c..b88386ff05c4 100644 --- a/packages/SystemUI/res/values-te/strings.xml +++ b/packages/SystemUI/res/values-te/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"రేపు మళ్లీ ఆటోమేటిక్గా ఆన్ చేస్తుంది"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"క్విక్ షేర్, Find My Device వంటి ఫీచర్లు బ్లూటూత్ను ఉపయోగిస్తాయి"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"బ్లూటూత్ రేపు ఉదయం ఆన్ అవుతుంది"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"ఆడియో షేరింగ్"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"ఆడియోను షేర్ చేస్తున్నారు"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> బ్యాటరీ"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"ఆడియో"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"హెడ్సెట్"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"మీ పరికర అనుభవంలో ఏ భాగం ప్రభావితమైంది?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"సమస్య రకాన్ని ఎంచుకోండి"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"స్క్రీన్ రికార్డ్"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"పనితీరు"</string> + <string name="user_interface" msgid="3712869377953950887">"యూజర్ ఇంటర్ఫేస్"</string> + <string name="thermal" msgid="6758074791325414831">"థర్మల్"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"వన్-హ్యాండెడ్ మోడ్"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"వినికిడి పరికరాలు"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"యాక్టివ్"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"డిస్కనెక్ట్ అయ్యింది"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"వినికిడి పరికరాలు"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"కొత్త పరికరాన్ని పెయిర్ చేయండి"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"కొత్త పరికరాన్ని పెయిర్ చేయడానికి క్లిక్ చేయండి"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"మడిచే సదుపాయం గల పరికరం"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"మడిచే సదుపాయం లేని పరికరం"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"స్టయిలస్ బ్యాటరీ <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"మీ స్టైలస్ను ఛార్జర్కి కనెక్ట్ చేయండి"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"తక్కువ స్టైలస్ బ్యాటరీ"</string> <string name="video_camera" msgid="7654002575156149298">"వీడియో కెమెరా"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"సెర్చ్ షార్ట్కట్లు"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"కుదించండి చిహ్నం"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"విస్తరించండి చిహ్నం"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"లేదా"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"కీబోర్డ్ బ్యాక్లైట్"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$dలో %1$dవ స్థాయి"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"హోమ్ కంట్రోల్స్"</string> diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml index 8a766299f354..12c676ee482f 100644 --- a/packages/SystemUI/res/values-th/strings.xml +++ b/packages/SystemUI/res/values-th/strings.xml @@ -279,10 +279,12 @@ <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ยกเลิกการเชื่อมต่อ"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"เปิดใช้งาน"</string> <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"เปิดอีกครั้งโดยอัตโนมัติในวันพรุ่งนี้"</string> - <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"ฟีเจอร์ต่างๆ เช่น Quick Share และหาอุปกรณ์ของฉัน ใช้บลูทูธ"</string> + <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"ฟีเจอร์ต่างๆ เช่น Quick Share และ \"หาอุปกรณ์ของฉัน\" ใช้บลูทูธ"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"บลูทูธจะเปิดพรุ่งนี้เช้า"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"การแชร์เสียง"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"กำลังแชร์เสียง"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"แบตเตอรี่ <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"เสียง"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ชุดหูฟัง"</string> @@ -366,10 +368,8 @@ <string name="thermal" msgid="6758074791325414831">"ความร้อน"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"โหมดมือเดียว"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"เครื่องช่วยฟัง"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"ใช้งานอยู่"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"ยกเลิกการเชื่อมต่อแล้ว"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"เครื่องช่วยฟัง"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"จับคู่อุปกรณ์ใหม่"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"คลิกเพื่อจับคู่อุปกรณ์ใหม่"</string> @@ -1324,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"ค้นหาแป้นพิมพ์ลัด"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"ไอคอนยุบ"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"ไอคอนขยาย"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"หรือ"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"ไฟแบ็กไลต์ของแป้นพิมพ์"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"ระดับที่ %1$d จาก %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"ระบบควบคุมอุปกรณ์สมาร์ทโฮม"</string> diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml index e74139f693a9..84a4541f3694 100644 --- a/packages/SystemUI/res/values-tl/strings.xml +++ b/packages/SystemUI/res/values-tl/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Awtomatikong i-on ulit bukas"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Gumagamit ng Bluetooth ang mga feature tulad ng Quick Share at Hanapin ang Aking Device"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Mag-o-on ang Bluetooth bukas ng umaga"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Pag-share ng Audio"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Ibinabahagi ang Audio"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> na baterya"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Headset"</string> @@ -366,10 +368,8 @@ <string name="thermal" msgid="6758074791325414831">"Thermal"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"One-hand mode"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Mga hearing device"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Aktibo"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Nadiskonekta"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Mga hearing device"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Magpares ng bagong device"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"I-click para magpares ng bagong device"</string> @@ -1324,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Mga shortcut ng paghahanap"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"I-collapse ang icon"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"I-expand ang icon"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"o"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Backlight ng keyboard"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Level %1$d sa %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Mga Home Control"</string> diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml index a66fbedd4769..c2df2c24c61d 100644 --- a/packages/SystemUI/res/values-tr/strings.xml +++ b/packages/SystemUI/res/values-tr/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Yarın otomatik olarak tekrar aç"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Quick Share ve Cihazımı Bul gibi özellikler Bluetooth\'u kullanır"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth yarın sabah açılacak"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Ses Paylaşımı"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Ses paylaşılıyor"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Pil düzeyi <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Ses"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Mikrofonlu kulaklık"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Cihaz deneyiminiz ne şekilde etkilendi?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Sorun türünü seçin"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Ekran kaydedicisi"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"Performans"</string> + <string name="user_interface" msgid="3712869377953950887">"Kullanıcı Arayüzü"</string> + <string name="thermal" msgid="6758074791325414831">"Termal"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Tek el modu"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"İşitme cihazları"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Etkin"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Bağlı değil"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"İşitme cihazları"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Yeni cihaz eşle"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Yeni cihaz eşlemek için tıklayın"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"katlanmış"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"katlanmamış"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Ekran kalemi pili: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Ekran kaleminizi bir şarj cihazına bağlayın"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Ekran kaleminin pil seviyesi düşük"</string> <string name="video_camera" msgid="7654002575156149298">"Video kamera"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Arama kısayolları"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Daralt simgesi"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Genişlet simgesi"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"veya"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Klavye aydınlatması"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Seviye %1$d / %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Ev Kontrolleri"</string> diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml index 5262d744336d..5fcfb3ea41c5 100644 --- a/packages/SystemUI/res/values-uk/strings.xml +++ b/packages/SystemUI/res/values-uk/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Автоматично ввімкнути знову завтра"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Такі функції, як швидкий обмін і \"Знайти пристрій\", використовують Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth увімкнеться завтра вранці"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Надсилання аудіо"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Надсилання аудіо"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> заряду акумулятора"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Аудіопристрій"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Гарнітура"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"На який аспект роботи пристрою вплинула проблема?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Виберіть тип проблеми"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Запис відео з екрана"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"Продуктивність"</string> + <string name="user_interface" msgid="3712869377953950887">"Інтерфейс користувача"</string> + <string name="thermal" msgid="6758074791325414831">"Нагрівання"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Режим керування однією рукою"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Слухові апарати"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Під’єднано"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Від’єднано"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Слухові апарати"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Підключити новий пристрій"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Натисніть, щоб підключити новий пристрій"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"складений"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"розкладений"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Заряд акумулятора стилуса: <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Підключіть стилус до зарядного пристрою"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Низький заряд акумулятора стилуса"</string> <string name="video_camera" msgid="7654002575156149298">"Відеокамера"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Комбінації клавіш для пошуку"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Значок згортання"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Значок розгортання"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"або"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Підсвічування клавіатури"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Рівень %1$d з %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Автоматизація дому"</string> diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml index 0d3f1feb406f..f08dd7eb13b7 100644 --- a/packages/SystemUI/res/values-ur/strings.xml +++ b/packages/SystemUI/res/values-ur/strings.xml @@ -278,11 +278,13 @@ <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"محفوظ ہے"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"غیر منسلک کریں"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"فعال کریں"</string> - <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"کل دوبارہ خودکار طور پر آن ہوگا"</string> + <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"کل دوبارہ خودکار طور پر آن کریں"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"فوری اشتراک اور \'میرا آلہ ڈھونڈیں\' جیسی خصوصیات بلوٹوتھ کا استعمال کرتی ہیں"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"بلوٹوتھ کل صبح آن ہو جائے گا"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"آڈیو کا اشتراک"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"آڈیو کا اشتراک ہو رہا ہے"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> بیٹری"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"آڈیو"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"ہیڈ سیٹ"</string> @@ -366,10 +368,8 @@ <string name="thermal" msgid="6758074791325414831">"تھرمل"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"ایک ہاتھ کی وضع"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"سماعت کے آلات"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"فعال"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"غیر منسلک ہے"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"سماعت کے آلات"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"نئے آلے کا جوڑا بنائیں"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"نئے آلے کا جوڑا بنانے کے لیے کلک کریں"</string> @@ -1328,8 +1328,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"تلاش کے شارٹ کٹس"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"آئیکن سکیڑیں"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"آئیکن پھیلائیں"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"یا"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"کی بورڈ بیک لائٹ"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"%2$d میں سے %1$d کا لیول"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"ہوم کنٹرولز"</string> diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml index 3f3983d21fb0..4a04a337a6cd 100644 --- a/packages/SystemUI/res/values-uz/strings.xml +++ b/packages/SystemUI/res/values-uz/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Ertaga yana avtomatik yoqilsin"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Tezkor ulashuv va Qurilmamni top kabi funksiyalar Bluetooth ishlatadi"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth ertaga ertalab yoqiladi"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Audio ulashuvi"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Audio ulashuvi yoniq"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"Batareya quvvati: <xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Audio"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Garnitura"</string> @@ -366,10 +368,8 @@ <string name="thermal" msgid="6758074791325414831">"Termal"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Ixcham rejim"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Eshitish qurilmalari"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Faol"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Uzildi"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Eshitish qurilmalari"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Yangi qurilmani ulash"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Yangi qurilmani ulash uchun bosing"</string> @@ -1324,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Tezkor tugmalar qidiruvi"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Yigʻish belgisi"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Yoyish belgisi"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"yoki"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Klaviatura orqa yoritkichi"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Daraja: %1$d / %2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Uy boshqaruvi"</string> diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml index dc4fa1241559..45d9165472cc 100644 --- a/packages/SystemUI/res/values-vi/strings.xml +++ b/packages/SystemUI/res/values-vi/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Tự động bật lại vào ngày mai"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Các tính năng như Chia sẻ nhanh và Tìm thiết bị của tôi đều sử dụng Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"Bluetooth sẽ bật vào sáng mai"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Chia sẻ âm thanh"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Đang chia sẻ âm thanh"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> pin"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Âm thanh"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Tai nghe"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Bạn gặp loại vấn đề gì khi dùng thiết bị?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Chọn loại vấn đề"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Ghi màn hình"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"Hiệu suất"</string> + <string name="user_interface" msgid="3712869377953950887">"Giao diện người dùng"</string> + <string name="thermal" msgid="6758074791325414831">"Nhiệt"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Chế độ một tay"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Thiết bị trợ thính"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Đang hoạt động"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Đã ngắt kết nối"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Thiết bị trợ thính"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Ghép nối thiết bị mới"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Nhấp để ghép nối thiết bị mới"</string> @@ -933,7 +930,7 @@ <string name="running_foreground_services_title" msgid="5137313173431186685">"Ứng dụng đang chạy trong nền"</string> <string name="running_foreground_services_msg" msgid="3009459259222695385">"Nhấn để biết chi tiết về mức sử dụng dữ liệu và pin"</string> <string name="mobile_data_disable_title" msgid="5366476131671617790">"Tắt dữ liệu di động?"</string> - <string name="mobile_data_disable_message" msgid="8604966027899770415">"Bạn sẽ không có quyền sử dụng dữ liệu hoặc truy cập Internet thông qua chế độ <xliff:g id="CARRIER">%s</xliff:g>. Bạn chỉ có thể truy cập Internet thông qua Wi-Fi."</string> + <string name="mobile_data_disable_message" msgid="8604966027899770415">"Bạn sẽ không có quyền sử dụng dữ liệu hoặc truy cập Internet thông qua mạng <xliff:g id="CARRIER">%s</xliff:g>. Bạn chỉ có thể truy cập Internet thông qua Wi-Fi."</string> <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"nhà mạng của bạn"</string> <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"Chuyển về <xliff:g id="CARRIER">%s</xliff:g>?"</string> <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"Dữ liệu di động sẽ không tự động chuyển dựa trên tình trạng phủ sóng"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"gập"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"mở"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Mức pin bút cảm ứng <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Hãy kết nối bút cảm ứng với bộ sạc"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Bút cảm ứng bị yếu pin"</string> <string name="video_camera" msgid="7654002575156149298">"Máy quay video"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Lối tắt tìm kiếm"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Biểu tượng Thu gọn"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Biểu tượng Mở rộng"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"hoặc"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Đèn nền bàn phím"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Độ sáng %1$d/%2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Điều khiển nhà"</string> diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml index 9b69f9ef2086..98a1187bd661 100644 --- a/packages/SystemUI/res/values-zh-rCN/strings.xml +++ b/packages/SystemUI/res/values-zh-rCN/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"明天自动重新开启"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"快速分享、查找我的设备等功能会使用蓝牙"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"蓝牙将在明天早上开启"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"音频分享"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"正在分享音频"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> 的电量"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"音频"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"耳机"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"设备体验的哪个方面受到影响?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"选择问题类型"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"屏幕录制"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"性能"</string> + <string name="user_interface" msgid="3712869377953950887">"界面"</string> + <string name="thermal" msgid="6758074791325414831">"散热"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"单手模式"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"助听装置"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"已连接"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"已断开连接"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"助听装置"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"与新设备配对"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"点击即可与新设备配对"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"折叠状态"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"展开状态"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s/%2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"触控笔电量为 <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"请将触控笔连接充电器"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"触控笔电池电量低"</string> <string name="video_camera" msgid="7654002575156149298">"摄像机"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"搜索快捷键"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"收起图标"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"展开图标"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"或"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"键盘背光"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"第 %1$d 级,共 %2$d 级"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"家居控制"</string> diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml index 5c164ab23c28..edc624ff9c26 100644 --- a/packages/SystemUI/res/values-zh-rHK/strings.xml +++ b/packages/SystemUI/res/values-zh-rHK/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"明天自動重新開啟"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"「快速共享」和「尋找我的裝置」等功能都會使用藍牙"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"藍牙將於明天上午開啟"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"音訊分享功能"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"正在分享音訊"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"電量:<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"音訊"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"耳機"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"哪些裝置使用體驗受影響?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"選取問題類型"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"螢幕錄影"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"效能"</string> + <string name="user_interface" msgid="3712869377953950887">"使用者介面"</string> + <string name="thermal" msgid="6758074791325414831">"熱能"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"單手模式"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"助聽器"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"已連線"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"已中斷連線"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"助聽器"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"配對新裝置"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"㩒一下就可以配對新裝置"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"已摺疊"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"已打開"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"觸控筆電量:<xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"將觸控筆連接充電器"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"觸控筆電量不足"</string> <string name="video_camera" msgid="7654002575156149298">"攝影機"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"搜尋快速鍵"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"收合圖示"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"展開圖示"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"或"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"鍵盤背光"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"第 %1$d 級,共 %2$d 級"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"智能家居"</string> diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml index 0d50ad57768d..567928276f46 100644 --- a/packages/SystemUI/res/values-zh-rTW/strings.xml +++ b/packages/SystemUI/res/values-zh-rTW/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"明天自動重新開啟"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"「快速分享」和「尋找我的裝置」等功能都需要使用藍牙技術"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"藍牙會在明天早上開啟"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"音訊分享"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"正在分享音訊"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"電量:<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g>"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"音訊"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"耳機"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"哪些裝置使用體驗受到影響?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"選取問題類型"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"螢幕錄影"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"效能"</string> + <string name="user_interface" msgid="3712869377953950887">"使用者介面"</string> + <string name="thermal" msgid="6758074791325414831">"熱成像"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"單手模式"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"助聽器"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"已連線"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"連線中斷"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"助聽器"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"配對新裝置"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"按一下即可配對新裝置"</string> @@ -380,7 +377,7 @@ <string name="hearing_devices_preset_label" msgid="7878267405046232358">"預設"</string> <string name="live_caption_title" msgid="8916875614623730005">"即時字幕"</string> <string name="sensor_privacy_start_use_mic_dialog_title" msgid="563796653825944944">"要解除封鎖裝置麥克風嗎?"</string> - <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"要解除封鎖裝置相機嗎?"</string> + <string name="sensor_privacy_start_use_camera_dialog_title" msgid="8807639852654305227">"解除封鎖裝置相機?"</string> <string name="sensor_privacy_start_use_mic_camera_dialog_title" msgid="4316471859905020023">"要將裝置的相機和麥克風解除封鎖嗎?"</string> <string name="sensor_privacy_start_use_mic_dialog_content" msgid="1624701280680913717">"執行後,具備麥克風存取權的所有應用程式和服務,都將可使用麥克風。"</string> <string name="sensor_privacy_start_use_camera_dialog_content" msgid="4704948062372435963">"執行後,具備相機存取權的所有應用程式和服務,都將可使用相機。"</string> @@ -933,7 +930,7 @@ <string name="running_foreground_services_title" msgid="5137313173431186685">"在背景執行的應用程式"</string> <string name="running_foreground_services_msg" msgid="3009459259222695385">"輕觸即可查看電池和數據用量詳情"</string> <string name="mobile_data_disable_title" msgid="5366476131671617790">"要關閉行動數據嗎?"</string> - <string name="mobile_data_disable_message" msgid="8604966027899770415">"你將無法透過「<xliff:g id="CARRIER">%s</xliff:g>」使用行動數據或網際網路。你只能透過 Wi-Fi 使用網際網路。"</string> + <string name="mobile_data_disable_message" msgid="8604966027899770415">"你將無法透過「<xliff:g id="CARRIER">%s</xliff:g>」使用行動數據或網際網路,只能利用 Wi-Fi 上網。"</string> <string name="mobile_data_disable_message_default_carrier" msgid="6496033312431658238">"你的電信業者"</string> <string name="auto_data_switch_disable_title" msgid="5146527155665190652">"要切換回「<xliff:g id="CARRIER">%s</xliff:g>」嗎?"</string> <string name="auto_data_switch_disable_message" msgid="5885533647399535852">"行動數據不會依據可用性自動切換"</string> @@ -1210,7 +1207,7 @@ <string name="fgs_manager_footer_label" msgid="8276763570622288231">"{count,plural, =1{# 個應用程式正在運作}other{# 個應用程式正在運作}}"</string> <string name="fgs_dot_content_description" msgid="2865071539464777240">"新資訊"</string> <string name="fgs_manager_dialog_title" msgid="5879184257257718677">"運作中的應用程式"</string> - <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"這些應用程式會在沒有使用的情況下持續運作。應用程式的實用度會因此提升,但也可能影響電池續航力。"</string> + <string name="fgs_manager_dialog_message" msgid="2670045017200730076">"這些應用程式即使是在閒置狀態下,也會持續運作。應用程式的功能會因而提升,但也可能影響電池續航力。"</string> <string name="fgs_manager_app_item_stop_button_label" msgid="7188317969020801156">"停止"</string> <string name="fgs_manager_app_item_stop_button_stopped_label" msgid="6950382004441263922">"已停止"</string> <string name="clipboard_edit_text_done" msgid="4551887727694022409">"完成"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"已摺疊"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"已展開"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"觸控筆電量:<xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"將觸控筆接上充電器"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"觸控筆電力不足"</string> <string name="video_camera" msgid="7654002575156149298">"攝影機"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"搜尋快速鍵"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"收合圖示"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"展開圖示"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"或"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"鍵盤背光"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"第 %1$d 級,共 %2$d 級"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"居家控制"</string> diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml index 2e03bd502214..a0a57acaa362 100644 --- a/packages/SystemUI/res/values-zu/strings.xml +++ b/packages/SystemUI/res/values-zu/strings.xml @@ -281,8 +281,10 @@ <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Vula ngokuzenzekela futhi kusasa"</string> <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Izakhi ezifana nokuthi Ukwabelana Ngokushesha kanye nokuthi Thola Idivayisi Yami zisebenzisa i-Bluetooth"</string> <string name="turn_on_bluetooth_auto_info_enabled" msgid="7440944034584560279">"IBluetooth izovuleka kusasa ekuseni"</string> - <string name="quick_settings_bluetooth_audio_sharing_button" msgid="4499275822759907822">"Ukwabelana Ngokuqoshiwe"</string> - <string name="quick_settings_bluetooth_audio_sharing_button_sharing" msgid="8626191139359072540">"Ukwabelana Ngomsindo"</string> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (7545274861795853838) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (3069309588231072128) --> + <skip /> <string name="quick_settings_bluetooth_secondary_label_battery_level" msgid="4182034939479344093">"<xliff:g id="BATTERY_LEVEL_AS_PERCENTAGE">%s</xliff:g> ibhethri"</string> <string name="quick_settings_bluetooth_secondary_label_audio" msgid="780333390310051161">"Umsindo"</string> <string name="quick_settings_bluetooth_secondary_label_headset" msgid="2332093067553000852">"Ihedisethi"</string> @@ -361,18 +363,13 @@ <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Kuthinteke yiphi ingxenye yokusebenzisa idivayisi?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Khetha uhlobo lwenkinga"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Irekhodi lesikrini"</string> - <!-- no translation found for performance (6552785217174378320) --> - <skip /> - <!-- no translation found for user_interface (3712869377953950887) --> - <skip /> - <!-- no translation found for thermal (6758074791325414831) --> - <skip /> + <string name="performance" msgid="6552785217174378320">"Ukusebenza"</string> + <string name="user_interface" msgid="3712869377953950887">"Okusetshenziswa Kubonwa"</string> + <string name="thermal" msgid="6758074791325414831">"Ithermal"</string> <string name="quick_settings_onehanded_label" msgid="2416537930246274991">"Imodi yesandla esisodwa"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"Izinsizakuzwa"</string> - <!-- no translation found for quick_settings_hearing_devices_connected (6519069502397037781) --> - <skip /> - <!-- no translation found for quick_settings_hearing_devices_disconnected (8907061223998176187) --> - <skip /> + <string name="quick_settings_hearing_devices_connected" msgid="6519069502397037781">"Kuyasebenza"</string> + <string name="quick_settings_hearing_devices_disconnected" msgid="8907061223998176187">"Inqamukile"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"Izinsizakuzwa"</string> <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"Bhangqa idivayisi entsha"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"Chofoza ukuze ubhangqe idivayisi entsha"</string> @@ -1275,8 +1272,7 @@ <string name="quick_settings_rotation_posture_folded" msgid="2430280856312528289">"kugoqiwe"</string> <string name="quick_settings_rotation_posture_unfolded" msgid="6372316273574167114">"kuvuliwe"</string> <string name="rotation_tile_with_posture_secondary_label_template" msgid="7648496484163318886">"%1$s / %2$s"</string> - <!-- no translation found for stylus_battery_low_percentage (2564243323894629626) --> - <skip /> + <string name="stylus_battery_low_percentage" msgid="2564243323894629626">"Ibhethri lestylus <xliff:g id="PERCENTAGE">%s</xliff:g>"</string> <string name="stylus_battery_low_subtitle" msgid="3583843128908823273">"Xhuma i-stylus yakho kushaja"</string> <string name="stylus_battery_low" msgid="7134370101603167096">"Ibhethri le-stylus liphansi"</string> <string name="video_camera" msgid="7654002575156149298">"Ikhamera yevidiyo"</string> @@ -1328,8 +1324,7 @@ <string name="shortcut_helper_search_placeholder" msgid="5488547526269871819">"Sesha izinqamuleli"</string> <string name="shortcut_helper_content_description_collapse_icon" msgid="8028015738431664954">"Goqa isithonjana"</string> <string name="shortcut_helper_content_description_expand_icon" msgid="1084435697860417390">"Nweba isithonjana"</string> - <!-- no translation found for shortcut_helper_key_combinations_or_separator (7082902112102125540) --> - <skip /> + <string name="shortcut_helper_key_combinations_or_separator" msgid="7082902112102125540">"noma"</string> <string name="keyboard_backlight_dialog_title" msgid="8273102932345564724">"Ilambu lekhibhodi"</string> <string name="keyboard_backlight_value" msgid="7336398765584393538">"Ileveli %1$d ka-%2$d"</string> <string name="home_controls_dream_label" msgid="6567105701292324257">"Izilawuli Zasekhaya"</string> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 0017db68f85b..fe968a761f69 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -319,6 +319,29 @@ <string name="screenrecord_save_error">Error saving screen recording</string> <!-- A toast message shown when the screen recording cannot be started due to a generic error [CHAR LIMIT=NONE] --> <string name="screenrecord_start_error">Error starting screen recording</string> + <!-- Title for a dialog shown to the user that will let them stop recording their screen [CHAR LIMIT=50] --> + <string name="screenrecord_stop_dialog_title">Stop recording screen?</string> + <!-- Text telling a user that they will stop recording their screen if they click the "Stop recording" button [CHAR LIMIT=100] --> + <string name="screenrecord_stop_dialog_message">You will stop recording your screen</string> + <!-- Button to stop a screen recording [CHAR LIMIT=35] --> + <string name="screenrecord_stop_dialog_button">Stop recording</string> + + <!-- Title for a dialog shown to the user that will let them stop sharing their screen to another app on the device [CHAR LIMIT=50] --> + <string name="share_to_app_stop_dialog_title">Stop sharing screen?</string> + <!-- Text telling a user that they will stop sharing their screen if they click the "Stop sharing" button [CHAR LIMIT=100] --> + <string name="share_to_app_stop_dialog_message">You will stop sharing your screen</string> + <!-- Button to stop screen sharing [CHAR LIMIT=35] --> + <string name="share_to_app_stop_dialog_button">Stop sharing</string> + + <!-- Title for a dialog shown to the user that will let them stop casting their screen to a different device [CHAR LIMIT=50] --> + <string name="cast_to_other_device_stop_dialog_title">Stop casting screen?</string> + <!-- Text telling a user that they will stop casting their screen to a different device if they click the "Stop casting" button [CHAR LIMIT=100] --> + <string name="cast_to_other_device_stop_dialog_message">You will stop casting your screen</string> + <!-- Button to stop screen casting to a different device [CHAR LIMIT=35] --> + <string name="cast_to_other_device_stop_dialog_button">Stop casting</string> + + <!-- Button to close a dialog without doing any action [CHAR LIMIT=20] --> + <string name="close_dialog_button">Close</string> <!-- Notification title displayed for issue recording [CHAR LIMIT=50]--> <string name="issuerecord_title">Issue Recorder</string> @@ -3538,6 +3561,15 @@ --> <string name="shortcut_helper_key_combinations_or_separator">or</string> + <!-- Label for button opening tutorial for back gesture on touchpad [CHAR LIMIT=NONE] --> + <string name="touchpad_tutorial_back_gesture_button">Back gesture</string> + <!-- Label for button opening tutorial for back gesture on touchpad [CHAR LIMIT=NONE] --> + <string name="touchpad_tutorial_home_gesture_button">Home gesture</string> + <!-- Label for button opening tutorial on using Action key from keyboard [CHAR LIMIT=NONE] --> + <string name="touchpad_tutorial_action_key_button">Action key</string> + <!-- Label for button finishing touchpad tutorial [CHAR LIMIT=NONE] --> + <string name="touchpad_tutorial_done_button">Done</string> + <!-- Content description for keyboard backlight brightness dialog [CHAR LIMIT=NONE] --> <string name="keyboard_backlight_dialog_title">Keyboard backlight</string> <!-- Content description for keyboard backlight brightness value [CHAR LIMIT=NONE] --> diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimator.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimator.kt index 7a8c82cee32a..4fd54566bfb6 100644 --- a/packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimator.kt +++ b/packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimator.kt @@ -37,10 +37,17 @@ class UnfoldConstantTranslateAnimator( private lateinit var rootView: ViewGroup private var translationMax = 0f + /** + * Initializes the animator, it is allowed to call this method multiple times, for example + * to update the rootView or maximum translation + */ fun init(rootView: ViewGroup, translationMax: Float) { + if (!::rootView.isInitialized) { + progressProvider.addCallback(this) + } + this.rootView = rootView this.translationMax = translationMax - progressProvider.addCallback(this) } override fun onTransitionStarted() { diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt index 7170be6124d2..19d918f5c556 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUnfoldTransition.kt @@ -17,16 +17,21 @@ package com.android.keyguard import android.content.Context -import android.view.ViewGroup -import com.android.systemui.res.R +import android.view.View +import com.android.systemui.keyguard.MigrateClocksToBlueprint +import com.android.systemui.keyguard.ui.view.KeyguardRootView import com.android.systemui.plugins.statusbar.StatusBarStateController -import com.android.systemui.statusbar.StatusBarState.KEYGUARD +import com.android.systemui.res.R +import com.android.systemui.shared.R as sharedR +import com.android.systemui.shade.NotificationShadeWindowView import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator.Direction.END import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator.Direction.START import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator.ViewIdToTranslate +import com.android.systemui.statusbar.StatusBarState.KEYGUARD import com.android.systemui.unfold.SysUIUnfoldScope -import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider +import com.android.systemui.unfold.UnfoldTransitionProgressProvider +import com.android.systemui.unfold.dagger.NaturalRotation import javax.inject.Inject /** @@ -38,8 +43,10 @@ class KeyguardUnfoldTransition @Inject constructor( private val context: Context, + private val keyguardRootView: KeyguardRootView, + private val shadeWindowView: NotificationShadeWindowView, statusBarStateController: StatusBarStateController, - unfoldProgressProvider: NaturalRotationUnfoldProgressProvider, + @NaturalRotation unfoldProgressProvider: UnfoldTransitionProgressProvider, ) { /** Certain views only need to move if they are not currently centered */ @@ -50,27 +57,94 @@ constructor( private val filterKeyguard: () -> Boolean = { statusBarStateController.getState() == KEYGUARD } private val translateAnimator by lazy { + val smartSpaceViews = if (MigrateClocksToBlueprint.isEnabled) { + // Use scrollX instead of translationX as translation is already set by [AodBurnInLayer] + val scrollXTranslation = { view: View, translation: Float -> + view.scrollX = -translation.toInt() + } + + setOf( + ViewIdToTranslate( + viewId = sharedR.id.date_smartspace_view, + direction = START, + shouldBeAnimated = filterKeyguard, + translateFunc = scrollXTranslation, + ), + ViewIdToTranslate( + viewId = sharedR.id.bc_smartspace_view, + direction = START, + shouldBeAnimated = filterKeyguard, + translateFunc = scrollXTranslation, + ), + ViewIdToTranslate( + viewId = sharedR.id.weather_smartspace_view, + direction = START, + shouldBeAnimated = filterKeyguard, + translateFunc = scrollXTranslation, + ) + ) + } else { + setOf(ViewIdToTranslate( + viewId = R.id.keyguard_status_area, + direction = START, + shouldBeAnimated = filterKeyguard, + translateFunc = { view, value -> + (view as? KeyguardStatusAreaView)?.translateXFromUnfold = value + } + )) + } + UnfoldConstantTranslateAnimator( viewsIdToTranslate = setOf( - ViewIdToTranslate(R.id.keyguard_status_area, START, filterKeyguard, - { view, value -> - (view as? KeyguardStatusAreaView)?.translateXFromUnfold = value - }), ViewIdToTranslate( - R.id.lockscreen_clock_view_large, START, filterKeyguardAndSplitShadeOnly), - ViewIdToTranslate(R.id.lockscreen_clock_view, START, filterKeyguard), + viewId = R.id.lockscreen_clock_view_large, + direction = START, + shouldBeAnimated = filterKeyguardAndSplitShadeOnly + ), + ViewIdToTranslate( + viewId = R.id.lockscreen_clock_view, + direction = START, + shouldBeAnimated = filterKeyguard + ), ViewIdToTranslate( - R.id.notification_stack_scroller, END, filterKeyguardAndSplitShadeOnly), - ViewIdToTranslate(R.id.start_button, START, filterKeyguard), - ViewIdToTranslate(R.id.end_button, END, filterKeyguard)), - progressProvider = unfoldProgressProvider) + viewId = R.id.notification_stack_scroller, + direction = END, + shouldBeAnimated = filterKeyguardAndSplitShadeOnly + ) + ) + smartSpaceViews, + progressProvider = unfoldProgressProvider + ) } - /** Relies on the [parent] to locate views to translate. */ - fun setup(parent: ViewGroup) { + private val shortcutButtonsAnimator by lazy { + UnfoldConstantTranslateAnimator( + viewsIdToTranslate = + setOf( + ViewIdToTranslate( + viewId = R.id.start_button, + direction = START, + shouldBeAnimated = filterKeyguard + ), + ViewIdToTranslate( + viewId = R.id.end_button, + direction = END, + shouldBeAnimated = filterKeyguard + ) + ), + progressProvider = unfoldProgressProvider + ) + } + + /** Initializes the keyguard fold/unfold transition */ + fun setup() { val translationMax = context.resources.getDimensionPixelSize(R.dimen.keyguard_unfold_translation_x).toFloat() - translateAnimator.init(parent, translationMax) + + translateAnimator.init(shadeWindowView, translationMax) + + // Use keyguard root view as there is another instance of start/end buttons with the same ID + // outside of the keyguard root view + shortcutButtonsAnimator.init(keyguardRootView, translationMax) } } diff --git a/packages/SystemUI/src/com/android/systemui/ambient/statusbar/ui/AmbientStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/ambient/statusbar/ui/AmbientStatusBarViewController.java index a242d5a25238..abdc3338b7f2 100644 --- a/packages/SystemUI/src/com/android/systemui/ambient/statusbar/ui/AmbientStatusBarViewController.java +++ b/packages/SystemUI/src/com/android/systemui/ambient/statusbar/ui/AmbientStatusBarViewController.java @@ -30,6 +30,7 @@ import android.view.View; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; +import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dreams.DreamLogger; import com.android.systemui.dreams.DreamOverlayNotificationCountProvider; @@ -82,9 +83,11 @@ public class AmbientStatusBarViewController extends ViewController<AmbientStatus private final Executor mMainExecutor; private final List<DreamOverlayStatusBarItemsProvider.StatusBarItem> mExtraStatusBarItems = new ArrayList<>(); + private final CommunalSceneInteractor mCommunalSceneInteractor; private final DreamLogger mLogger; private boolean mIsAttached; + private boolean mCommunalVisible; // Whether dream entry animations are finished. private boolean mEntryAnimationsFinished = false; @@ -140,6 +143,7 @@ public class AmbientStatusBarViewController extends ViewController<AmbientStatus DreamOverlayStateController dreamOverlayStateController, UserTracker userTracker, WifiInteractor wifiInteractor, + CommunalSceneInteractor communalSceneInteractor, @DreamLog LogBuffer logBuffer) { super(view); mResources = resources; @@ -155,6 +159,7 @@ public class AmbientStatusBarViewController extends ViewController<AmbientStatus mDreamOverlayStateController = dreamOverlayStateController; mUserTracker = userTracker; mWifiInteractor = wifiInteractor; + mCommunalSceneInteractor = communalSceneInteractor; mLogger = new DreamLogger(logBuffer, TAG); // Register to receive show/hide updates for the system status bar. Our custom status bar @@ -172,6 +177,12 @@ public class AmbientStatusBarViewController extends ViewController<AmbientStatus network -> updateWifiUnavailableStatusIcon( network instanceof WifiNetworkModel.Active)); + collectFlow( + mView, + mCommunalSceneInteractor.isCommunalVisible(), + this::onCommunalVisibleChanged + ); + mNextAlarmController.addCallback(mNextAlarmCallback); updateAlarmStatusIcon(); @@ -230,9 +241,15 @@ public class AmbientStatusBarViewController extends ViewController<AmbientStatus mView.setTranslationY(translationY); } + private void onCommunalVisibleChanged(boolean visible) { + mCommunalVisible = visible; + updateVisibility(); + } + private boolean shouldShowStatusBar() { - return !mDreamOverlayStateController.isLowLightActive() - && !mStatusBarWindowStateController.windowIsShowing(); + return (!mDreamOverlayStateController.isLowLightActive() + && !mStatusBarWindowStateController.windowIsShowing()) + || mCommunalVisible; } @VisibleForTesting diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogLogger.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogLogger.kt index c30aea07e959..72312b87dc57 100644 --- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogLogger.kt +++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogLogger.kt @@ -16,6 +16,7 @@ package com.android.systemui.bluetooth.qsdialog +import android.bluetooth.BluetoothDevice import com.android.systemui.log.LogBuffer import com.android.systemui.log.core.LogLevel.DEBUG import com.android.systemui.log.dagger.BluetoothTileDialogLog @@ -103,4 +104,29 @@ constructor(@BluetoothTileDialogLog private val logBuffer: LogBuffer) { fun logDeviceUiUpdate(duration: Long) = logBuffer.log(TAG, DEBUG, { long1 = duration }, { "DeviceUiUpdate. duration=$long1" }) + + fun logDeviceClickInAudioSharingWhenEnabled(inAudioSharing: Boolean) { + logBuffer.log( + TAG, + DEBUG, + { str1 = inAudioSharing.toString() }, + { "DeviceClick. in audio sharing=$str1" } + ) + } + + fun logConnectedLeByGroupId(map: Map<Int, List<BluetoothDevice>>) { + logBuffer.log(TAG, DEBUG, { str1 = map.toString() }, { "ConnectedLeByGroupId. map=$str1" }) + } + + fun logLaunchSettingsCriteriaMatched(criteria: String, deviceItem: DeviceItem) { + logBuffer.log( + TAG, + DEBUG, + { + str1 = criteria + str2 = deviceItem.toString() + }, + { "$str1. deviceItem=$str2" } + ) + } } diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogModule.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogModule.kt deleted file mode 100644 index 2e9169e03d80..000000000000 --- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogModule.kt +++ /dev/null @@ -1,29 +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.bluetooth.qsdialog - -import com.android.systemui.dagger.SysUISingleton -import dagger.Binds -import dagger.Module - -@Module -interface BluetoothTileDialogModule { - @Binds - @SysUISingleton - fun bindDeviceItemActionInteractor( - impl: DeviceItemActionInteractorImpl - ): DeviceItemActionInteractor -} diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogUiEvent.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogUiEvent.kt index b592b8ed4332..4a358c0b1292 100644 --- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogUiEvent.kt +++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/BluetoothTileDialogUiEvent.kt @@ -35,7 +35,17 @@ enum class BluetoothTileDialogUiEvent(val metricId: Int) : UiEventLogger.UiEvent CONNECTED_OTHER_DEVICE_DISCONNECT(1508), @UiEvent(doc = "The auto on toggle is clicked") BLUETOOTH_AUTO_ON_TOGGLE_CLICKED(1617), @UiEvent(doc = "The audio sharing button is clicked") - BLUETOOTH_AUDIO_SHARING_BUTTON_CLICKED(1700); + BLUETOOTH_AUDIO_SHARING_BUTTON_CLICKED(1700), + @UiEvent(doc = "Currently broadcasting and a LE audio supported device is clicked") + LAUNCH_SETTINGS_IN_SHARING_LE_DEVICE_CLICKED(1717), + @UiEvent(doc = "Currently broadcasting and a non-LE audio supported device is clicked") + LAUNCH_SETTINGS_IN_SHARING_NON_LE_DEVICE_CLICKED(1718), + @UiEvent( + doc = "Not broadcasting, having one connected, another saved LE audio device is clicked" + ) + LAUNCH_SETTINGS_NOT_SHARING_SAVED_LE_DEVICE_CLICKED(1719), + @UiEvent(doc = "Not broadcasting, one of the two connected LE audio devices is clicked") + LAUNCH_SETTINGS_NOT_SHARING_CONNECTED_LE_DEVICE_CLICKED(1720); override fun getId() = metricId } diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractor.kt index 931176003b1b..4dafa93ab5c2 100644 --- a/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractor.kt @@ -16,32 +16,87 @@ package com.android.systemui.bluetooth.qsdialog +import android.bluetooth.BluetoothDevice +import android.bluetooth.BluetoothProfile +import android.content.Intent +import android.os.Bundle +import android.provider.Settings import com.android.internal.logging.UiEventLogger +import com.android.settingslib.bluetooth.A2dpProfile +import com.android.settingslib.bluetooth.BluetoothUtils +import com.android.settingslib.bluetooth.HeadsetProfile +import com.android.settingslib.bluetooth.HearingAidProfile +import com.android.settingslib.bluetooth.LeAudioProfile +import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast +import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant +import com.android.settingslib.bluetooth.LocalBluetoothManager +import com.android.systemui.animation.DialogTransitionAnimator +import com.android.systemui.bluetooth.qsdialog.DeviceItemActionInteractor.LaunchSettingsCriteria.Companion.getCurrentConnectedLeByGroupId import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background +import com.android.systemui.plugins.ActivityStarter import com.android.systemui.statusbar.phone.SystemUIDialog import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.withContext -/** Defines interface for click handling of a DeviceItem. */ -interface DeviceItemActionInteractor { - suspend fun onClick(deviceItem: DeviceItem, dialog: SystemUIDialog) -} - @SysUISingleton -open class DeviceItemActionInteractorImpl +class DeviceItemActionInteractor @Inject constructor( + private val activityStarter: ActivityStarter, + private val dialogTransitionAnimator: DialogTransitionAnimator, + private val localBluetoothManager: LocalBluetoothManager?, @Background private val backgroundDispatcher: CoroutineDispatcher, private val logger: BluetoothTileDialogLogger, private val uiEventLogger: UiEventLogger, -) : DeviceItemActionInteractor { +) { + private val leAudioProfile: LeAudioProfile? + get() = localBluetoothManager?.profileManager?.leAudioProfile + + private val assistantProfile: LocalBluetoothLeBroadcastAssistant? + get() = localBluetoothManager?.profileManager?.leAudioBroadcastAssistantProfile + + private val launchSettingsCriteriaList: List<LaunchSettingsCriteria> + get() = + listOf( + InSharingClickedNoSource(localBluetoothManager, backgroundDispatcher, logger), + NotSharingClickedNonConnect( + leAudioProfile, + assistantProfile, + backgroundDispatcher, + logger + ), + NotSharingClickedConnected( + leAudioProfile, + assistantProfile, + backgroundDispatcher, + logger + ) + ) - override suspend fun onClick(deviceItem: DeviceItem, dialog: SystemUIDialog) { + suspend fun onClick(deviceItem: DeviceItem, dialog: SystemUIDialog) { withContext(backgroundDispatcher) { logger.logDeviceClick(deviceItem.cachedBluetoothDevice.address, deviceItem.type) + if ( + BluetoothUtils.isAudioSharingEnabled() && + localBluetoothManager != null && + leAudioProfile != null && + assistantProfile != null + ) { + val inAudioSharing = BluetoothUtils.isBroadcasting(localBluetoothManager) + logger.logDeviceClickInAudioSharingWhenEnabled(inAudioSharing) + val criteriaMatched = + launchSettingsCriteriaList.firstOrNull { + it.matched(inAudioSharing, deviceItem) + } + if (criteriaMatched != null) { + uiEventLogger.log(criteriaMatched.getClickUiEvent(deviceItem)) + launchSettings(deviceItem.cachedBluetoothDevice.device, dialog) + return@withContext + } + } deviceItem.cachedBluetoothDevice.apply { when (deviceItem.type) { DeviceItemType.ACTIVE_MEDIA_BLUETOOTH_DEVICE -> { @@ -69,4 +124,184 @@ constructor( } } } + + private fun launchSettings(device: BluetoothDevice, dialog: SystemUIDialog) { + val intent = + Intent(Settings.ACTION_BLUETOOTH_SETTINGS).apply { + putExtra( + EXTRA_SHOW_FRAGMENT_ARGUMENTS, + Bundle().apply { + putParcelable(LocalBluetoothLeBroadcast.EXTRA_BLUETOOTH_DEVICE, device) + } + ) + } + intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TASK + activityStarter.postStartActivityDismissingKeyguard( + intent, + 0, + dialogTransitionAnimator.createActivityTransitionController(dialog) + ) + } + + private interface LaunchSettingsCriteria { + suspend fun matched(inAudioSharing: Boolean, deviceItem: DeviceItem): Boolean + + suspend fun getClickUiEvent(deviceItem: DeviceItem): BluetoothTileDialogUiEvent + + companion object { + suspend fun getCurrentConnectedLeByGroupId( + leAudioProfile: LeAudioProfile, + assistantProfile: LocalBluetoothLeBroadcastAssistant, + @Background backgroundDispatcher: CoroutineDispatcher, + logger: BluetoothTileDialogLogger, + ): Map<Int, List<BluetoothDevice>> { + return withContext(backgroundDispatcher) { + assistantProfile + .getDevicesMatchingConnectionStates( + intArrayOf(BluetoothProfile.STATE_CONNECTED) + ) + ?.filterNotNull() + ?.groupBy { leAudioProfile.getGroupId(it) } + ?.also { logger.logConnectedLeByGroupId(it) } ?: emptyMap() + } + } + } + } + + private class InSharingClickedNoSource( + private val localBluetoothManager: LocalBluetoothManager?, + @Background private val backgroundDispatcher: CoroutineDispatcher, + private val logger: BluetoothTileDialogLogger, + ) : LaunchSettingsCriteria { + // If currently broadcasting and the clicked device is not connected to the source + override suspend fun matched(inAudioSharing: Boolean, deviceItem: DeviceItem): Boolean { + return withContext(backgroundDispatcher) { + val matched = + inAudioSharing && + deviceItem.isMediaDevice && + !BluetoothUtils.hasConnectedBroadcastSource( + deviceItem.cachedBluetoothDevice, + localBluetoothManager + ) + + if (matched) { + logger.logLaunchSettingsCriteriaMatched("InSharingClickedNoSource", deviceItem) + } + + matched + } + } + + override suspend fun getClickUiEvent(deviceItem: DeviceItem) = + if (deviceItem.isLeAudioSupported) + BluetoothTileDialogUiEvent.LAUNCH_SETTINGS_IN_SHARING_LE_DEVICE_CLICKED + else BluetoothTileDialogUiEvent.LAUNCH_SETTINGS_IN_SHARING_NON_LE_DEVICE_CLICKED + } + + private class NotSharingClickedNonConnect( + private val leAudioProfile: LeAudioProfile?, + private val assistantProfile: LocalBluetoothLeBroadcastAssistant?, + @Background private val backgroundDispatcher: CoroutineDispatcher, + private val logger: BluetoothTileDialogLogger, + ) : LaunchSettingsCriteria { + // If not broadcasting, having one device connected, and clicked on a not yet connected LE + // audio device + override suspend fun matched(inAudioSharing: Boolean, deviceItem: DeviceItem): Boolean { + return withContext(backgroundDispatcher) { + val matched = + leAudioProfile?.let { leAudio -> + assistantProfile?.let { assistant -> + !inAudioSharing && + getCurrentConnectedLeByGroupId( + leAudio, + assistant, + backgroundDispatcher, + logger + ) + .size == 1 && + deviceItem.isNotConnectedLeAudioSupported + } + } ?: false + + if (matched) { + logger.logLaunchSettingsCriteriaMatched( + "NotSharingClickedNonConnect", + deviceItem + ) + } + + matched + } + } + + override suspend fun getClickUiEvent(deviceItem: DeviceItem) = + BluetoothTileDialogUiEvent.LAUNCH_SETTINGS_NOT_SHARING_SAVED_LE_DEVICE_CLICKED + } + + private class NotSharingClickedConnected( + private val leAudioProfile: LeAudioProfile?, + private val assistantProfile: LocalBluetoothLeBroadcastAssistant?, + @Background private val backgroundDispatcher: CoroutineDispatcher, + private val logger: BluetoothTileDialogLogger, + ) : LaunchSettingsCriteria { + // If not broadcasting, having two device connected, clicked on any connected LE audio + // devices + override suspend fun matched(inAudioSharing: Boolean, deviceItem: DeviceItem): Boolean { + return withContext(backgroundDispatcher) { + val matched = + leAudioProfile?.let { leAudio -> + assistantProfile?.let { assistant -> + !inAudioSharing && + getCurrentConnectedLeByGroupId( + leAudio, + assistant, + backgroundDispatcher, + logger + ) + .size == 2 && + deviceItem.isActiveOrConnectedLeAudioSupported + } + } ?: false + + if (matched) { + logger.logLaunchSettingsCriteriaMatched( + "NotSharingClickedConnected", + deviceItem + ) + } + + matched + } + } + + override suspend fun getClickUiEvent(deviceItem: DeviceItem) = + BluetoothTileDialogUiEvent.LAUNCH_SETTINGS_NOT_SHARING_CONNECTED_LE_DEVICE_CLICKED + } + + private companion object { + const val EXTRA_SHOW_FRAGMENT_ARGUMENTS = ":settings:show_fragment_args" + + val DeviceItem.isLeAudioSupported: Boolean + get() = + cachedBluetoothDevice.profiles.any { profile -> + profile is LeAudioProfile && profile.isEnabled(cachedBluetoothDevice.device) + } + + val DeviceItem.isNotConnectedLeAudioSupported: Boolean + get() = type == DeviceItemType.SAVED_BLUETOOTH_DEVICE && isLeAudioSupported + + val DeviceItem.isActiveOrConnectedLeAudioSupported: Boolean + get() = + (type == DeviceItemType.ACTIVE_MEDIA_BLUETOOTH_DEVICE || + type == DeviceItemType.AVAILABLE_MEDIA_BLUETOOTH_DEVICE) && isLeAudioSupported + + val DeviceItem.isMediaDevice: Boolean + get() = + cachedBluetoothDevice.connectableProfiles.any { + it is A2dpProfile || + it is HearingAidProfile || + it is LeAudioProfile || + it is HeadsetProfile + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt index 9d110e665ebe..10d6881e4f93 100644 --- a/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/deviceentry/domain/interactor/DeviceEntryInteractor.kt @@ -29,6 +29,7 @@ import com.android.systemui.keyguard.domain.interactor.TrustInteractor import com.android.systemui.scene.domain.interactor.SceneInteractor import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.util.kotlin.Quad +import com.android.systemui.utils.coroutines.flow.mapLatestConflated import javax.inject.Inject import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -37,6 +38,7 @@ import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map @@ -96,7 +98,16 @@ constructor( .filter { currentScene -> currentScene == Scenes.Gone || currentScene == Scenes.Lockscreen } - .map { it == Scenes.Gone } + .mapLatestConflated { scene -> + if (scene == Scenes.Gone) { + // Make sure device unlock status is definitely unlocked before we consider the + // device "entered". + deviceUnlockedInteractor.deviceUnlockStatus.first { it.isUnlocked } + true + } else { + false + } + } .stateIn( scope = applicationScope, started = SharingStarted.Eagerly, diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt index 4ac0c5683d06..9d6c2bf4c1d1 100644 --- a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayRepository.kt @@ -16,6 +16,7 @@ package com.android.systemui.display.data.repository +import android.annotation.SuppressLint import android.hardware.display.DisplayManager import android.hardware.display.DisplayManager.DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED import android.hardware.display.DisplayManager.DisplayListener @@ -27,6 +28,7 @@ import android.util.Log import android.view.Display import com.android.app.tracing.FlowTracing.traceEach import com.android.app.tracing.traceSection +import com.android.systemui.Flags import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Background @@ -42,11 +44,12 @@ import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged -import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.filterIsInstance import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onStart +import kotlinx.coroutines.flow.scan import kotlinx.coroutines.flow.shareIn import kotlinx.coroutines.flow.stateIn @@ -91,6 +94,7 @@ interface DisplayRepository { } @SysUISingleton +@SuppressLint("SharedFlowCreation") class DisplayRepositoryImpl @Inject constructor( @@ -132,44 +136,77 @@ constructor( allDisplayEvents.filterIsInstance<DisplayEvent.Changed>().map { event -> event.displayId } override val displayAdditionEvent: Flow<Display?> = - allDisplayEvents.filterIsInstance<DisplayEvent.Added>().map { - displayManager.getDisplay(it.displayId) - } + allDisplayEvents.filterIsInstance<DisplayEvent.Added>().map { getDisplay(it.displayId) } + + private val oldEnabledDisplays: Flow<Set<Display>> = + allDisplayEvents + .map { getDisplays() } + .shareIn(bgApplicationScope, started = SharingStarted.WhileSubscribed(), replay = 1) + + /** Propagate to the listeners only enabled displays */ + private val enabledDisplayIds: Flow<Set<Int>> = + if (Flags.enableEfficientDisplayRepository()) { + allDisplayEvents + .scan(initial = emptySet()) { previousIds: Set<Int>, event: DisplayEvent -> + val id = event.displayId + when (event) { + is DisplayEvent.Removed -> previousIds - id + is DisplayEvent.Added, + is DisplayEvent.Changed -> previousIds + id + } + } + .shareIn( + bgApplicationScope, + started = SharingStarted.WhileSubscribed(), + replay = 1 + ) + } else { + oldEnabledDisplays.map { enabledDisplaysSet -> + enabledDisplaysSet.map { it.displayId }.toSet() + } + } + .debugLog("enabledDisplayIds") /** * Represents displays that went though the [DisplayListener.onDisplayAdded] callback. * * Those are commonly the ones provided by [DisplayManager.getDisplays] by default. */ - private val enabledDisplays = - allDisplayEvents - .map { getDisplays() } - .shareIn(bgApplicationScope, started = SharingStarted.WhileSubscribed(), replay = 1) + private val enabledDisplays: Flow<Set<Display>> = + if (Flags.enableEfficientDisplayRepository()) { + enabledDisplayIds + .mapElementsLazily { displayId -> getDisplay(displayId) } + .flowOn(backgroundCoroutineDispatcher) + .debugLog("enabledDisplayIds") + } else { + oldEnabledDisplays + } + /** + * Represents displays that went though the [DisplayListener.onDisplayAdded] callback. + * + * Those are commonly the ones provided by [DisplayManager.getDisplays] by default. + */ override val displays: Flow<Set<Display>> = enabledDisplays private fun getDisplays(): Set<Display> = traceSection("$TAG#getDisplays()") { displayManager.displays?.toSet() ?: emptySet() } - /** Propagate to the listeners only enabled displays */ - private val enabledDisplayIds: Flow<Set<Int>> = - enabledDisplays - .map { enabledDisplaysSet -> enabledDisplaysSet.map { it.displayId }.toSet() } - .debugLog("enabledDisplayIds") - private val _ignoredDisplayIds = MutableStateFlow<Set<Int>>(emptySet()) private val ignoredDisplayIds: Flow<Set<Int>> = _ignoredDisplayIds.debugLog("ignoredDisplayIds") private fun getInitialConnectedDisplays(): Set<Int> = - displayManager - .getDisplays(DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED) - .map { it.displayId } - .toSet() - .also { - if (DEBUG) { - Log.d(TAG, "getInitialConnectedDisplays: $it") + traceSection("$TAG#getInitialConnectedDisplays") { + displayManager + .getDisplays(DISPLAY_CATEGORY_ALL_INCLUDING_DISABLED) + .map { it.displayId } + .toSet() + .also { + if (DEBUG) { + Log.d(TAG, "getInitialConnectedDisplays: $it") + } } - } + } /* keeps connected displays until they are disconnected. */ private val connectedDisplayIds: StateFlow<Set<Int>> = @@ -230,6 +267,9 @@ constructor( private fun getDisplayType(displayId: Int): Int? = traceSection("$TAG#getDisplayType") { displayManager.getDisplay(displayId)?.type } + private fun getDisplay(displayId: Int): Display? = + traceSection("$TAG#getDisplay") { displayManager.getDisplay(displayId) } + /** * Pending displays are the ones connected, but not enabled and not ignored. * @@ -307,6 +347,30 @@ constructor( } } + /** + * Maps a set of T to a set of V, minimizing the number of `createValue` calls taking into + * account the diff between each root flow emission. + * + * This is needed to minimize the number of [getDisplay] in this class. Note that if the + * [createValue] returns a null element, it will not be added in the output set. + */ + private fun <T, V> Flow<Set<T>>.mapElementsLazily(createValue: (T) -> V?): Flow<Set<V>> { + var initialSet = emptySet<T>() + val currentMap = mutableMapOf<T, V>() + var resultSet = emptySet<V>() + return onEach { currentSet -> + val removed = initialSet - currentSet + val added = currentSet - initialSet + if (added.isNotEmpty() || removed.isNotEmpty()) { + added.forEach { key: T -> createValue(key)?.let { currentMap[key] = it } } + removed.forEach { key: T -> currentMap.remove(key) } + resultSet = currentMap.values.toSet() // Creates a **copy** of values + } + initialSet = currentSet + } + .map { resultSet } + } + private companion object { const val TAG = "DisplayRepository" val DEBUG = Log.isLoggable(TAG, Log.DEBUG) || Compile.IS_DEBUG diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt index 48660f25907a..a595eebe6c84 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt @@ -50,6 +50,7 @@ import com.android.systemui.shade.data.repository.ShadeRepository import com.android.systemui.statusbar.CommandQueue import com.android.systemui.statusbar.notification.NotificationUtils.interpolate import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor +import com.android.systemui.util.kotlin.Utils.Companion.sample as sampleCombine import com.android.systemui.util.kotlin.Utils.Companion.sampleFilter import com.android.systemui.util.kotlin.pairwise import com.android.systemui.util.kotlin.sample @@ -77,7 +78,7 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.merge import kotlinx.coroutines.flow.onStart import kotlinx.coroutines.flow.stateIn -import com.android.systemui.util.kotlin.Utils.Companion.sample as sampleCombine +import kotlinx.coroutines.flow.transform /** * Encapsulates business-logic related to the keyguard but not to a more specific part within it. @@ -111,7 +112,7 @@ constructor( keyguardTransitionInteractor.transitionState.map { step -> val startingProgress = lastChangeStep.value val progress = step.value - if (step.to == AOD && progress >= startingProgress) { + if (step.to == AOD && progress >= startingProgress && startingProgress < 1f) { val adjustedProgress = ((progress - startingProgress) / (1F - startingProgress)).coerceIn( 0F, @@ -327,28 +328,35 @@ constructor( * This uses legacyShadeExpansion to process swipe up events. In the future, the touch input * signal should be sent directly to transitions. */ - val dismissAlpha: Flow<Float?> = + val dismissAlpha: Flow<Float> = shadeRepository.legacyShadeExpansion - .filter { it < 1f } .sampleCombine( statusBarState, keyguardTransitionInteractor.currentKeyguardState, + keyguardTransitionInteractor.transitionState, isKeyguardDismissible, ) - .map { - (legacyShadeExpansion, statusBarState, currentKeyguardState, isKeyguardDismissible) - -> + .filter { (_, _, _, step, _) -> !step.transitionState.isTransitioning() } + .transform { + ( + legacyShadeExpansion, + statusBarState, + currentKeyguardState, + step, + isKeyguardDismissible) -> if ( statusBarState == StatusBarState.KEYGUARD && isKeyguardDismissible && - currentKeyguardState == LOCKSCREEN + currentKeyguardState == LOCKSCREEN && + legacyShadeExpansion != 1f ) { - MathUtils.constrainedMap(0f, 1f, 0.95f, 1f, legacyShadeExpansion) - } else { - null + emit(MathUtils.constrainedMap(0f, 1f, 0.95f, 1f, legacyShadeExpansion)) + } else if (legacyShadeExpansion == 0f || legacyShadeExpansion == 1f) { + // Resets alpha state + emit(1f) } } - .onStart { emit(null) } + .onStart { emit(1f) } .distinctUntilChanged() val keyguardTranslationY: Flow<Float> = diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt index 3a43b1c480dc..069f65b4efa6 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/WindowManagerLockscreenVisibilityInteractor.kt @@ -105,7 +105,17 @@ constructor( is ObservableTransitionState.Transition -> when { transitionState.fromScene == Scenes.Lockscreen && - transitionState.toScene == Scenes.Gone -> flowOf(true) + transitionState.toScene == Scenes.Gone -> + sceneInteractor + .get() + .isTransitionUserInputOngoing + .flatMapLatestConflated { isUserInputOngoing -> + if (isUserInputOngoing) { + isDeviceEntered + } else { + flowOf(true) + } + } transitionState.fromScene == Scenes.Bouncer && transitionState.toScene == Scenes.Gone -> transitionState.progress.map { progress -> diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt index 940f42377b42..f5c521a3d8c7 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModel.kt @@ -32,6 +32,7 @@ import com.android.systemui.res.R import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.shade.shared.model.ShadeMode import com.android.systemui.statusbar.notification.domain.interactor.NotificationsKeyguardInteractor +import com.android.systemui.statusbar.notification.icon.ui.viewmodel.NotificationIconContainerAlwaysOnDisplayViewModel import com.android.systemui.statusbar.ui.SystemBarUtilsProxy import javax.inject.Inject import kotlinx.coroutines.CoroutineScope @@ -48,6 +49,7 @@ class KeyguardClockViewModel constructor( keyguardClockInteractor: KeyguardClockInteractor, @Application private val applicationScope: CoroutineScope, + aodNotificationIconViewModel: NotificationIconContainerAlwaysOnDisplayViewModel, notifsKeyguardInteractor: NotificationsKeyguardInteractor, @get:VisibleForTesting val shadeInteractor: ShadeInteractor, private val systemBarUtils: SystemBarUtilsProxy, @@ -105,8 +107,13 @@ constructor( initialValue = false ) - val isAodIconsVisible: StateFlow<Boolean> = - notifsKeyguardInteractor.areNotificationsFullyHidden.stateIn( + // To translate elements below smartspace in weather clock to avoid overlapping between date + // element in weather clock and aod icons + val isAodIconsVisible: StateFlow<Boolean> = combine(aodNotificationIconViewModel.icons.map { + it.visibleIcons.isNotEmpty() + }, notifsKeyguardInteractor.areNotificationsFullyHidden) { hasIcons, visible -> + hasIcons && visible + }.stateIn( scope = applicationScope, started = SharingStarted.WhileSubscribed(), initialValue = false diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt index 5027524e7a4b..aefff7d0f79b 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardRootViewModel.kt @@ -66,7 +66,6 @@ import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combineTransform import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filter -import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.merge import kotlinx.coroutines.flow.onStart @@ -236,7 +235,7 @@ constructor( // value emitted by any of them. Do not add flows that cannot make this guarantee. merge( alphaOnShadeExpansion, - keyguardInteractor.dismissAlpha.filterNotNull(), + keyguardInteractor.dismissAlpha, alternateBouncerToGoneTransitionViewModel.lockscreenAlpha(viewState), aodToGoneTransitionViewModel.lockscreenAlpha(viewState), aodToLockscreenTransitionViewModel.lockscreenAlpha(viewState), diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/data/repository/MediaFilterRepository.kt b/packages/SystemUI/src/com/android/systemui/media/controls/data/repository/MediaFilterRepository.kt index 220d32663098..6a91d1b15325 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/data/repository/MediaFilterRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/data/repository/MediaFilterRepository.kt @@ -218,7 +218,7 @@ constructor( mediaFromRecPackageName = null _currentMedia.value = sortedMap.values.toList() } - } else if (sortedMap.size > sortedMedia.size && it.active) { + } else if (sortedMap.size > _currentMedia.value.size && it.active) { _currentMedia.value = sortedMap.values.toList() } else { // When loading an update for an existing media control. diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaItem.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaItem.java index fbb84defc372..4496b258bde4 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaItem.java +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaItem.java @@ -16,7 +16,9 @@ package com.android.systemui.media.dialog; -import androidx.annotation.IntDef; +import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; import com.android.settingslib.media.MediaDevice; import com.android.systemui.res.R; @@ -46,40 +48,50 @@ public class MediaItem { int TYPE_PAIR_NEW_DEVICE = 2; } - public MediaItem() { - this.mMediaDeviceOptional = Optional.empty(); - this.mTitle = null; - this.mMediaItemType = MediaItemType.TYPE_PAIR_NEW_DEVICE; + /** + * Returns a new {@link MediaItemType#TYPE_DEVICE} {@link MediaItem} with its {@link + * #getMediaDevice() media device} set to {@code device} and its title set to {@code device}'s + * name. + */ + public static MediaItem createDeviceMediaItem(@NonNull MediaDevice device) { + return new MediaItem(device, device.getName(), MediaItemType.TYPE_DEVICE); } - public MediaItem(String title, int mediaItemType) { - this.mMediaDeviceOptional = Optional.empty(); - this.mTitle = title; - this.mMediaItemType = mediaItemType; + /** + * Returns a new {@link MediaItemType#TYPE_PAIR_NEW_DEVICE} {@link MediaItem} with both {@link + * #getMediaDevice() media device} and title set to {@code null}. + */ + public static MediaItem createPairNewDeviceMediaItem() { + return new MediaItem( + /* device */ null, /* title */ null, MediaItemType.TYPE_PAIR_NEW_DEVICE); + } + + /** + * Returns a new {@link MediaItemType#TYPE_GROUP_DIVIDER} {@link MediaItem} with the specified + * title and a {@code null} {@link #getMediaDevice() media device}. + */ + public static MediaItem createGroupDividerMediaItem(@Nullable String title) { + return new MediaItem(/* device */ null, title, MediaItemType.TYPE_GROUP_DIVIDER); } - public MediaItem(MediaDevice mediaDevice) { - this.mMediaDeviceOptional = Optional.of(mediaDevice); - this.mTitle = mediaDevice.getName(); - this.mMediaItemType = MediaItemType.TYPE_DEVICE; + private MediaItem( + @Nullable MediaDevice device, @Nullable String title, @MediaItemType int type) { + this.mMediaDeviceOptional = Optional.ofNullable(device); + this.mTitle = title; + this.mMediaItemType = type; } public Optional<MediaDevice> getMediaDevice() { return mMediaDeviceOptional; } - /** - * Get layout id based on media item Type. - */ - public static int getMediaLayoutId(int mediaItemType) { - switch (mediaItemType) { - case MediaItemType.TYPE_DEVICE: - case MediaItemType.TYPE_PAIR_NEW_DEVICE: - return R.layout.media_output_list_item_advanced; - case MediaItemType.TYPE_GROUP_DIVIDER: - default: - return R.layout.media_output_list_group_divider; - } + /** Get layout id based on media item Type. */ + public static int getMediaLayoutId(@MediaItemType int mediaItemType) { + return switch (mediaItemType) { + case MediaItemType.TYPE_DEVICE, MediaItemType.TYPE_PAIR_NEW_DEVICE -> + R.layout.media_output_list_item_advanced; + default -> R.layout.media_output_list_group_divider; + }; } public String getTitle() { diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java index c2cfdbe410b8..1e8656311f1d 100644 --- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java +++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java @@ -68,6 +68,7 @@ import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import androidx.core.graphics.drawable.IconCompat; +import com.android.internal.annotations.GuardedBy; import com.android.settingslib.RestrictedLockUtilsInternal; import com.android.settingslib.Utils; import com.android.settingslib.bluetooth.BluetoothUtils; @@ -656,10 +657,16 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback, if (DEBUG) { Log.d(TAG, "No connected media device or muting expected device exist."); } - return categorizeMediaItems(null, devices, needToHandleMutingExpectedDevice); + return categorizeMediaItemsLocked( + /* connectedMediaDevice */ null, + devices, + needToHandleMutingExpectedDevice); } // selected device exist - return categorizeMediaItems(connectedMediaDevice, devices, false); + return categorizeMediaItemsLocked( + connectedMediaDevice, + devices, + /* needToHandleMutingExpectedDevice */ false); } // To keep the same list order final List<MediaDevice> targetMediaDevices = new ArrayList<>(); @@ -682,8 +689,9 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback, devices.removeAll(targetMediaDevices); targetMediaDevices.addAll(devices); } - List<MediaItem> finalMediaItems = targetMediaDevices.stream().map( - MediaItem::new).collect(Collectors.toList()); + List<MediaItem> finalMediaItems = targetMediaDevices.stream() + .map(MediaItem::createDeviceMediaItem) + .collect(Collectors.toList()); dividerItems.forEach(finalMediaItems::add); attachConnectNewDeviceItemIfNeeded(finalMediaItems); return finalMediaItems; @@ -694,51 +702,50 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback, * Initial categorization of current devices, will not be called for updates to the devices * list. */ - private List<MediaItem> categorizeMediaItems(MediaDevice connectedMediaDevice, + @GuardedBy("mMediaDevicesLock") + private List<MediaItem> categorizeMediaItemsLocked(MediaDevice connectedMediaDevice, List<MediaDevice> devices, boolean needToHandleMutingExpectedDevice) { - synchronized (mMediaDevicesLock) { - List<MediaItem> finalMediaItems = new ArrayList<>(); - Set<String> selectedDevicesIds = getSelectedMediaDevice().stream().map( - MediaDevice::getId).collect(Collectors.toSet()); - if (connectedMediaDevice != null) { - selectedDevicesIds.add(connectedMediaDevice.getId()); - } - boolean suggestedDeviceAdded = false; - boolean displayGroupAdded = false; - for (MediaDevice device : devices) { - if (needToHandleMutingExpectedDevice && device.isMutingExpectedDevice()) { - finalMediaItems.add(0, new MediaItem(device)); - } else if (!needToHandleMutingExpectedDevice && selectedDevicesIds.contains( - device.getId())) { - finalMediaItems.add(0, new MediaItem(device)); - } else { - if (device.isSuggestedDevice() && !suggestedDeviceAdded) { - attachGroupDivider(finalMediaItems, mContext.getString( - R.string.media_output_group_title_suggested_device)); - suggestedDeviceAdded = true; - } else if (!device.isSuggestedDevice() && !displayGroupAdded) { - attachGroupDivider(finalMediaItems, mContext.getString( - R.string.media_output_group_title_speakers_and_displays)); - displayGroupAdded = true; - } - finalMediaItems.add(new MediaItem(device)); + List<MediaItem> finalMediaItems = new ArrayList<>(); + Set<String> selectedDevicesIds = getSelectedMediaDevice().stream() + .map(MediaDevice::getId) + .collect(Collectors.toSet()); + if (connectedMediaDevice != null) { + selectedDevicesIds.add(connectedMediaDevice.getId()); + } + boolean suggestedDeviceAdded = false; + boolean displayGroupAdded = false; + for (MediaDevice device : devices) { + if (needToHandleMutingExpectedDevice && device.isMutingExpectedDevice()) { + finalMediaItems.add(0, MediaItem.createDeviceMediaItem(device)); + } else if (!needToHandleMutingExpectedDevice && selectedDevicesIds.contains( + device.getId())) { + finalMediaItems.add(0, MediaItem.createDeviceMediaItem(device)); + } else { + if (device.isSuggestedDevice() && !suggestedDeviceAdded) { + attachGroupDivider(finalMediaItems, mContext.getString( + R.string.media_output_group_title_suggested_device)); + suggestedDeviceAdded = true; + } else if (!device.isSuggestedDevice() && !displayGroupAdded) { + attachGroupDivider(finalMediaItems, mContext.getString( + R.string.media_output_group_title_speakers_and_displays)); + displayGroupAdded = true; } + finalMediaItems.add(MediaItem.createDeviceMediaItem(device)); } - attachConnectNewDeviceItemIfNeeded(finalMediaItems); - return finalMediaItems; } + attachConnectNewDeviceItemIfNeeded(finalMediaItems); + return finalMediaItems; } private void attachGroupDivider(List<MediaItem> mediaItems, String title) { - mediaItems.add( - new MediaItem(title, MediaItem.MediaItemType.TYPE_GROUP_DIVIDER)); + mediaItems.add(MediaItem.createGroupDividerMediaItem(title)); } private void attachConnectNewDeviceItemIfNeeded(List<MediaItem> mediaItems) { // Attach "Connect a device" item only when current output is not remote and not a group if (!isCurrentConnectedDeviceRemote() && getSelectedMediaDevice().size() == 1) { - mediaItems.add(new MediaItem()); + mediaItems.add(MediaItem.createPairNewDeviceMediaItem()); } } diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/data/model/MediaProjectionState.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/data/model/MediaProjectionState.kt index 1d5f6f52000c..de300b2ff900 100644 --- a/packages/SystemUI/src/com/android/systemui/mediaprojection/data/model/MediaProjectionState.kt +++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/data/model/MediaProjectionState.kt @@ -20,7 +20,21 @@ import android.app.ActivityManager.RunningTaskInfo /** Represents the state of media projection. */ sealed interface MediaProjectionState { - object NotProjecting : MediaProjectionState - object EntireScreen : MediaProjectionState - data class SingleTask(val task: RunningTaskInfo) : MediaProjectionState + /** There is no media being projected. */ + data object NotProjecting : MediaProjectionState + + /** + * Media is currently being projected. + * + * @property hostPackage the package name of the app that is receiving the content of the media + * projection (aka which app the phone screen contents are being sent to). + */ + sealed class Projecting(open val hostPackage: String) : MediaProjectionState { + /** The entire screen is being projected. */ + data class EntireScreen(override val hostPackage: String) : Projecting(hostPackage) + + /** Only a single task is being projected. */ + data class SingleTask(override val hostPackage: String, val task: RunningTaskInfo) : + Projecting(hostPackage) + } } diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepository.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepository.kt index 3ce0a1e00910..8a9adc7a5c88 100644 --- a/packages/SystemUI/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepository.kt @@ -64,6 +64,10 @@ constructor( } } + override suspend fun stopProjecting() { + withContext(backgroundDispatcher) { mediaProjectionManager.stopActiveProjection() } + } + override val mediaProjectionState: Flow<MediaProjectionState> = conflatedCallbackFlow { val callback = @@ -83,7 +87,9 @@ constructor( session: ContentRecordingSession? ) { Log.d(TAG, "MediaProjectionManager.Callback#onSessionStarted: $session") - launch { trySendWithFailureLogging(stateForSession(session), TAG) } + launch { + trySendWithFailureLogging(stateForSession(info, session), TAG) + } } } mediaProjectionManager.addCallback(callback, handler) @@ -95,19 +101,23 @@ constructor( initialValue = MediaProjectionState.NotProjecting, ) - private suspend fun stateForSession(session: ContentRecordingSession?): MediaProjectionState { + private suspend fun stateForSession( + info: MediaProjectionInfo, + session: ContentRecordingSession? + ): MediaProjectionState { if (session == null) { return MediaProjectionState.NotProjecting } + + val hostPackage = info.packageName if (session.contentToRecord == RECORD_CONTENT_DISPLAY || session.tokenToRecord == null) { - return MediaProjectionState.EntireScreen + return MediaProjectionState.Projecting.EntireScreen(hostPackage) } val matchingTask = tasksRepository.findRunningTaskFromWindowContainerToken( checkNotNull(session.tokenToRecord) - ) - ?: return MediaProjectionState.EntireScreen - return MediaProjectionState.SingleTask(matchingTask) + ) ?: return MediaProjectionState.Projecting.EntireScreen(hostPackage) + return MediaProjectionState.Projecting.SingleTask(hostPackage, matchingTask) } companion object { diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionRepository.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionRepository.kt index 21300dbff929..50182d7a3b33 100644 --- a/packages/SystemUI/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionRepository.kt @@ -26,6 +26,9 @@ interface MediaProjectionRepository { /** Switches the task that should be projected. */ suspend fun switchProjectedTask(task: RunningTaskInfo) + /** Stops the currently active projection. */ + suspend fun stopProjecting() + /** Represents the current [MediaProjectionState]. */ val mediaProjectionState: Flow<MediaProjectionState> } diff --git a/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/domain/interactor/TaskSwitchInteractor.kt b/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/domain/interactor/TaskSwitchInteractor.kt index c232d4d16294..118639c521f6 100644 --- a/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/domain/interactor/TaskSwitchInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/mediaprojection/taskswitcher/domain/interactor/TaskSwitchInteractor.kt @@ -57,7 +57,7 @@ constructor( mediaProjectionRepository.mediaProjectionState.flatMapLatest { projectionState -> Log.d(TAG, "MediaProjectionState -> $projectionState") when (projectionState) { - is MediaProjectionState.SingleTask -> { + is MediaProjectionState.Projecting.SingleTask -> { val projectedTask = projectionState.task tasksRepository.foregroundTask.map { foregroundTask -> if (hasForegroundTaskSwitched(projectedTask, foregroundTask)) { @@ -67,7 +67,7 @@ constructor( } } } - is MediaProjectionState.EntireScreen, + is MediaProjectionState.Projecting.EntireScreen, is MediaProjectionState.NotProjecting -> { flowOf(TaskSwitchState.NotProjectingTask) } diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java index 0e819c2b0a4f..07289cb7b809 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java @@ -2028,12 +2028,15 @@ public class NavigationBar extends ViewController<NavigationBarView> implements getBarTransitions().setBackgroundOverrideAlpha(1f); } } - updateScreenPinningGestures(); + + // Update the window layout params when the nav mode changes as that will affect the + // system gesture insets + setNavBarMode(mode); + repositionNavigationBar(mCurrentRotation); if (!canShowSecondaryHandle()) { resetSecondaryHandle(); } - setNavBarMode(mode); mView.setShouldShowSwipeUpUi(mOverviewProxyService.shouldShowSwipeUpUI()); } }; diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java index 41cd2c46b998..99c95b54a4ca 100644 --- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java +++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java @@ -24,6 +24,7 @@ import static com.android.systemui.Flags.edgebackGestureHandlerGetRunningTasksBa import static com.android.systemui.classifier.Classifier.BACK_GESTURE; import static com.android.systemui.navigationbar.gestural.Utilities.isTrackpadScroll; import static com.android.systemui.navigationbar.gestural.Utilities.isTrackpadThreeFingerSwipe; +import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_TOUCHPAD_GESTURES_DISABLED; import static java.util.stream.Collectors.joining; @@ -1021,8 +1022,10 @@ public class EdgeBackGestureHandler implements PluginListener<NavigationEdgeBack if (mIsTrackpadThreeFingerSwipe) { // Trackpad back gestures don't have zones, so we don't need to check if the down // event is within insets. - mAllowGesture = isBackAllowedCommon && isValidTrackpadBackGesture( - true /* isTrackpadEvent */); + boolean trackpadGesturesEnabled = + (mSysUiFlags & SYSUI_STATE_TOUCHPAD_GESTURES_DISABLED) == 0; + mAllowGesture = isBackAllowedCommon && trackpadGesturesEnabled + && isValidTrackpadBackGesture(true /* isTrackpadEvent */); } else { mAllowGesture = isBackAllowedCommon && !mUsingThreeButtonNav && isWithinInsets && isWithinTouchRegion((int) ev.getX(), (int) ev.getY()) diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java index ea89be61d773..b705a0389300 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java +++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java @@ -21,7 +21,6 @@ import static com.android.systemui.qs.dagger.QSFlagsModule.RBC_AVAILABLE; import android.content.Context; import android.os.Handler; -import com.android.systemui.bluetooth.qsdialog.BluetoothTileDialogModule; import com.android.systemui.dagger.NightDisplayListenerModule; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Background; @@ -61,7 +60,6 @@ import javax.inject.Named; */ @Module(subcomponents = {QSFragmentComponent.class, QSSceneComponent.class}, includes = { - BluetoothTileDialogModule.class, MediaModule.class, PanelsModule.class, QSExternalModule.class, diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractor.kt b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractor.kt index 79720c1d8df6..5637115cd6da 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/impl/screenrecord/domain/interactor/ScreenRecordTileUserActionInteractor.kt @@ -35,6 +35,7 @@ import com.android.systemui.qs.tiles.base.interactor.QSTileUserActionInteractor import com.android.systemui.qs.tiles.viewmodel.QSTileUserAction import com.android.systemui.screenrecord.RecordingController import com.android.systemui.screenrecord.data.model.ScreenRecordModel +import com.android.systemui.screenrecord.data.repository.ScreenRecordRepository import com.android.systemui.statusbar.phone.KeyguardDismissUtil import javax.inject.Inject import kotlin.coroutines.CoroutineContext @@ -47,6 +48,7 @@ constructor( @Application private val context: Context, @Main private val mainContext: CoroutineContext, @Background private val backgroundContext: CoroutineContext, + private val screenRecordRepository: ScreenRecordRepository, private val recordingController: RecordingController, private val keyguardInteractor: KeyguardInteractor, private val keyguardDismissUtil: KeyguardDismissUtil, @@ -65,8 +67,7 @@ constructor( Log.d(TAG, "Cancelling countdown") withContext(backgroundContext) { recordingController.cancelCountdown() } } - is ScreenRecordModel.Recording -> - withContext(backgroundContext) { recordingController.stopRecording() } + is ScreenRecordModel.Recording -> screenRecordRepository.stopRecording() is ScreenRecordModel.DoingNothing -> withContext(mainContext) { showPrompt(action.expandable, user.identifier) @@ -122,8 +123,7 @@ constructor( controller, animateBackgroundBoundsChange = true, ) - } - ?: dialog.show() + } ?: dialog.show() } else { dialog.show() } diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt index 0304e730cb68..1e689bd11fa3 100644 --- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt +++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SceneContainerStartable.kt @@ -39,6 +39,7 @@ import com.android.systemui.deviceentry.domain.interactor.DeviceEntryInteractor import com.android.systemui.deviceentry.domain.interactor.DeviceUnlockedInteractor import com.android.systemui.deviceentry.shared.model.DeviceUnlockSource import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor +import com.android.systemui.keyguard.domain.interactor.WindowManagerLockscreenVisibilityInteractor import com.android.systemui.model.SceneContainerPlugin import com.android.systemui.model.SysUiState import com.android.systemui.model.updateFlags @@ -81,6 +82,7 @@ import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.filterIsInstance import kotlinx.coroutines.flow.filterNot +import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map @@ -120,6 +122,7 @@ constructor( private val uiEventLogger: UiEventLogger, private val sceneBackInteractor: SceneBackInteractor, private val shadeSessionStorage: SessionStorage, + private val windowMgrLockscreenVisInteractor: WindowManagerLockscreenVisibilityInteractor, ) : CoreStartable { private val centralSurfaces: CentralSurfaces? get() = centralSurfacesOptLazy.get().getOrNull() @@ -227,6 +230,25 @@ constructor( handleDeviceUnlockStatus() handlePowerState() handleShadeTouchability() + handleSurfaceBehindKeyguardVisibility() + } + + private fun handleSurfaceBehindKeyguardVisibility() { + applicationScope.launch { + sceneInteractor.currentScene.collectLatest { currentScene -> + if (currentScene == Scenes.Lockscreen) { + // Wait for surface to become visible + windowMgrLockscreenVisInteractor.surfaceBehindVisibility.first { it } + // Make sure the device is actually unlocked before force-changing the scene + deviceUnlockedInteractor.deviceUnlockStatus.first { it.isUnlocked } + // Override the current transition, if any, by forcing the scene to Gone + sceneInteractor.changeScene( + toScene = Scenes.Gone, + loggingReason = "surface behind keyguard is visible", + ) + } + } + } } private fun handleBouncerImeVisibility() { @@ -329,8 +351,7 @@ constructor( Scenes.Gone to "device was unlocked in Bouncer scene" } else { val prevScene = previousScene.value - (prevScene - ?: Scenes.Gone) to + (prevScene ?: Scenes.Gone) to "device was unlocked in Bouncer scene, from sceneKey=$prevScene" } isOnLockscreen -> diff --git a/packages/SystemUI/src/com/android/systemui/screenrecord/data/repository/ScreenRecordRepository.kt b/packages/SystemUI/src/com/android/systemui/screenrecord/data/repository/ScreenRecordRepository.kt index d59d220abe3f..9eeb3b9576d8 100644 --- a/packages/SystemUI/src/com/android/systemui/screenrecord/data/repository/ScreenRecordRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/screenrecord/data/repository/ScreenRecordRepository.kt @@ -28,6 +28,7 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.onStart +import kotlinx.coroutines.withContext /** * Repository storing information about the state of screen recording. @@ -38,6 +39,9 @@ import kotlinx.coroutines.flow.onStart interface ScreenRecordRepository { /** The current screen recording state. Note that this is a cold flow. */ val screenRecordState: Flow<ScreenRecordModel> + + /** Stops the recording. */ + suspend fun stopRecording() } @SysUISingleton @@ -90,4 +94,8 @@ constructor( ScreenRecordModel.DoingNothing } } + + override suspend fun stopRecording() { + withContext(bgCoroutineContext) { recordingController.stopRecording() } + } } diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelUnfoldAnimationController.kt b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelUnfoldAnimationController.kt index 14659e7909ab..f463cb5bcbd6 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelUnfoldAnimationController.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelUnfoldAnimationController.kt @@ -47,6 +47,7 @@ constructor( viewsIdToTranslate = setOf( ViewIdToTranslate(R.id.quick_settings_panel, START, filterShade), + ViewIdToTranslate(R.id.qs_footer_actions, START, filterShade), ViewIdToTranslate(R.id.notification_stack_scroller, END, filterShade)), progressProvider = progressProvider) } @@ -55,9 +56,8 @@ constructor( UnfoldConstantTranslateAnimator( viewsIdToTranslate = setOf( - ViewIdToTranslate(R.id.statusIcons, END, filterShade), + ViewIdToTranslate(R.id.shade_header_system_icons, END, filterShade), ViewIdToTranslate(R.id.privacy_container, END, filterShade), - ViewIdToTranslate(R.id.batteryRemainingIcon, END, filterShade), ViewIdToTranslate(R.id.carrier_group, END, filterShade), ViewIdToTranslate(R.id.clock, START, filterShade), ViewIdToTranslate(R.id.date, START, filterShade) diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java index 3826b50ab024..262befc1e9ad 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java @@ -537,8 +537,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump private final KeyguardMediaController mKeyguardMediaController; private final Optional<KeyguardUnfoldTransition> mKeyguardUnfoldTransition; - private final Optional<NotificationPanelUnfoldAnimationController> - mNotificationPanelUnfoldAnimationController; /** The drag distance required to fully expand the split shade. */ private int mSplitShadeFullTransitionDistance; @@ -964,8 +962,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump mKeyguardUnfoldTransition = unfoldComponent.map( SysUIUnfoldComponent::getKeyguardUnfoldTransition); - mNotificationPanelUnfoldAnimationController = unfoldComponent.map( - SysUIUnfoldComponent::getNotificationPanelUnfoldAnimationController); updateUserSwitcherFlags(); mKeyguardBottomAreaViewModel = keyguardBottomAreaViewModel; @@ -1131,9 +1127,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump mShadeHeaderController.init(); mShadeHeaderController.setShadeCollapseAction( () -> collapse(/* delayed= */ false , /* speedUpFactor= */ 1.0f)); - mKeyguardUnfoldTransition.ifPresent(u -> u.setup(mView)); - mNotificationPanelUnfoldAnimationController.ifPresent(controller -> - controller.setup(mNotificationContainerParent)); // Dreaming->Lockscreen collectFlow( @@ -1511,9 +1504,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump if (!KeyguardBottomAreaRefactor.isEnabled()) { setKeyguardBottomAreaVisibility(mBarState, false); } - - mKeyguardUnfoldTransition.ifPresent(u -> u.setup(mView)); - mNotificationPanelUnfoldAnimationController.ifPresent(u -> u.setup(mView)); } private void attachSplitShadeMediaPlayerContainer(FrameLayout container) { @@ -1797,6 +1787,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump private void updateKeyguardStatusViewAlignment(boolean animate) { boolean shouldBeCentered = shouldKeyguardStatusViewBeCentered(); + mKeyguardUnfoldTransition.ifPresent(t -> t.setStatusViewCentered(shouldBeCentered)); if (MigrateClocksToBlueprint.isEnabled()) { mKeyguardInteractor.setClockShouldBeCentered(shouldBeCentered); return; @@ -1804,7 +1795,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump ConstraintLayout layout = mNotificationContainerParent; mKeyguardStatusViewController.updateAlignment( layout, mSplitShadeEnabled, shouldBeCentered, animate); - mKeyguardUnfoldTransition.ifPresent(t -> t.setStatusViewCentered(shouldBeCentered)); } private boolean shouldKeyguardStatusViewBeCentered() { diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java index 1df085b24cdc..e41f99b99e35 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java @@ -32,6 +32,7 @@ import android.view.ViewGroup; import com.android.internal.annotations.VisibleForTesting; import com.android.keyguard.AuthKeyguardMessageArea; +import com.android.keyguard.KeyguardUnfoldTransition; import com.android.keyguard.LockIconViewController; import com.android.systemui.Dumpable; import com.android.systemui.animation.ActivityTransitionAnimator; @@ -72,6 +73,7 @@ import com.android.systemui.statusbar.phone.DozeServiceHost; import com.android.systemui.statusbar.phone.PhoneStatusBarViewController; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; import com.android.systemui.statusbar.window.StatusBarWindowStateController; +import com.android.systemui.unfold.SysUIUnfoldComponent; import com.android.systemui.unfold.UnfoldTransitionProgressProvider; import com.android.systemui.util.time.SystemClock; @@ -174,6 +176,7 @@ public class NotificationShadeWindowViewController implements Dumpable { DozeScrimController dozeScrimController, NotificationShadeWindowController controller, Optional<UnfoldTransitionProgressProvider> unfoldTransitionProgressProvider, + Optional<SysUIUnfoldComponent> unfoldComponent, KeyguardUnlockAnimationController keyguardUnlockAnimationController, NotificationInsetsController notificationInsetsController, AmbientState ambientState, @@ -234,6 +237,14 @@ public class NotificationShadeWindowViewController implements Dumpable { notificationLaunchAnimationInteractor.isLaunchAnimationRunning(), this::setExpandAnimationRunning); + var keyguardUnfoldTransition = unfoldComponent.map( + SysUIUnfoldComponent::getKeyguardUnfoldTransition); + var notificationPanelUnfoldAnimationController = unfoldComponent.map( + SysUIUnfoldComponent::getNotificationPanelUnfoldAnimationController); + + keyguardUnfoldTransition.ifPresent(KeyguardUnfoldTransition::setup); + notificationPanelUnfoldAnimationController.ifPresent(u -> u.setup(mView)); + mClock = clock; if (featureFlagsClassic.isEnabled(Flags.SPLIT_SHADE_SUBPIXEL_OPTIMIZATION)) { unfoldTransitionProgressProvider.ifPresent( diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt index 6c7606170d9f..b2e0cd04687c 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModel.kt @@ -30,6 +30,9 @@ import com.android.systemui.plugins.ActivityStarter import com.android.systemui.privacy.OngoingPrivacyChip import com.android.systemui.privacy.PrivacyItem import com.android.systemui.res.R +import com.android.systemui.scene.domain.interactor.SceneInteractor +import com.android.systemui.scene.shared.model.SceneFamilies +import com.android.systemui.scene.shared.model.TransitionKeys import com.android.systemui.shade.domain.interactor.PrivacyChipInteractor import com.android.systemui.shade.domain.interactor.ShadeHeaderClockInteractor import com.android.systemui.shade.domain.interactor.ShadeInteractor @@ -57,6 +60,7 @@ constructor( @Application private val applicationScope: CoroutineScope, context: Context, private val activityStarter: ActivityStarter, + private val sceneInteractor: SceneInteractor, shadeInteractor: ShadeInteractor, mobileIconsInteractor: MobileIconsInteractor, val mobileIconsViewModel: MobileIconsViewModel, @@ -139,6 +143,15 @@ constructor( clockInteractor.launchClockActivity() } + /** Notifies that the system icons container was clicked. */ + fun onSystemIconContainerClicked() { + sceneInteractor.changeScene( + SceneFamilies.Home, + "ShadeHeaderViewModel.onSystemIconContainerClicked", + TransitionKeys.SlightlyFasterShadeCollapse, + ) + } + /** Notifies that the shadeCarrierGroup was clicked. */ fun onShadeCarrierGroupClicked() { activityStarter.postStartActivityDismissingKeyguard( diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/domain/interactor/OngoingActivityChipInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/domain/interactor/OngoingActivityChipInteractor.kt index c3d37fb3d952..086a32dfe49c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/domain/interactor/OngoingActivityChipInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/domain/interactor/OngoingActivityChipInteractor.kt @@ -16,11 +16,37 @@ package com.android.systemui.statusbar.chips.domain.interactor +import android.view.View +import com.android.systemui.animation.DialogTransitionAnimator +import com.android.systemui.res.R import com.android.systemui.statusbar.chips.domain.model.OngoingActivityChipModel +import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer +import com.android.systemui.statusbar.phone.SystemUIDialog import kotlinx.coroutines.flow.StateFlow /** Interface for an interactor that knows the state of a single type of ongoing activity chip. */ interface OngoingActivityChipInteractor { /** A flow modeling the chip that should be shown. */ val chip: StateFlow<OngoingActivityChipModel> + + companion object { + /** Creates a chip click listener that launches a dialog created by [dialogDelegate]. */ + fun createDialogLaunchOnClickListener( + dialogDelegate: SystemUIDialog.Delegate, + dialogTransitionAnimator: DialogTransitionAnimator, + ): View.OnClickListener { + return View.OnClickListener { view -> + val dialog = dialogDelegate.createDialog() + val launchableView = + view.requireViewById<ChipBackgroundContainer>( + R.id.ongoing_activity_chip_background + ) + // TODO(b/343699052): This makes a beautiful animate-in, but the + // animate-out looks odd because the dialog animates back into the chip + // but then the chip disappears. If we aren't able to address + // b/343699052 in time for launch, we should just use `dialog.show`. + dialogTransitionAnimator.showFromView(dialog, launchableView) + } + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractor.kt index ac16d26e415c..6611434b661e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractor.kt @@ -16,6 +16,9 @@ package com.android.systemui.statusbar.chips.mediaprojection.domain.interactor +import android.content.pm.PackageManager +import androidx.annotation.DrawableRes +import com.android.systemui.animation.DialogTransitionAnimator import com.android.systemui.common.shared.model.ContentDescription import com.android.systemui.common.shared.model.Icon import com.android.systemui.dagger.SysUISingleton @@ -24,7 +27,12 @@ import com.android.systemui.mediaprojection.data.model.MediaProjectionState import com.android.systemui.mediaprojection.data.repository.MediaProjectionRepository import com.android.systemui.res.R import com.android.systemui.statusbar.chips.domain.interactor.OngoingActivityChipInteractor +import com.android.systemui.statusbar.chips.domain.interactor.OngoingActivityChipInteractor.Companion.createDialogLaunchOnClickListener import com.android.systemui.statusbar.chips.domain.model.OngoingActivityChipModel +import com.android.systemui.statusbar.chips.mediaprojection.ui.view.EndCastToOtherDeviceDialogDelegate +import com.android.systemui.statusbar.chips.mediaprojection.ui.view.EndShareToAppDialogDelegate +import com.android.systemui.statusbar.phone.SystemUIDialog +import com.android.systemui.util.Utils import com.android.systemui.util.time.SystemClock import javax.inject.Inject import kotlinx.coroutines.CoroutineScope @@ -32,6 +40,7 @@ import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.launch /** * Interactor for media-projection-related chips in the status bar. @@ -47,33 +56,88 @@ import kotlinx.coroutines.flow.stateIn class MediaProjectionChipInteractor @Inject constructor( - @Application scope: CoroutineScope, - mediaProjectionRepository: MediaProjectionRepository, - val systemClock: SystemClock, + @Application private val scope: CoroutineScope, + private val mediaProjectionRepository: MediaProjectionRepository, + private val packageManager: PackageManager, + private val systemClock: SystemClock, + private val dialogFactory: SystemUIDialog.Factory, + private val dialogTransitionAnimator: DialogTransitionAnimator, ) : OngoingActivityChipInteractor { override val chip: StateFlow<OngoingActivityChipModel> = mediaProjectionRepository.mediaProjectionState .map { state -> when (state) { is MediaProjectionState.NotProjecting -> OngoingActivityChipModel.Hidden - is MediaProjectionState.EntireScreen, - is MediaProjectionState.SingleTask -> { - // TODO(b/332662551): Distinguish between cast-to-other-device and - // share-to-app. - OngoingActivityChipModel.Shown( - icon = - Icon.Resource( - R.drawable.ic_cast_connected, - ContentDescription.Resource(R.string.accessibility_casting) - ), - // TODO(b/332662551): See if we can use a MediaProjection API to fetch - // this time. - startTimeMs = systemClock.elapsedRealtime() - ) { - // TODO(b/332662551): Implement the pause dialog. + is MediaProjectionState.Projecting -> { + if (isProjectionToOtherDevice(state.hostPackage)) { + createCastToOtherDeviceChip() + } else { + createShareToAppChip() } } } } .stateIn(scope, SharingStarted.WhileSubscribed(), OngoingActivityChipModel.Hidden) + + /** Stops the currently active projection. */ + fun stopProjecting() { + scope.launch { mediaProjectionRepository.stopProjecting() } + } + + /** + * Returns true iff projecting to the given [packageName] means that we're projecting to a + * *different* device (as opposed to projecting to some application on *this* device). + */ + private fun isProjectionToOtherDevice(packageName: String?): Boolean { + // The [isHeadlessRemoteDisplayProvider] check approximates whether a projection is to a + // different device or the same device, because headless remote display packages are the + // only kinds of packages that do cast-to-other-device. This isn't exactly perfect, + // because it means that any projection by those headless remote display packages will be + // marked as going to a different device, even if that isn't always true. See b/321078669. + return Utils.isHeadlessRemoteDisplayProvider(packageManager, packageName) + } + + private fun createCastToOtherDeviceChip(): OngoingActivityChipModel.Shown { + return OngoingActivityChipModel.Shown( + icon = + Icon.Resource( + CAST_TO_OTHER_DEVICE_ICON, + ContentDescription.Resource(R.string.accessibility_casting) + ), + // TODO(b/332662551): Maybe use a MediaProjection API to fetch this time. + startTimeMs = systemClock.elapsedRealtime(), + createDialogLaunchOnClickListener( + castToOtherDeviceDialogDelegate, + dialogTransitionAnimator, + ), + ) + } + + private val castToOtherDeviceDialogDelegate = + EndCastToOtherDeviceDialogDelegate( + dialogFactory, + this@MediaProjectionChipInteractor, + ) + + private fun createShareToAppChip(): OngoingActivityChipModel.Shown { + return OngoingActivityChipModel.Shown( + // TODO(b/332662551): Use the right content description. + icon = Icon.Resource(SHARE_TO_APP_ICON, contentDescription = null), + // TODO(b/332662551): Maybe use a MediaProjection API to fetch this time. + startTimeMs = systemClock.elapsedRealtime(), + createDialogLaunchOnClickListener(shareToAppDialogDelegate, dialogTransitionAnimator), + ) + } + + private val shareToAppDialogDelegate = + EndShareToAppDialogDelegate( + dialogFactory, + this@MediaProjectionChipInteractor, + ) + + companion object { + // TODO(b/332662551): Use the right icon. + @DrawableRes val SHARE_TO_APP_ICON = R.drawable.ic_screenshot_share + @DrawableRes val CAST_TO_OTHER_DEVICE_ICON = R.drawable.ic_cast_connected + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndCastToOtherDeviceDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndCastToOtherDeviceDialogDelegate.kt new file mode 100644 index 000000000000..33cec9755b1f --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndCastToOtherDeviceDialogDelegate.kt @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.chips.mediaprojection.ui.view + +import android.os.Bundle +import com.android.systemui.res.R +import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractor +import com.android.systemui.statusbar.phone.SystemUIDialog + +/** A dialog that lets the user stop an ongoing cast-screen-to-other-device event. */ +class EndCastToOtherDeviceDialogDelegate( + private val systemUIDialogFactory: SystemUIDialog.Factory, + private val interactor: MediaProjectionChipInteractor, +) : SystemUIDialog.Delegate { + override fun createDialog(): SystemUIDialog { + return systemUIDialogFactory.create(this) + } + + override fun beforeCreate(dialog: SystemUIDialog, savedInstanceState: Bundle?) { + with(dialog) { + setIcon(MediaProjectionChipInteractor.CAST_TO_OTHER_DEVICE_ICON) + setTitle(R.string.cast_to_other_device_stop_dialog_title) + // TODO(b/332662551): Use a different message if they're sharing just a single app. + setMessage(R.string.cast_to_other_device_stop_dialog_message) + // No custom on-click, because the dialog will automatically be dismissed when the + // button is clicked anyway. + setNegativeButton(R.string.close_dialog_button, /* onClick= */ null) + setPositiveButton(R.string.cast_to_other_device_stop_dialog_button) { _, _ -> + interactor.stopProjecting() + } + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndShareToAppDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndShareToAppDialogDelegate.kt new file mode 100644 index 000000000000..3a863b10ef82 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndShareToAppDialogDelegate.kt @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.chips.mediaprojection.ui.view + +import android.os.Bundle +import com.android.systemui.res.R +import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractor +import com.android.systemui.statusbar.phone.SystemUIDialog + +/** A dialog that lets the user stop an ongoing share-screen-to-app event. */ +class EndShareToAppDialogDelegate( + private val systemUIDialogFactory: SystemUIDialog.Factory, + private val interactor: MediaProjectionChipInteractor, +) : SystemUIDialog.Delegate { + override fun createDialog(): SystemUIDialog { + return systemUIDialogFactory.create(this) + } + + override fun beforeCreate(dialog: SystemUIDialog, savedInstanceState: Bundle?) { + with(dialog) { + setIcon(MediaProjectionChipInteractor.SHARE_TO_APP_ICON) + setTitle(R.string.share_to_app_stop_dialog_title) + // TODO(b/332662551): Use a different message if they're sharing just a single app. + setMessage(R.string.share_to_app_stop_dialog_message) + // No custom on-click, because the dialog will automatically be dismissed when the + // button is clicked anyway. + setNegativeButton(R.string.close_dialog_button, /* onClick= */ null) + setPositiveButton(R.string.share_to_app_stop_dialog_button) { _, _ -> + interactor.stopProjecting() + } + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractor.kt index 585ff5f78ff8..4959b0950fb5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractor.kt @@ -16,6 +16,8 @@ package com.android.systemui.statusbar.chips.screenrecord.domain.interactor +import androidx.annotation.DrawableRes +import com.android.systemui.animation.DialogTransitionAnimator import com.android.systemui.common.shared.model.Icon import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.qualifiers.Application @@ -23,7 +25,10 @@ import com.android.systemui.res.R import com.android.systemui.screenrecord.data.model.ScreenRecordModel import com.android.systemui.screenrecord.data.repository.ScreenRecordRepository import com.android.systemui.statusbar.chips.domain.interactor.OngoingActivityChipInteractor +import com.android.systemui.statusbar.chips.domain.interactor.OngoingActivityChipInteractor.Companion.createDialogLaunchOnClickListener import com.android.systemui.statusbar.chips.domain.model.OngoingActivityChipModel +import com.android.systemui.statusbar.chips.screenrecord.ui.view.EndScreenRecordingDialogDelegate +import com.android.systemui.statusbar.phone.SystemUIDialog import com.android.systemui.util.time.SystemClock import javax.inject.Inject import kotlinx.coroutines.CoroutineScope @@ -31,15 +36,18 @@ import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.launch /** Interactor for the screen recording chip shown in the status bar. */ @SysUISingleton class ScreenRecordChipInteractor @Inject constructor( - @Application scope: CoroutineScope, - screenRecordRepository: ScreenRecordRepository, - val systemClock: SystemClock, + @Application private val scope: CoroutineScope, + private val screenRecordRepository: ScreenRecordRepository, + private val systemClock: SystemClock, + private val dialogFactory: SystemUIDialog.Factory, + private val dialogTransitionAnimator: DialogTransitionAnimator, ) : OngoingActivityChipInteractor { override val chip: StateFlow<OngoingActivityChipModel> = screenRecordRepository.screenRecordState @@ -51,16 +59,29 @@ constructor( is ScreenRecordModel.Recording -> OngoingActivityChipModel.Shown( // TODO(b/332662551): Also provide a content description. - icon = - Icon.Resource( - R.drawable.stat_sys_screen_record, - contentDescription = null - ), - startTimeMs = systemClock.elapsedRealtime() - ) { - // TODO(b/332662551): Implement the pause dialog. - } + icon = Icon.Resource(ICON, contentDescription = null), + startTimeMs = systemClock.elapsedRealtime(), + createDialogLaunchOnClickListener( + dialogDelegate, + dialogTransitionAnimator + ), + ) } } .stateIn(scope, SharingStarted.WhileSubscribed(), OngoingActivityChipModel.Hidden) + + /** Stops the recording. */ + fun stopRecording() { + scope.launch { screenRecordRepository.stopRecording() } + } + + private val dialogDelegate = + EndScreenRecordingDialogDelegate( + dialogFactory, + this@ScreenRecordChipInteractor, + ) + + companion object { + @DrawableRes val ICON = R.drawable.ic_screenrecord + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/ui/view/EndScreenRecordingDialogDelegate.kt b/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/ui/view/EndScreenRecordingDialogDelegate.kt new file mode 100644 index 000000000000..b8e8cfa11afc --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/chips/screenrecord/ui/view/EndScreenRecordingDialogDelegate.kt @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.chips.screenrecord.ui.view + +import android.os.Bundle +import com.android.systemui.res.R +import com.android.systemui.statusbar.chips.screenrecord.domain.interactor.ScreenRecordChipInteractor +import com.android.systemui.statusbar.phone.SystemUIDialog + +/** A dialog that lets the user stop an ongoing screen recording. */ +class EndScreenRecordingDialogDelegate( + private val systemUIDialogFactory: SystemUIDialog.Factory, + private val interactor: ScreenRecordChipInteractor, +) : SystemUIDialog.Delegate { + + override fun createDialog(): SystemUIDialog { + return systemUIDialogFactory.create(this) + } + + override fun beforeCreate(dialog: SystemUIDialog, savedInstanceState: Bundle?) { + with(dialog) { + setIcon(ScreenRecordChipInteractor.ICON) + setTitle(R.string.screenrecord_stop_dialog_title) + // TODO(b/332662551): Use a different message if they're sharing just a single app. + setMessage(R.string.screenrecord_stop_dialog_message) + // No custom on-click, because the dialog will automatically be dismissed when the + // button is clicked anyway. + setNegativeButton(R.string.close_dialog_button, /* onClick= */ null) + setPositiveButton(R.string.screenrecord_stop_dialog_button) { _, _ -> + interactor.stopRecording() + } + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java index d1fabb168d90..939424945038 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/wrapper/NotificationHeaderViewWrapper.java @@ -358,11 +358,7 @@ public class NotificationHeaderViewWrapper extends NotificationViewWrapper imple * @param whenMillis */ public void setNotificationWhen(long whenMillis) { - if (mNotificationHeader == null) { - return; - } - - final View timeView = mNotificationHeader.findViewById(com.android.internal.R.id.time); + final View timeView = mView.findViewById(com.android.internal.R.id.time); if (timeView instanceof DateTimeView) { ((DateTimeView) timeView).setTime(whenMillis); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt index 6a8c43a077a6..b13630fa302a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModel.kt @@ -53,6 +53,7 @@ import com.android.systemui.keyguard.ui.viewmodel.GlanceableHubToLockscreenTrans import com.android.systemui.keyguard.ui.viewmodel.GoneToAodTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.GoneToDozingTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.GoneToDreamingTransitionViewModel +import com.android.systemui.keyguard.ui.viewmodel.GoneToLockscreenTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.LockscreenToDreamingTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.LockscreenToGlanceableHubTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.LockscreenToGoneTransitionViewModel @@ -120,6 +121,7 @@ constructor( private val goneToAodTransitionViewModel: GoneToAodTransitionViewModel, private val goneToDozingTransitionViewModel: GoneToDozingTransitionViewModel, private val goneToDreamingTransitionViewModel: GoneToDreamingTransitionViewModel, + private val goneToLockscreenTransitionViewModel: GoneToLockscreenTransitionViewModel, private val lockscreenToDreamingTransitionViewModel: LockscreenToDreamingTransitionViewModel, private val lockscreenToGlanceableHubTransitionViewModel: LockscreenToGlanceableHubTransitionViewModel, @@ -472,6 +474,9 @@ constructor( // All transition view models are mututally exclusive, and safe to merge val alphaTransitions = merge( + keyguardInteractor.dismissAlpha.dumpWhileCollecting( + "keyguardInteractor.dismissAlpha" + ), alternateBouncerToGoneTransitionViewModel.notificationAlpha(viewState), aodToGoneTransitionViewModel.notificationAlpha(viewState), aodToLockscreenTransitionViewModel.notificationAlpha, @@ -482,6 +487,7 @@ constructor( goneToAodTransitionViewModel.notificationAlpha, goneToDreamingTransitionViewModel.lockscreenAlpha, goneToDozingTransitionViewModel.lockscreenAlpha, + goneToLockscreenTransitionViewModel.lockscreenAlpha, lockscreenToDreamingTransitionViewModel.lockscreenAlpha, lockscreenToGoneTransitionViewModel.notificationAlpha(viewState), lockscreenToOccludedTransitionViewModel.lockscreenAlpha, @@ -498,24 +504,12 @@ constructor( // These remaining cases handle alpha changes within an existing state, such as // shade expansion or swipe to dismiss combineTransform( - isOnLockscreenWithoutShade, isTransitioningToHiddenKeyguard, - shadeCollapseFadeIn, alphaForShadeAndQsExpansion, - keyguardInteractor.dismissAlpha.dumpWhileCollecting( - "keyguardInteractor.keyguardAlpha" - ), ) { - isOnLockscreenWithoutShade, isTransitioningToHiddenKeyguard, - shadeCollapseFadeIn, - alphaForShadeAndQsExpansion, - dismissAlpha -> - if (isOnLockscreenWithoutShade) { - if (!shadeCollapseFadeIn && dismissAlpha != null) { - emit(dismissAlpha) - } - } else if (!isTransitioningToHiddenKeyguard) { + alphaForShadeAndQsExpansion -> + if (!isTransitioningToHiddenKeyguard) { emit(alphaForShadeAndQsExpansion) } }, diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java index e44edcbd4ebb..cd59d4ebfd73 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java @@ -21,6 +21,7 @@ import static android.app.StatusBarManager.DISABLE_SYSTEM_INFO; import static com.android.systemui.Flags.updateUserSwitcherBackground; import static com.android.systemui.statusbar.StatusBarState.KEYGUARD; +import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow; import android.content.res.Configuration; import android.content.res.Resources; @@ -46,6 +47,7 @@ import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.keyguard.logging.KeyguardLogger; import com.android.systemui.battery.BatteryMeterViewController; +import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor; import com.android.systemui.dagger.qualifiers.Background; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.log.core.LogLevel; @@ -83,6 +85,7 @@ import java.io.PrintWriter; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Executor; +import java.util.function.Consumer; import javax.inject.Inject; @@ -128,6 +131,7 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat private final Executor mBackgroundExecutor; private final Object mLock = new Object(); private final KeyguardLogger mLogger; + private final CommunalSceneInteractor mCommunalSceneInteractor; private View mSystemIconsContainer; private final StatusOverlayHoverListenerFactory mStatusOverlayHoverListenerFactory; @@ -241,6 +245,12 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat } }; + private boolean mCommunalShowing; + + private final Consumer<Boolean> mCommunalConsumer = (communalShowing) -> { + mCommunalShowing = communalShowing; + updateViewState(); + }; private final DisableStateTracker mDisableStateTracker; @@ -298,7 +308,8 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat @Main Executor mainExecutor, @Background Executor backgroundExecutor, KeyguardLogger logger, - StatusOverlayHoverListenerFactory statusOverlayHoverListenerFactory + StatusOverlayHoverListenerFactory statusOverlayHoverListenerFactory, + CommunalSceneInteractor communalSceneInteractor ) { super(view); mCarrierTextController = carrierTextController; @@ -324,6 +335,7 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat mMainExecutor = mainExecutor; mBackgroundExecutor = backgroundExecutor; mLogger = logger; + mCommunalSceneInteractor = communalSceneInteractor; mFirstBypassAttempt = mKeyguardBypassController.getBypassEnabled(); mKeyguardStateController.addCallback( @@ -405,6 +417,7 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat UserHandle.USER_ALL); updateUserSwitcher(); onThemeChanged(); + collectFlow(mView, mCommunalSceneInteractor.isCommunalVisible(), mCommunalConsumer); } @Override @@ -559,6 +572,7 @@ public class KeyguardStatusBarViewController extends ViewController<KeyguardStat && !mDozing && !hideForBypass && !mDisableStateTracker.isDisabled() + && !mCommunalShowing ? View.VISIBLE : View.INVISIBLE; updateViewState(newAlpha, newVisibility); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt index a2ec1f21a35c..44b5bafc0859 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ui/binder/CollapsedStatusBarViewBinder.kt @@ -94,7 +94,7 @@ class CollapsedStatusBarViewBinderImpl @Inject constructor() : CollapsedStatusBa is OngoingActivityChipModel.Shown -> { IconViewBinder.bind(chipModel.icon, chipIconView) ChipChronometerBinder.bind(chipModel.startTimeMs, chipTimeView) - // TODO(b/332662551): Attach click listener to chip + chipView.setOnClickListener(chipModel.onClickListener) listener.onOngoingActivityStatusChanged( hasOngoingActivity = true diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/TouchpadTutorialViewModel.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/TouchpadTutorialViewModel.kt index 7669524ee316..11740a8a4ecc 100644 --- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/TouchpadTutorialViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/TouchpadTutorialViewModel.kt @@ -27,6 +27,10 @@ class TouchpadTutorialViewModel : ViewModel() { private val _screen = MutableStateFlow(Screen.TUTORIAL_SELECTION) val screen: StateFlow<Screen> = _screen + fun goTo(screen: Screen) { + _screen.value = screen + } + class Factory @Inject constructor() : ViewModelProvider.Factory { @Suppress("UNCHECKED_CAST") diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/TutorialSelectionViewModel.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/TutorialSelectionViewModel.kt deleted file mode 100644 index 1a8272d8e7e3..000000000000 --- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/TutorialSelectionViewModel.kt +++ /dev/null @@ -1,30 +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.touchpad.tutorial.ui - -import androidx.lifecycle.ViewModel -import androidx.lifecycle.ViewModelProvider - -class TutorialSelectionViewModel : ViewModel() - -class TutorialSelectionViewModelFactory : ViewModelProvider.Factory { - - @Suppress("UNCHECKED_CAST") - override fun <T : ViewModel> create(modelClass: Class<T>): T { - return TutorialSelectionViewModel() as T - } -} diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/view/TouchpadTutorialActivity.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/view/TouchpadTutorialActivity.kt index 09dd909cd9a3..b7629c7160ee 100644 --- a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/view/TouchpadTutorialActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/view/TouchpadTutorialActivity.kt @@ -19,6 +19,7 @@ package com.android.systemui.touchpad.tutorial.ui.view import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent +import androidx.activity.enableEdgeToEdge import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.lifecycle.Lifecycle.State.STARTED @@ -33,8 +34,6 @@ import com.android.systemui.touchpad.tutorial.ui.Screen.BACK_GESTURE import com.android.systemui.touchpad.tutorial.ui.Screen.HOME_GESTURE import com.android.systemui.touchpad.tutorial.ui.Screen.TUTORIAL_SELECTION import com.android.systemui.touchpad.tutorial.ui.TouchpadTutorialViewModel -import com.android.systemui.touchpad.tutorial.ui.TutorialSelectionViewModel -import com.android.systemui.touchpad.tutorial.ui.TutorialSelectionViewModelFactory import javax.inject.Inject class TouchpadTutorialActivity @@ -45,27 +44,31 @@ constructor( override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - setContent { PlatformTheme { TouchpadTutorialScreen(viewModelFactory) } } + enableEdgeToEdge() + setContent { + PlatformTheme { TouchpadTutorialScreen(viewModelFactory, closeTutorial = { finish() }) } + } } } @Composable -fun TouchpadTutorialScreen(viewModelFactory: ViewModelProvider.Factory) { +fun TouchpadTutorialScreen(viewModelFactory: ViewModelProvider.Factory, closeTutorial: () -> Unit) { val vm = viewModel<TouchpadTutorialViewModel>(factory = viewModelFactory) val activeScreen by vm.screen.collectAsStateWithLifecycle(STARTED) when (activeScreen) { - TUTORIAL_SELECTION -> TutorialSelectionScreen() + TUTORIAL_SELECTION -> + TutorialSelectionScreen( + onBackTutorialClicked = { vm.goTo(BACK_GESTURE) }, + onHomeTutorialClicked = { vm.goTo(HOME_GESTURE) }, + onActionKeyTutorialClicked = {}, + onDoneButtonClicked = closeTutorial + ) BACK_GESTURE -> BackGestureTutorialScreen() HOME_GESTURE -> HomeGestureTutorialScreen() } } @Composable -fun TutorialSelectionScreen() { - val vm = viewModel<TutorialSelectionViewModel>(factory = TutorialSelectionViewModelFactory()) -} - -@Composable fun BackGestureTutorialScreen() { val vm = viewModel<BackGestureTutorialViewModel>(factory = GestureViewModelFactory()) } diff --git a/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/view/TutorialSelectionScreen.kt b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/view/TutorialSelectionScreen.kt new file mode 100644 index 000000000000..532eb1b82197 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/touchpad/tutorial/ui/view/TutorialSelectionScreen.kt @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.touchpad.tutorial.ui.view + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.aspectRatio +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import com.android.systemui.res.R + +@Composable +fun TutorialSelectionScreen( + onBackTutorialClicked: () -> Unit, + onHomeTutorialClicked: () -> Unit, + onActionKeyTutorialClicked: () -> Unit, + onDoneButtonClicked: () -> Unit, +) { + Column( + verticalArrangement = Arrangement.Center, + modifier = + Modifier.background( + color = MaterialTheme.colorScheme.surfaceContainer, + ) + .fillMaxSize() + ) { + TutorialSelectionButtons( + onBackTutorialClicked = onBackTutorialClicked, + onHomeTutorialClicked = onHomeTutorialClicked, + onActionKeyTutorialClicked = onActionKeyTutorialClicked, + modifier = Modifier.padding(60.dp) + ) + DoneButton( + onDoneButtonClicked = onDoneButtonClicked, + modifier = Modifier.padding(horizontal = 60.dp) + ) + } +} + +@Composable +private fun TutorialSelectionButtons( + onBackTutorialClicked: () -> Unit, + onHomeTutorialClicked: () -> Unit, + onActionKeyTutorialClicked: () -> Unit, + modifier: Modifier = Modifier +) { + Row( + horizontalArrangement = Arrangement.spacedBy(20.dp), + verticalAlignment = Alignment.CenterVertically, + modifier = modifier + ) { + TutorialButton( + text = stringResource(R.string.touchpad_tutorial_back_gesture_button), + onClick = onBackTutorialClicked, + color = MaterialTheme.colorScheme.primary, + modifier = Modifier.weight(1f) + ) + TutorialButton( + text = stringResource(R.string.touchpad_tutorial_home_gesture_button), + onClick = onHomeTutorialClicked, + color = MaterialTheme.colorScheme.secondary, + modifier = Modifier.weight(1f) + ) + TutorialButton( + text = stringResource(R.string.touchpad_tutorial_action_key_button), + onClick = onActionKeyTutorialClicked, + color = MaterialTheme.colorScheme.tertiary, + modifier = Modifier.weight(1f) + ) + } +} + +@Composable +private fun TutorialButton( + text: String, + onClick: () -> Unit, + color: Color, + modifier: Modifier = Modifier +) { + Button( + onClick = onClick, + shape = RoundedCornerShape(16.dp), + colors = ButtonDefaults.buttonColors(containerColor = color), + modifier = modifier.aspectRatio(0.66f) + ) { + Text(text = text, style = MaterialTheme.typography.headlineLarge) + } +} + +@Composable +private fun DoneButton(onDoneButtonClicked: () -> Unit, modifier: Modifier = Modifier) { + Row( + horizontalArrangement = Arrangement.End, + verticalAlignment = Alignment.CenterVertically, + modifier = modifier.fillMaxWidth() + ) { + Button(onClick = onDoneButtonClicked) { + Text(stringResource(R.string.touchpad_tutorial_done_button)) + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt b/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt index a27989d772c4..291903d7254f 100644 --- a/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt +++ b/packages/SystemUI/src/com/android/systemui/unfold/SysUIUnfoldModule.kt @@ -21,6 +21,7 @@ import com.android.systemui.CoreStartable import com.android.systemui.dagger.SysUISingleton import com.android.systemui.shade.NotificationPanelUnfoldAnimationController import com.android.systemui.statusbar.phone.StatusBarMoveFromCenterAnimationController +import com.android.systemui.unfold.dagger.NaturalRotation import com.android.systemui.unfold.dagger.UnfoldBg import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider @@ -108,6 +109,13 @@ abstract class SysUIUnfoldInternalModule { abstract fun bindsFoldLightRevealOverlayAnimation( anim: FoldLightRevealOverlayAnimation ): FullscreenLightRevealAnimation + + @Binds + @NaturalRotation + @SysUIUnfoldScope + abstract fun bindNaturalRotationUnfoldProgressProvider( + provider: NaturalRotationUnfoldProgressProvider + ): UnfoldTransitionProgressProvider } @SysUIUnfoldScope diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUnfoldTransitionTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUnfoldTransitionTest.kt index 3afca5933e5d..336183d71ff1 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUnfoldTransitionTest.kt +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUnfoldTransitionTest.kt @@ -18,26 +18,25 @@ package com.android.keyguard import android.testing.AndroidTestingRunner import android.view.View -import android.view.ViewGroup import androidx.test.filters.SmallTest -import com.android.systemui.res.R import com.android.systemui.SysuiTestCase +import com.android.systemui.keyguard.ui.view.KeyguardRootView +import com.android.systemui.kosmos.Kosmos import com.android.systemui.plugins.statusbar.StatusBarStateController +import com.android.systemui.res.R +import com.android.systemui.shade.NotificationShadeWindowView import com.android.systemui.statusbar.StatusBarState.KEYGUARD import com.android.systemui.statusbar.StatusBarState.SHADE +import com.android.systemui.unfold.FakeUnfoldTransitionProvider import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener -import com.android.systemui.unfold.util.NaturalRotationUnfoldProgressProvider -import com.android.systemui.util.mockito.capture -import com.android.systemui.util.mockito.whenever +import com.android.systemui.unfold.fakeUnfoldTransitionProgressProvider import com.google.common.truth.Truth.assertThat import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.mockito.ArgumentCaptor -import org.mockito.Captor import org.mockito.Mock -import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations +import org.mockito.kotlin.whenever /** * Translates items away/towards the hinge when the device is opened/closed. This is controlled by @@ -47,11 +46,16 @@ import org.mockito.MockitoAnnotations @RunWith(AndroidTestingRunner::class) class KeyguardUnfoldTransitionTest : SysuiTestCase() { - @Mock private lateinit var progressProvider: NaturalRotationUnfoldProgressProvider + private val kosmos = Kosmos() + + private val progressProvider: FakeUnfoldTransitionProvider = + kosmos.fakeUnfoldTransitionProgressProvider - @Captor private lateinit var progressListenerCaptor: ArgumentCaptor<TransitionProgressListener> + @Mock + private lateinit var keyguardRootView: KeyguardRootView - @Mock private lateinit var parent: ViewGroup + @Mock + private lateinit var notificationShadeWindowView: NotificationShadeWindowView @Mock private lateinit var statusBarStateController: StatusBarStateController @@ -66,13 +70,15 @@ class KeyguardUnfoldTransitionTest : SysuiTestCase() { xTranslationMax = context.resources.getDimensionPixelSize(R.dimen.keyguard_unfold_translation_x).toFloat() - underTest = KeyguardUnfoldTransition(context, statusBarStateController, progressProvider) + underTest = KeyguardUnfoldTransition( + context, keyguardRootView, notificationShadeWindowView, + statusBarStateController, progressProvider + ) - underTest.setup(parent) + underTest.setup() underTest.statusViewCentered = false - verify(progressProvider).addCallback(capture(progressListenerCaptor)) - progressListener = progressListenerCaptor.value + progressListener = progressProvider } @Test @@ -81,7 +87,9 @@ class KeyguardUnfoldTransitionTest : SysuiTestCase() { underTest.statusViewCentered = true val view = View(context) - whenever(parent.findViewById<View>(R.id.lockscreen_clock_view_large)).thenReturn(view) + whenever(keyguardRootView.findViewById<View>(R.id.lockscreen_clock_view_large)).thenReturn( + view + ) progressListener.onTransitionStarted() assertThat(view.translationX).isZero() @@ -101,7 +109,9 @@ class KeyguardUnfoldTransitionTest : SysuiTestCase() { whenever(statusBarStateController.getState()).thenReturn(SHADE) val view = View(context) - whenever(parent.findViewById<View>(R.id.lockscreen_clock_view_large)).thenReturn(view) + whenever(keyguardRootView.findViewById<View>(R.id.lockscreen_clock_view_large)).thenReturn( + view + ) progressListener.onTransitionStarted() assertThat(view.translationX).isZero() @@ -121,7 +131,10 @@ class KeyguardUnfoldTransitionTest : SysuiTestCase() { whenever(statusBarStateController.getState()).thenReturn(KEYGUARD) val view = View(context) - whenever(parent.findViewById<View>(R.id.lockscreen_clock_view_large)).thenReturn(view) + whenever( + notificationShadeWindowView + .findViewById<View>(R.id.lockscreen_clock_view_large) + ).thenReturn(view) progressListener.onTransitionStarted() assertThat(view.translationX).isZero() diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractorImplTest.kt deleted file mode 100644 index 64bd742d3af1..000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractorImplTest.kt +++ /dev/null @@ -1,121 +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.bluetooth.qsdialog - -import android.bluetooth.BluetoothDevice -import android.testing.TestableLooper -import androidx.test.ext.junit.runners.AndroidJUnit4 -import androidx.test.filters.SmallTest -import com.android.settingslib.bluetooth.CachedBluetoothDevice -import com.android.systemui.SysuiTestCase -import com.android.systemui.kosmos.testDispatcher -import com.android.systemui.kosmos.testScope -import com.android.systemui.statusbar.phone.SystemUIDialog -import com.android.systemui.testKosmos -import com.android.systemui.util.mockito.whenever -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.test.UnconfinedTestDispatcher -import kotlinx.coroutines.test.runTest -import org.junit.Before -import org.junit.Rule -import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.Mock -import org.mockito.Mockito.verify -import org.mockito.junit.MockitoJUnit -import org.mockito.junit.MockitoRule - -@SmallTest -@RunWith(AndroidJUnit4::class) -@TestableLooper.RunWithLooper(setAsMainLooper = true) -@OptIn(ExperimentalCoroutinesApi::class) -class DeviceItemActionInteractorImplTest : SysuiTestCase() { - @get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule() - private val kosmos = testKosmos().apply { testDispatcher = UnconfinedTestDispatcher() } - private lateinit var actionInteractorImpl: DeviceItemActionInteractor - - @Mock private lateinit var dialog: SystemUIDialog - @Mock private lateinit var cachedDevice: CachedBluetoothDevice - @Mock private lateinit var device: BluetoothDevice - @Mock private lateinit var deviceItem: DeviceItem - - @Before - fun setUp() { - actionInteractorImpl = kosmos.deviceItemActionInteractor - whenever(deviceItem.cachedBluetoothDevice).thenReturn(cachedDevice) - whenever(cachedDevice.address).thenReturn("ADDRESS") - whenever(cachedDevice.device).thenReturn(device) - } - - @Test - fun testOnClick_connectedMedia_setActive() { - with(kosmos) { - testScope.runTest { - whenever(deviceItem.type) - .thenReturn(DeviceItemType.AVAILABLE_MEDIA_BLUETOOTH_DEVICE) - actionInteractorImpl.onClick(deviceItem, dialog) - verify(cachedDevice).setActive() - verify(bluetoothTileDialogLogger) - .logDeviceClick( - cachedDevice.address, - DeviceItemType.AVAILABLE_MEDIA_BLUETOOTH_DEVICE - ) - } - } - } - - @Test - fun testOnClick_activeMedia_disconnect() { - with(kosmos) { - testScope.runTest { - whenever(deviceItem.type).thenReturn(DeviceItemType.ACTIVE_MEDIA_BLUETOOTH_DEVICE) - actionInteractorImpl.onClick(deviceItem, dialog) - verify(cachedDevice).disconnect() - verify(bluetoothTileDialogLogger) - .logDeviceClick( - cachedDevice.address, - DeviceItemType.ACTIVE_MEDIA_BLUETOOTH_DEVICE - ) - } - } - } - - @Test - fun testOnClick_connectedOtherDevice_disconnect() { - with(kosmos) { - testScope.runTest { - whenever(deviceItem.type).thenReturn(DeviceItemType.CONNECTED_BLUETOOTH_DEVICE) - actionInteractorImpl.onClick(deviceItem, dialog) - verify(cachedDevice).disconnect() - verify(bluetoothTileDialogLogger) - .logDeviceClick(cachedDevice.address, DeviceItemType.CONNECTED_BLUETOOTH_DEVICE) - } - } - } - - @Test - fun testOnClick_saved_connect() { - with(kosmos) { - testScope.runTest { - whenever(deviceItem.type).thenReturn(DeviceItemType.SAVED_BLUETOOTH_DEVICE) - actionInteractorImpl.onClick(deviceItem, dialog) - verify(cachedDevice).connect() - verify(bluetoothTileDialogLogger) - .logDeviceClick(cachedDevice.address, DeviceItemType.SAVED_BLUETOOTH_DEVICE) - } - } - } -} diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractorKosmos.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractorKosmos.kt index e8e37bc81866..5ff46346b386 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractorKosmos.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractorKosmos.kt @@ -13,19 +13,28 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.android.systemui.bluetooth.qsdialog import com.android.internal.logging.uiEventLogger +import com.android.settingslib.bluetooth.LocalBluetoothManager +import com.android.systemui.animation.DialogTransitionAnimator import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.testDispatcher +import com.android.systemui.plugins.activityStarter import com.android.systemui.util.mockito.mock val Kosmos.bluetoothTileDialogLogger: BluetoothTileDialogLogger by Kosmos.Fixture { mock {} } +val Kosmos.localBluetoothManager: LocalBluetoothManager by Kosmos.Fixture { mock {} } + +val Kosmos.dialogTransitionAnimator: DialogTransitionAnimator by Kosmos.Fixture { mock {} } + val Kosmos.deviceItemActionInteractor: DeviceItemActionInteractor by Kosmos.Fixture { - DeviceItemActionInteractorImpl( + DeviceItemActionInteractor( + activityStarter, + dialogTransitionAnimator, + localBluetoothManager, testDispatcher, bluetoothTileDialogLogger, uiEventLogger, diff --git a/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractorTest.kt new file mode 100644 index 000000000000..82465065c1e1 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/bluetooth/qsdialog/DeviceItemActionInteractorTest.kt @@ -0,0 +1,459 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES 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.bluetooth.qsdialog + +import android.bluetooth.BluetoothDevice +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper +import androidx.test.filters.SmallTest +import com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession +import com.android.dx.mockito.inline.extended.StaticMockitoSession +import com.android.settingslib.bluetooth.BluetoothUtils +import com.android.settingslib.bluetooth.CachedBluetoothDevice +import com.android.settingslib.bluetooth.LeAudioProfile +import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant +import com.android.settingslib.bluetooth.LocalBluetoothProfileManager +import com.android.systemui.SysuiTestCase +import com.android.systemui.kosmos.testDispatcher +import com.android.systemui.kosmos.testScope +import com.android.systemui.plugins.activityStarter +import com.android.systemui.statusbar.phone.SystemUIDialog +import com.android.systemui.testKosmos +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.UnconfinedTestDispatcher +import kotlinx.coroutines.test.runTest +import org.junit.After +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers +import org.mockito.Mock +import org.mockito.Mockito +import org.mockito.Mockito.verify +import org.mockito.junit.MockitoJUnit +import org.mockito.junit.MockitoRule +import org.mockito.kotlin.whenever + +@SmallTest +@RunWith(AndroidTestingRunner::class) +@TestableLooper.RunWithLooper(setAsMainLooper = true) +@OptIn(ExperimentalCoroutinesApi::class) +class DeviceItemActionInteractorTest : SysuiTestCase() { + @get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule() + private val kosmos = testKosmos().apply { testDispatcher = UnconfinedTestDispatcher() } + private lateinit var actionInteractorImpl: DeviceItemActionInteractor + private lateinit var mockitoSession: StaticMockitoSession + private lateinit var activeMediaDeviceItem: DeviceItem + private lateinit var notConnectedDeviceItem: DeviceItem + private lateinit var connectedMediaDeviceItem: DeviceItem + private lateinit var connectedOtherDeviceItem: DeviceItem + @Mock private lateinit var dialog: SystemUIDialog + @Mock private lateinit var profileManager: LocalBluetoothProfileManager + @Mock private lateinit var leAudioProfile: LeAudioProfile + @Mock private lateinit var assistantProfile: LocalBluetoothLeBroadcastAssistant + @Mock private lateinit var bluetoothDevice: BluetoothDevice + @Mock private lateinit var bluetoothDeviceGroupId2: BluetoothDevice + @Mock private lateinit var cachedBluetoothDevice: CachedBluetoothDevice + + @Before + fun setUp() { + mockitoSession = + mockitoSession().initMocks(this).mockStatic(BluetoothUtils::class.java).startMocking() + activeMediaDeviceItem = + DeviceItem( + type = DeviceItemType.ACTIVE_MEDIA_BLUETOOTH_DEVICE, + cachedBluetoothDevice = cachedBluetoothDevice, + deviceName = DEVICE_NAME, + connectionSummary = DEVICE_CONNECTION_SUMMARY, + iconWithDescription = null, + background = null + ) + notConnectedDeviceItem = + DeviceItem( + type = DeviceItemType.SAVED_BLUETOOTH_DEVICE, + cachedBluetoothDevice = cachedBluetoothDevice, + deviceName = DEVICE_NAME, + connectionSummary = DEVICE_CONNECTION_SUMMARY, + iconWithDescription = null, + background = null + ) + connectedMediaDeviceItem = + DeviceItem( + type = DeviceItemType.AVAILABLE_MEDIA_BLUETOOTH_DEVICE, + cachedBluetoothDevice = cachedBluetoothDevice, + deviceName = DEVICE_NAME, + connectionSummary = DEVICE_CONNECTION_SUMMARY, + iconWithDescription = null, + background = null + ) + connectedOtherDeviceItem = + DeviceItem( + type = DeviceItemType.CONNECTED_BLUETOOTH_DEVICE, + cachedBluetoothDevice = cachedBluetoothDevice, + deviceName = DEVICE_NAME, + connectionSummary = DEVICE_CONNECTION_SUMMARY, + iconWithDescription = null, + background = null + ) + actionInteractorImpl = kosmos.deviceItemActionInteractor + } + + @After + fun tearDown() { + mockitoSession.finishMocking() + } + + @Test + fun testOnClick_connectedMedia_setActive() { + with(kosmos) { + testScope.runTest { + whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS) + whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(false) + actionInteractorImpl.onClick(connectedMediaDeviceItem, dialog) + verify(cachedBluetoothDevice).setActive() + verify(bluetoothTileDialogLogger) + .logDeviceClick( + cachedBluetoothDevice.address, + DeviceItemType.AVAILABLE_MEDIA_BLUETOOTH_DEVICE + ) + } + } + } + + @Test + fun testOnClick_activeMedia_disconnect() { + with(kosmos) { + testScope.runTest { + whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS) + whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(false) + actionInteractorImpl.onClick(activeMediaDeviceItem, dialog) + verify(cachedBluetoothDevice).disconnect() + verify(bluetoothTileDialogLogger) + .logDeviceClick( + cachedBluetoothDevice.address, + DeviceItemType.ACTIVE_MEDIA_BLUETOOTH_DEVICE + ) + } + } + } + + @Test + fun testOnClick_connectedOtherDevice_disconnect() { + with(kosmos) { + testScope.runTest { + whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS) + whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(false) + actionInteractorImpl.onClick(connectedOtherDeviceItem, dialog) + verify(cachedBluetoothDevice).disconnect() + verify(bluetoothTileDialogLogger) + .logDeviceClick( + cachedBluetoothDevice.address, + DeviceItemType.CONNECTED_BLUETOOTH_DEVICE + ) + } + } + } + + @Test + fun testOnClick_saved_connect() { + with(kosmos) { + testScope.runTest { + whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS) + whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(false) + actionInteractorImpl.onClick(notConnectedDeviceItem, dialog) + verify(cachedBluetoothDevice).connect() + verify(bluetoothTileDialogLogger) + .logDeviceClick( + cachedBluetoothDevice.address, + DeviceItemType.SAVED_BLUETOOTH_DEVICE + ) + } + } + } + + @Test + fun testOnClick_audioSharingDisabled_shouldNotLaunchSettings() { + with(kosmos) { + testScope.runTest { + whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS) + whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(false) + + actionInteractorImpl.onClick(connectedMediaDeviceItem, dialog) + verify(activityStarter, Mockito.never()) + .postStartActivityDismissingKeyguard( + ArgumentMatchers.any(), + ArgumentMatchers.anyInt(), + ArgumentMatchers.any() + ) + } + } + } + + @Test + fun testOnClick_inAudioSharing_clickedDeviceHasSource_shouldNotLaunchSettings() { + with(kosmos) { + testScope.runTest { + whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS) + whenever(cachedBluetoothDevice.connectableProfiles) + .thenReturn(listOf(leAudioProfile)) + + whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(true) + whenever(localBluetoothManager.profileManager).thenReturn(profileManager) + whenever(profileManager.leAudioProfile).thenReturn(leAudioProfile) + whenever(profileManager.leAudioBroadcastAssistantProfile) + .thenReturn(assistantProfile) + + whenever(BluetoothUtils.isBroadcasting(ArgumentMatchers.any())).thenReturn(true) + whenever( + BluetoothUtils.hasConnectedBroadcastSource( + ArgumentMatchers.any(), + ArgumentMatchers.any() + ) + ) + .thenReturn(true) + + actionInteractorImpl.onClick(connectedMediaDeviceItem, dialog) + verify(activityStarter, Mockito.never()) + .postStartActivityDismissingKeyguard( + ArgumentMatchers.any(), + ArgumentMatchers.anyInt(), + ArgumentMatchers.any() + ) + } + } + } + + @Test + fun testOnClick_inAudioSharing_clickedDeviceNoSource_shouldLaunchSettings() { + with(kosmos) { + testScope.runTest { + whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS) + whenever(cachedBluetoothDevice.device).thenReturn(bluetoothDevice) + whenever(cachedBluetoothDevice.connectableProfiles) + .thenReturn(listOf(leAudioProfile)) + + whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(true) + whenever(localBluetoothManager.profileManager).thenReturn(profileManager) + whenever(profileManager.leAudioProfile).thenReturn(leAudioProfile) + whenever(profileManager.leAudioBroadcastAssistantProfile) + .thenReturn(assistantProfile) + + whenever(BluetoothUtils.isBroadcasting(ArgumentMatchers.any())).thenReturn(true) + whenever( + BluetoothUtils.hasConnectedBroadcastSource( + ArgumentMatchers.any(), + ArgumentMatchers.any() + ) + ) + .thenReturn(false) + + actionInteractorImpl.onClick(connectedMediaDeviceItem, dialog) + verify(activityStarter) + .postStartActivityDismissingKeyguard( + ArgumentMatchers.any(), + ArgumentMatchers.anyInt(), + ArgumentMatchers.any() + ) + } + } + } + + @Test + fun testOnClick_noConnectedLeDevice_shouldNotLaunchSettings() { + with(kosmos) { + testScope.runTest { + whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS) + + whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(true) + whenever(localBluetoothManager.profileManager).thenReturn(profileManager) + whenever(profileManager.leAudioProfile).thenReturn(leAudioProfile) + whenever(profileManager.leAudioBroadcastAssistantProfile) + .thenReturn(assistantProfile) + + actionInteractorImpl.onClick(notConnectedDeviceItem, dialog) + verify(activityStarter, Mockito.never()) + .postStartActivityDismissingKeyguard( + ArgumentMatchers.any(), + ArgumentMatchers.anyInt(), + ArgumentMatchers.any() + ) + } + } + } + + @Test + fun testOnClick_hasOneConnectedLeDevice_clickedNonLe_shouldNotLaunchSettings() { + with(kosmos) { + testScope.runTest { + whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS) + + whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(true) + whenever(localBluetoothManager.profileManager).thenReturn(profileManager) + whenever(profileManager.leAudioProfile).thenReturn(leAudioProfile) + whenever(profileManager.leAudioBroadcastAssistantProfile) + .thenReturn(assistantProfile) + + whenever( + assistantProfile.getDevicesMatchingConnectionStates(ArgumentMatchers.any()) + ) + .thenReturn(listOf(bluetoothDevice)) + + actionInteractorImpl.onClick(notConnectedDeviceItem, dialog) + verify(activityStarter, Mockito.never()) + .postStartActivityDismissingKeyguard( + ArgumentMatchers.any(), + ArgumentMatchers.anyInt(), + ArgumentMatchers.any() + ) + } + } + } + + @Test + fun testOnClick_hasOneConnectedLeDevice_clickedLe_shouldLaunchSettings() { + with(kosmos) { + testScope.runTest { + whenever(cachedBluetoothDevice.device).thenReturn(bluetoothDevice) + whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS) + whenever(cachedBluetoothDevice.profiles).thenReturn(listOf(leAudioProfile)) + whenever(leAudioProfile.isEnabled(ArgumentMatchers.any())).thenReturn(true) + + whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(true) + whenever(localBluetoothManager.profileManager).thenReturn(profileManager) + whenever(profileManager.leAudioProfile).thenReturn(leAudioProfile) + whenever(profileManager.leAudioBroadcastAssistantProfile) + .thenReturn(assistantProfile) + + whenever( + assistantProfile.getDevicesMatchingConnectionStates(ArgumentMatchers.any()) + ) + .thenReturn(listOf(bluetoothDevice)) + + actionInteractorImpl.onClick(notConnectedDeviceItem, dialog) + verify(activityStarter) + .postStartActivityDismissingKeyguard( + ArgumentMatchers.any(), + ArgumentMatchers.anyInt(), + ArgumentMatchers.any() + ) + } + } + } + + @Test + fun testOnClick_hasOneConnectedLeDevice_clickedConnectedLe_shouldNotLaunchSettings() { + with(kosmos) { + testScope.runTest { + whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS) + + whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(true) + whenever(localBluetoothManager.profileManager).thenReturn(profileManager) + whenever(profileManager.leAudioProfile).thenReturn(leAudioProfile) + whenever(profileManager.leAudioBroadcastAssistantProfile) + .thenReturn(assistantProfile) + + whenever( + assistantProfile.getDevicesMatchingConnectionStates(ArgumentMatchers.any()) + ) + .thenReturn(listOf(bluetoothDevice)) + + actionInteractorImpl.onClick(connectedMediaDeviceItem, dialog) + verify(activityStarter, Mockito.never()) + .postStartActivityDismissingKeyguard( + ArgumentMatchers.any(), + ArgumentMatchers.anyInt(), + ArgumentMatchers.any() + ) + } + } + } + + @Test + fun testOnClick_hasTwoConnectedLeDevice_clickedNotConnectedLe_shouldNotLaunchSettings() { + with(kosmos) { + testScope.runTest { + whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS) + + whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(true) + whenever(localBluetoothManager.profileManager).thenReturn(profileManager) + whenever(profileManager.leAudioProfile).thenReturn(leAudioProfile) + whenever(profileManager.leAudioBroadcastAssistantProfile) + .thenReturn(assistantProfile) + + whenever( + assistantProfile.getDevicesMatchingConnectionStates(ArgumentMatchers.any()) + ) + .thenReturn(listOf(bluetoothDevice, bluetoothDeviceGroupId2)) + whenever(leAudioProfile.getGroupId(ArgumentMatchers.any())).thenAnswer { + val device = it.arguments.first() as BluetoothDevice + if (device == bluetoothDevice) GROUP_ID_1 else GROUP_ID_2 + } + + actionInteractorImpl.onClick(notConnectedDeviceItem, dialog) + verify(activityStarter, Mockito.never()) + .postStartActivityDismissingKeyguard( + ArgumentMatchers.any(), + ArgumentMatchers.anyInt(), + ArgumentMatchers.any() + ) + } + } + } + + @Test + fun testOnClick_hasTwoConnectedLeDevice_clickedConnectedLe_shouldLaunchSettings() { + with(kosmos) { + testScope.runTest { + whenever(cachedBluetoothDevice.device).thenReturn(bluetoothDevice) + whenever(cachedBluetoothDevice.address).thenReturn(DEVICE_ADDRESS) + whenever(cachedBluetoothDevice.profiles).thenReturn(listOf(leAudioProfile)) + whenever(leAudioProfile.isEnabled(ArgumentMatchers.any())).thenReturn(true) + + whenever(BluetoothUtils.isAudioSharingEnabled()).thenReturn(true) + whenever(localBluetoothManager.profileManager).thenReturn(profileManager) + whenever(profileManager.leAudioProfile).thenReturn(leAudioProfile) + whenever(profileManager.leAudioBroadcastAssistantProfile) + .thenReturn(assistantProfile) + + whenever( + assistantProfile.getDevicesMatchingConnectionStates(ArgumentMatchers.any()) + ) + .thenReturn(listOf(bluetoothDevice, bluetoothDeviceGroupId2)) + whenever(leAudioProfile.getGroupId(ArgumentMatchers.any())).thenAnswer { + val device = it.arguments.first() as BluetoothDevice + if (device == bluetoothDevice) GROUP_ID_1 else GROUP_ID_2 + } + + actionInteractorImpl.onClick(connectedMediaDeviceItem, dialog) + verify(activityStarter) + .postStartActivityDismissingKeyguard( + ArgumentMatchers.any(), + ArgumentMatchers.anyInt(), + ArgumentMatchers.any() + ) + } + } + } + + private companion object { + const val DEVICE_NAME = "device" + const val DEVICE_CONNECTION_SUMMARY = "active" + const val DEVICE_ADDRESS = "address" + const val GROUP_ID_1 = 1 + const val GROUP_ID_2 = 2 + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt index e02fb29d1070..89e09710eb94 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt @@ -30,7 +30,6 @@ import com.android.systemui.bouncer.data.repository.fakeKeyguardBouncerRepositor import com.android.systemui.communal.domain.interactor.communalInteractor import com.android.systemui.communal.domain.interactor.setCommunalAvailable import com.android.systemui.communal.shared.model.CommunalScenes -import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository import com.android.systemui.dock.fakeDockManager import com.android.systemui.flags.BrokenWithSceneContainer import com.android.systemui.flags.DisableSceneContainer @@ -608,31 +607,6 @@ class KeyguardTransitionScenariosTest(flags: FlagsParameterization?) : SysuiTest /** This handles security method NONE and screen off with lock timeout */ @Test - fun dozingToGoneWithKeyguardNotShowing() = - testScope.runTest { - // GIVEN a prior transition has run to DOZING - runTransitionAndSetWakefulness(KeyguardState.LOCKSCREEN, KeyguardState.DOZING) - runCurrent() - - // WHEN the device wakes up without a keyguard - keyguardRepository.setKeyguardShowing(false) - keyguardRepository.setKeyguardDismissible(true) - kosmos.fakeDeviceEntryRepository.setLockscreenEnabled(false) - powerInteractor.setAwakeForTest() - advanceTimeBy(60L) - - assertThat(transitionRepository) - .startedTransition( - to = KeyguardState.GONE, - from = KeyguardState.DOZING, - animatorAssertion = { it.isNotNull() } - ) - - coroutineContext.cancelChildren() - } - - /** This handles security method NONE and screen off with lock timeout */ - @Test @DisableSceneContainer fun dreamingToGoneWithKeyguardNotShowing() = testScope.runTest { diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java index ec02c6445b57..411ff91ebc2f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java @@ -113,8 +113,8 @@ public class MediaOutputAdapterTest extends SysuiTestCase { LocalMediaManager.MediaDeviceState.STATE_DISCONNECTED); mMediaDevices.add(mMediaDevice1); mMediaDevices.add(mMediaDevice2); - mMediaItems.add(new MediaItem(mMediaDevice1)); - mMediaItems.add(new MediaItem(mMediaDevice2)); + mMediaItems.add(MediaItem.createDeviceMediaItem(mMediaDevice1)); + mMediaItems.add(MediaItem.createDeviceMediaItem(mMediaDevice2)); mMediaOutputAdapter = new MediaOutputAdapter(mMediaOutputController); mMediaOutputAdapter.updateItems(); @@ -146,7 +146,8 @@ public class MediaOutputAdapterTest extends SysuiTestCase { mMediaOutputAdapter.updateItems(); mViewHolder = (MediaOutputAdapter.MediaDeviceViewHolder) mMediaOutputAdapter .onCreateViewHolder(new LinearLayout(mContext), 0); - mMediaItems.add(new MediaItem()); + mMediaItems.add(MediaItem.createPairNewDeviceMediaItem()); + mMediaItems.add(MediaItem.createPairNewDeviceMediaItem()); mMediaOutputAdapter.updateItems(); mMediaOutputAdapter.onBindViewHolder(mViewHolder, 2); @@ -589,7 +590,7 @@ public class MediaOutputAdapterTest extends SysuiTestCase { mMediaOutputAdapter.updateItems(); mViewHolder = (MediaOutputAdapter.MediaDeviceViewHolder) mMediaOutputAdapter .onCreateViewHolder(new LinearLayout(mContext), 0); - mMediaItems.add(new MediaItem()); + mMediaItems.add(MediaItem.createPairNewDeviceMediaItem()); mMediaOutputAdapter.updateItems(); mMediaOutputAdapter.onBindViewHolder(mViewHolder, 2); mViewHolder.mContainerLayout.performClick(); @@ -725,7 +726,7 @@ public class MediaOutputAdapterTest extends SysuiTestCase { public void updateItems_controllerItemsUpdated_notUpdatesInAdapterUntilUpdateItems() { mMediaOutputAdapter.updateItems(); List<MediaItem> updatedList = new ArrayList<>(); - updatedList.add(new MediaItem()); + updatedList.add(MediaItem.createPairNewDeviceMediaItem()); when(mMediaOutputController.getMediaItemList()).thenReturn(updatedList); assertThat(mMediaOutputAdapter.getItemCount()).isEqualTo(mMediaItems.size()); diff --git a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepositoryTest.kt index b7fefc0a202f..c0d411b12496 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/mediaprojection/data/repository/MediaProjectionManagerRepositoryTest.kt @@ -16,7 +16,9 @@ package com.android.systemui.mediaprojection.data.repository +import android.media.projection.MediaProjectionInfo import android.os.Binder +import android.os.UserHandle import android.view.ContentRecordingSession import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest @@ -26,6 +28,7 @@ import com.android.systemui.kosmos.testScope import com.android.systemui.mediaprojection.data.model.MediaProjectionState import com.android.systemui.mediaprojection.taskswitcher.FakeActivityTaskManager.Companion.createTask import com.android.systemui.mediaprojection.taskswitcher.FakeActivityTaskManager.Companion.createToken +import com.android.systemui.mediaprojection.taskswitcher.FakeMediaProjectionManager.Companion.createDisplaySession import com.android.systemui.mediaprojection.taskswitcher.fakeActivityTaskManager import com.android.systemui.mediaprojection.taskswitcher.fakeMediaProjectionManager import com.android.systemui.mediaprojection.taskswitcher.taskSwitcherKosmos @@ -33,6 +36,7 @@ import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith +import org.mockito.kotlin.verify @RunWith(AndroidJUnit4::class) @SmallTest @@ -55,7 +59,8 @@ class MediaProjectionManagerRepositoryTest : SysuiTestCase() { fakeActivityTaskManager.addRunningTasks(task) repo.switchProjectedTask(task) - assertThat(state).isEqualTo(MediaProjectionState.SingleTask(task)) + assertThat(state).isInstanceOf(MediaProjectionState.Projecting.SingleTask::class.java) + assertThat((state as MediaProjectionState.Projecting.SingleTask).task).isEqualTo(task) } @Test @@ -97,7 +102,7 @@ class MediaProjectionManagerRepositoryTest : SysuiTestCase() { session = ContentRecordingSession.createDisplaySession(/* displayToMirror= */ 123) ) - assertThat(state).isEqualTo(MediaProjectionState.EntireScreen) + assertThat(state).isInstanceOf(MediaProjectionState.Projecting.EntireScreen::class.java) } @Test @@ -110,7 +115,27 @@ class MediaProjectionManagerRepositoryTest : SysuiTestCase() { session = ContentRecordingSession.createTaskSession(taskWindowContainerToken) ) - assertThat(state).isEqualTo(MediaProjectionState.EntireScreen) + assertThat(state).isInstanceOf(MediaProjectionState.Projecting.EntireScreen::class.java) + } + + @Test + fun mediaProjectionState_entireScreen_hasHostPackage() = + testScope.runTest { + val state by collectLastValue(repo.mediaProjectionState) + + val info = + MediaProjectionInfo( + /* packageName= */ "com.media.projection.repository.test", + /* handle= */ UserHandle.getUserHandleForUid(UserHandle.myUserId()), + /* launchCookie = */ null, + ) + fakeMediaProjectionManager.dispatchOnSessionSet( + info = info, + session = createDisplaySession(), + ) + + assertThat((state as MediaProjectionState.Projecting.EntireScreen).hostPackage) + .isEqualTo("com.media.projection.repository.test") } @Test @@ -125,6 +150,39 @@ class MediaProjectionManagerRepositoryTest : SysuiTestCase() { session = ContentRecordingSession.createTaskSession(token.asBinder()) ) - assertThat(state).isEqualTo(MediaProjectionState.SingleTask(task)) + assertThat(state).isInstanceOf(MediaProjectionState.Projecting.SingleTask::class.java) + assertThat((state as MediaProjectionState.Projecting.SingleTask).task).isEqualTo(task) + } + + @Test + fun mediaProjectionState_singleTask_hasHostPackage() = + testScope.runTest { + val state by collectLastValue(repo.mediaProjectionState) + + val token = createToken() + val task = createTask(taskId = 1, token = token) + fakeActivityTaskManager.addRunningTasks(task) + + val info = + MediaProjectionInfo( + /* packageName= */ "com.media.projection.repository.test", + /* handle= */ UserHandle.getUserHandleForUid(UserHandle.myUserId()), + /* launchCookie = */ null, + ) + fakeMediaProjectionManager.dispatchOnSessionSet( + info = info, + session = ContentRecordingSession.createTaskSession(token.asBinder()) + ) + + assertThat((state as MediaProjectionState.Projecting.SingleTask).hostPackage) + .isEqualTo("com.media.projection.repository.test") + } + + @Test + fun stopProjecting_invokesManager() = + testScope.runTest { + repo.stopProjecting() + + verify(fakeMediaProjectionManager.mediaProjectionManager).stopActiveProjection() } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/data/repository/ScreenRecordRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/data/repository/ScreenRecordRepositoryTest.kt index b77a15b43bea..61ea437f10dd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/screenrecord/data/repository/ScreenRecordRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/screenrecord/data/repository/ScreenRecordRepositoryTest.kt @@ -119,4 +119,12 @@ class ScreenRecordRepositoryTest : SysuiTestCase() { assertThat(lastModel).isEqualTo(isRecording) } + + @Test + fun stopRecording_invokesController() = + testScope.runTest { + underTest.stopRecording() + + verify(recordingController).stopRecording() + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt index 586adbd65ce8..74a299910b18 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt @@ -70,6 +70,7 @@ import com.android.systemui.statusbar.phone.DozeServiceHost import com.android.systemui.statusbar.phone.PhoneStatusBarViewController import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager import com.android.systemui.statusbar.window.StatusBarWindowStateController +import com.android.systemui.unfold.SysUIUnfoldComponent import com.android.systemui.unfold.UnfoldTransitionProgressProvider import com.android.systemui.user.domain.interactor.SelectedUserInteractor import com.android.systemui.util.mockito.any @@ -85,6 +86,7 @@ import org.junit.Assert.assertEquals import org.junit.Before import org.junit.Test import org.junit.runner.RunWith +import org.mockito.Answers import org.mockito.ArgumentCaptor import org.mockito.Mock import org.mockito.Mockito.anyFloat @@ -132,6 +134,8 @@ class NotificationShadeWindowViewControllerTest(flags: FlagsParameterization) : private lateinit var mLockscreenHostedDreamGestureListener: LockscreenHostedDreamGestureListener @Mock private lateinit var notificationInsetsController: NotificationInsetsController @Mock private lateinit var mGlanceableHubContainerController: GlanceableHubContainerController + @Mock(answer = Answers.RETURNS_DEEP_STUBS) + private lateinit var sysUiUnfoldComponent: SysUIUnfoldComponent @Mock lateinit var keyguardBouncerComponentFactory: KeyguardBouncerComponent.Factory @Mock lateinit var keyguardBouncerComponent: KeyguardBouncerComponent @Mock lateinit var keyguardSecurityContainerController: KeyguardSecurityContainerController @@ -209,6 +213,7 @@ class NotificationShadeWindowViewControllerTest(flags: FlagsParameterization) : dozeScrimController, notificationShadeWindowController, unfoldTransitionProgressProvider, + Optional.of(sysUiUnfoldComponent), keyguardUnlockAnimationController, notificationInsetsController, ambientState, diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt index e83a46bb56a0..31bd12f9a168 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt @@ -58,6 +58,7 @@ import com.android.systemui.statusbar.phone.DozeScrimController import com.android.systemui.statusbar.phone.DozeServiceHost import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager import com.android.systemui.statusbar.window.StatusBarWindowStateController +import com.android.systemui.unfold.SysUIUnfoldComponent import com.android.systemui.unfold.UnfoldTransitionProgressProvider import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.mock @@ -112,6 +113,7 @@ class NotificationShadeWindowViewTest : SysuiTestCase() { @Mock private lateinit var shadeLogger: ShadeLogger @Mock private lateinit var dumpManager: DumpManager @Mock private lateinit var pulsingGestureListener: PulsingGestureListener + @Mock private lateinit var sysUiUnfoldComponent: SysUIUnfoldComponent @Mock private lateinit var mLockscreenHostedDreamGestureListener: LockscreenHostedDreamGestureListener @Mock private lateinit var keyguardBouncerComponentFactory: KeyguardBouncerComponent.Factory @@ -181,6 +183,7 @@ class NotificationShadeWindowViewTest : SysuiTestCase() { dozeScrimController, notificationShadeWindowController, unfoldTransitionProgressProvider, + Optional.of(sysUiUnfoldComponent), keyguardUnlockAnimationController, notificationInsetsController, ambientState, diff --git a/packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimatorTest.kt index a05a23bb8bb1..293dc048af1d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimatorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shared/animation/UnfoldConstantTranslateAnimatorTest.kt @@ -27,6 +27,9 @@ import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock +import org.mockito.Mockito.anyFloat +import org.mockito.Mockito.spy +import org.mockito.Mockito.verify import org.mockito.Mockito.`when` as whenever import org.mockito.MockitoAnnotations @@ -78,6 +81,22 @@ class UnfoldConstantTranslateAnimatorTest : SysuiTestCase() { } @Test + fun initMultipleTimes_onTransition_translationIsSetOnlyOnce() { + animator.init(parent, MAX_TRANSLATION) + animator.init(parent, MAX_TRANSLATION) + animator.init(parent, MAX_TRANSLATION) + + // GIVEN one view with a matching id + val view = spy(View(context)) + whenever(parent.findViewById<View>(START_VIEW_ID)).thenReturn(view) + progressProvider.onTransitionStarted() + + // WHEN the transition progresses, translation is updated once + progressProvider.onTransitionProgress(.5f) + verify(view).translationX = anyFloat() + } + + @Test fun onTransition_oneMovesStartWithRTL() { // GIVEN one view with a matching id val view = View(context) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/domain/interactor/OngoingActivityChipInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/domain/interactor/OngoingActivityChipInteractorTest.kt new file mode 100644 index 000000000000..abb6e2b02103 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/domain/interactor/OngoingActivityChipInteractorTest.kt @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.chips.domain.interactor + +import android.view.View +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.animation.DialogTransitionAnimator +import com.android.systemui.res.R +import com.android.systemui.statusbar.chips.domain.interactor.OngoingActivityChipInteractor.Companion.createDialogLaunchOnClickListener +import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer +import com.android.systemui.statusbar.phone.SystemUIDialog +import kotlin.test.Test +import org.mockito.ArgumentMatchers.anyBoolean +import org.mockito.kotlin.eq +import org.mockito.kotlin.mock +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever + +@SmallTest +class OngoingActivityChipInteractorTest : SysuiTestCase() { + private val mockSystemUIDialog = mock<SystemUIDialog>() + private val dialogDelegate = SystemUIDialog.Delegate { mockSystemUIDialog } + private val dialogTransitionAnimator = mock<DialogTransitionAnimator>() + + private val chipBackgroundView = mock<ChipBackgroundContainer>() + private val chipView = + mock<View>().apply { + whenever( + this.requireViewById<ChipBackgroundContainer>( + R.id.ongoing_activity_chip_background + ) + ) + .thenReturn(chipBackgroundView) + } + + @Test + fun createDialogLaunchOnClickListener_showsDialogOnClick() { + val clickListener = + createDialogLaunchOnClickListener(dialogDelegate, dialogTransitionAnimator) + + // Dialogs must be created on the main thread + context.mainExecutor.execute { + clickListener.onClick(chipView) + verify(dialogTransitionAnimator) + .showFromView( + eq(mockSystemUIDialog), + eq(chipBackgroundView), + eq(null), + anyBoolean(), + ) + } + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractorTest.kt index 0f33b9dfc077..a4505a99cb77 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractorTest.kt @@ -16,8 +16,15 @@ package com.android.systemui.statusbar.chips.mediaprojection.domain.interactor +import android.Manifest +import android.content.Intent +import android.content.packageManager +import android.content.pm.PackageManager +import android.content.pm.ResolveInfo +import android.view.View import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase +import com.android.systemui.animation.mockDialogTransitionAnimator import com.android.systemui.common.shared.model.Icon import com.android.systemui.coroutines.collectLastValue import com.android.systemui.kosmos.Kosmos @@ -27,11 +34,24 @@ import com.android.systemui.mediaprojection.data.repository.fakeMediaProjectionR import com.android.systemui.mediaprojection.taskswitcher.FakeActivityTaskManager.Companion.createTask import com.android.systemui.res.R import com.android.systemui.statusbar.chips.domain.model.OngoingActivityChipModel -import com.android.systemui.statusbar.chips.ui.viewmodel.mediaProjectionChipInteractor +import com.android.systemui.statusbar.chips.mediaprojection.ui.view.EndCastToOtherDeviceDialogDelegate +import com.android.systemui.statusbar.chips.mediaprojection.ui.view.EndShareToAppDialogDelegate +import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer +import com.android.systemui.statusbar.phone.SystemUIDialog +import com.android.systemui.statusbar.phone.mockSystemUIDialogFactory import com.android.systemui.util.time.fakeSystemClock import com.google.common.truth.Truth.assertThat import kotlin.test.Test import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.mockito.ArgumentMatchers.anyBoolean +import org.mockito.ArgumentMatchers.anyInt +import org.mockito.Mockito.doAnswer +import org.mockito.kotlin.any +import org.mockito.kotlin.eq +import org.mockito.kotlin.mock +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever @SmallTest class MediaProjectionChipInteractorTest : SysuiTestCase() { @@ -40,6 +60,30 @@ class MediaProjectionChipInteractorTest : SysuiTestCase() { private val mediaProjectionRepo = kosmos.fakeMediaProjectionRepository private val systemClock = kosmos.fakeSystemClock + private val mockCastDialog = mock<SystemUIDialog>() + private val mockShareDialog = mock<SystemUIDialog>() + + private val chipBackgroundView = mock<ChipBackgroundContainer>() + private val chipView = + mock<View>().apply { + whenever( + this.requireViewById<ChipBackgroundContainer>( + R.id.ongoing_activity_chip_background + ) + ) + .thenReturn(chipBackgroundView) + } + + @Before + fun setUp() { + setUpPackageManagerForMediaProjection(kosmos) + + whenever(kosmos.mockSystemUIDialogFactory.create(any<EndCastToOtherDeviceDialogDelegate>())) + .thenReturn(mockCastDialog) + whenever(kosmos.mockSystemUIDialogFactory.create(any<EndShareToAppDialogDelegate>())) + .thenReturn(mockShareDialog) + } + private val underTest = kosmos.mediaProjectionChipInteractor @Test @@ -53,12 +97,15 @@ class MediaProjectionChipInteractorTest : SysuiTestCase() { } @Test - fun chip_singleTaskState_isShownWithIcon() = + fun chip_singleTaskState_otherDevicesPackage_castToOtherDeviceChipShown() = testScope.runTest { val latest by collectLastValue(underTest.chip) mediaProjectionRepo.mediaProjectionState.value = - MediaProjectionState.SingleTask(createTask(taskId = 1)) + MediaProjectionState.Projecting.SingleTask( + CAST_TO_OTHER_DEVICES_PACKAGE, + createTask(taskId = 1) + ) assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java) val icon = (latest as OngoingActivityChipModel.Shown).icon @@ -66,11 +113,12 @@ class MediaProjectionChipInteractorTest : SysuiTestCase() { } @Test - fun chip_entireScreenState_isShownWithIcon() = + fun chip_entireScreenState_otherDevicesPackage_castToOtherDeviceChipShown() = testScope.runTest { val latest by collectLastValue(underTest.chip) - mediaProjectionRepo.mediaProjectionState.value = MediaProjectionState.EntireScreen + mediaProjectionRepo.mediaProjectionState.value = + MediaProjectionState.Projecting.EntireScreen(CAST_TO_OTHER_DEVICES_PACKAGE) assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java) val icon = (latest as OngoingActivityChipModel.Shown).icon @@ -78,12 +126,39 @@ class MediaProjectionChipInteractorTest : SysuiTestCase() { } @Test + fun chip_singleTaskState_normalPackage_shareToAppChipShown() = + testScope.runTest { + val latest by collectLastValue(underTest.chip) + + mediaProjectionRepo.mediaProjectionState.value = + MediaProjectionState.Projecting.SingleTask(NORMAL_PACKAGE, createTask(taskId = 1)) + + assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java) + val icon = (latest as OngoingActivityChipModel.Shown).icon + assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.ic_screenshot_share) + } + + @Test + fun chip_entireScreenState_normalPackage_shareToAppChipShown() = + testScope.runTest { + val latest by collectLastValue(underTest.chip) + + mediaProjectionRepo.mediaProjectionState.value = + MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE) + + assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java) + val icon = (latest as OngoingActivityChipModel.Shown).icon + assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.ic_screenshot_share) + } + + @Test fun chip_timeResetsOnEachNewShare() = testScope.runTest { val latest by collectLastValue(underTest.chip) systemClock.setElapsedRealtime(1234) - mediaProjectionRepo.mediaProjectionState.value = MediaProjectionState.EntireScreen + mediaProjectionRepo.mediaProjectionState.value = + MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE) assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java) assertThat((latest as OngoingActivityChipModel.Shown).startTimeMs).isEqualTo(1234) @@ -92,9 +167,99 @@ class MediaProjectionChipInteractorTest : SysuiTestCase() { assertThat(latest).isInstanceOf(OngoingActivityChipModel.Hidden::class.java) systemClock.setElapsedRealtime(5678) - mediaProjectionRepo.mediaProjectionState.value = MediaProjectionState.EntireScreen + mediaProjectionRepo.mediaProjectionState.value = + MediaProjectionState.Projecting.SingleTask( + CAST_TO_OTHER_DEVICES_PACKAGE, + createTask(taskId = 1) + ) assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java) assertThat((latest as OngoingActivityChipModel.Shown).startTimeMs).isEqualTo(5678) } + + @Test + fun chip_castToOtherDevice_clickListenerShowsCastDialog() = + testScope.runTest { + val latest by collectLastValue(underTest.chip) + mediaProjectionRepo.mediaProjectionState.value = + MediaProjectionState.Projecting.EntireScreen(CAST_TO_OTHER_DEVICES_PACKAGE) + + val clickListener = ((latest as OngoingActivityChipModel.Shown).onClickListener) + + // Dialogs must be created on the main thread + context.mainExecutor.execute { + clickListener.onClick(chipView) + verify(kosmos.mockDialogTransitionAnimator) + .showFromView( + eq(mockCastDialog), + eq(chipBackgroundView), + eq(null), + anyBoolean(), + ) + } + } + + @Test + fun chip_shareToApp_clickListenerShowsShareDialog() = + testScope.runTest { + val latest by collectLastValue(underTest.chip) + mediaProjectionRepo.mediaProjectionState.value = + MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE) + + val clickListener = ((latest as OngoingActivityChipModel.Shown).onClickListener) + + // Dialogs must be created on the main thread + context.mainExecutor.execute { + clickListener.onClick(chipView) + verify(kosmos.mockDialogTransitionAnimator) + .showFromView( + eq(mockShareDialog), + eq(chipBackgroundView), + eq(null), + anyBoolean(), + ) + } + } + + companion object { + const val CAST_TO_OTHER_DEVICES_PACKAGE = "other.devices.package" + const val NORMAL_PACKAGE = "some.normal.package" + + /** + * Sets up [kosmos.packageManager] so that [CAST_TO_OTHER_DEVICES_PACKAGE] is marked as a + * package that casts to other devices, and [NORMAL_PACKAGE] is *not* marked as casting to + * other devices. + */ + fun setUpPackageManagerForMediaProjection(kosmos: Kosmos) { + kosmos.packageManager.apply { + whenever( + this.checkPermission( + Manifest.permission.REMOTE_DISPLAY_PROVIDER, + CAST_TO_OTHER_DEVICES_PACKAGE + ) + ) + .thenReturn(PackageManager.PERMISSION_GRANTED) + whenever( + this.checkPermission( + Manifest.permission.REMOTE_DISPLAY_PROVIDER, + NORMAL_PACKAGE + ) + ) + .thenReturn(PackageManager.PERMISSION_DENIED) + + doAnswer { + // See Utils.isHeadlessRemoteDisplayProvider + if ( + (it.arguments[0] as Intent).`package` == CAST_TO_OTHER_DEVICES_PACKAGE + ) { + emptyList() + } else { + listOf(mock<ResolveInfo>()) + } + } + .whenever(this) + .queryIntentActivities(any(), anyInt()) + } + } + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndCastToOtherDeviceDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndCastToOtherDeviceDialogDelegateTest.kt new file mode 100644 index 000000000000..9a2f545fa67e --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndCastToOtherDeviceDialogDelegateTest.kt @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.chips.mediaprojection.ui.view + +import android.content.DialogInterface +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.testScope +import com.android.systemui.mediaprojection.data.repository.fakeMediaProjectionRepository +import com.android.systemui.res.R +import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.mediaProjectionChipInteractor +import com.android.systemui.statusbar.phone.SystemUIDialog +import com.android.systemui.statusbar.phone.mockSystemUIDialogFactory +import com.google.common.truth.Truth.assertThat +import kotlin.test.Test +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.mockito.kotlin.argumentCaptor +import org.mockito.kotlin.eq +import org.mockito.kotlin.mock +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever + +@SmallTest +@OptIn(ExperimentalCoroutinesApi::class) +class EndCastToOtherDeviceDialogDelegateTest : SysuiTestCase() { + private val kosmos = Kosmos() + private val sysuiDialog = mock<SystemUIDialog>() + private val sysuiDialogFactory = kosmos.mockSystemUIDialogFactory + private val underTest = + EndCastToOtherDeviceDialogDelegate( + sysuiDialogFactory, + kosmos.mediaProjectionChipInteractor, + ) + + @Before + fun setUp() { + whenever(sysuiDialogFactory.create(eq(underTest), eq(context))).thenReturn(sysuiDialog) + } + + @Test + fun icon() { + underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null) + + verify(sysuiDialog).setIcon(R.drawable.ic_cast_connected) + } + + @Test + fun title() { + underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null) + + verify(sysuiDialog).setTitle(R.string.cast_to_other_device_stop_dialog_title) + } + + @Test + fun message() { + underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null) + + verify(sysuiDialog).setMessage(R.string.cast_to_other_device_stop_dialog_message) + } + + @Test + fun negativeButton() { + underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null) + + verify(sysuiDialog).setNegativeButton(R.string.close_dialog_button, null) + } + + @Test + fun positiveButton() = + kosmos.testScope.runTest { + underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null) + + val clickListener = argumentCaptor<DialogInterface.OnClickListener>() + + // Verify the button has the right text + verify(sysuiDialog) + .setPositiveButton( + eq(R.string.cast_to_other_device_stop_dialog_button), + clickListener.capture() + ) + + // Verify that clicking the button stops the recording + assertThat(kosmos.fakeMediaProjectionRepository.stopProjectingInvoked).isFalse() + + clickListener.firstValue.onClick(mock<DialogInterface>(), 0) + runCurrent() + + assertThat(kosmos.fakeMediaProjectionRepository.stopProjectingInvoked).isTrue() + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndShareToAppDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndShareToAppDialogDelegateTest.kt new file mode 100644 index 000000000000..1d6e8669274d --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/mediaprojection/ui/view/EndShareToAppDialogDelegateTest.kt @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.chips.mediaprojection.ui.view + +import android.content.DialogInterface +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.testScope +import com.android.systemui.mediaprojection.data.repository.fakeMediaProjectionRepository +import com.android.systemui.res.R +import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.mediaProjectionChipInteractor +import com.android.systemui.statusbar.phone.SystemUIDialog +import com.android.systemui.statusbar.phone.mockSystemUIDialogFactory +import com.google.common.truth.Truth.assertThat +import kotlin.test.Test +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.mockito.kotlin.argumentCaptor +import org.mockito.kotlin.eq +import org.mockito.kotlin.mock +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever + +@SmallTest +@OptIn(ExperimentalCoroutinesApi::class) +class EndShareToAppDialogDelegateTest : SysuiTestCase() { + private val kosmos = Kosmos() + private val sysuiDialog = mock<SystemUIDialog>() + private val sysuiDialogFactory = kosmos.mockSystemUIDialogFactory + private val underTest = + EndShareToAppDialogDelegate( + sysuiDialogFactory, + kosmos.mediaProjectionChipInteractor, + ) + + @Before + fun setUp() { + whenever(sysuiDialogFactory.create(eq(underTest), eq(context))).thenReturn(sysuiDialog) + } + + @Test + fun icon() { + underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null) + + verify(sysuiDialog).setIcon(R.drawable.ic_screenshot_share) + } + + @Test + fun title() { + underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null) + + verify(sysuiDialog).setTitle(R.string.share_to_app_stop_dialog_title) + } + + @Test + fun message() { + underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null) + + verify(sysuiDialog).setMessage(R.string.share_to_app_stop_dialog_message) + } + + @Test + fun negativeButton() { + underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null) + + verify(sysuiDialog).setNegativeButton(R.string.close_dialog_button, null) + } + + @Test + fun positiveButton() = + kosmos.testScope.runTest { + underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null) + + val clickListener = argumentCaptor<DialogInterface.OnClickListener>() + + // Verify the button has the right text + verify(sysuiDialog) + .setPositiveButton( + eq(R.string.share_to_app_stop_dialog_button), + clickListener.capture() + ) + + // Verify that clicking the button stops the recording + assertThat(kosmos.fakeMediaProjectionRepository.stopProjectingInvoked).isFalse() + + clickListener.firstValue.onClick(mock<DialogInterface>(), 0) + runCurrent() + + assertThat(kosmos.fakeMediaProjectionRepository.stopProjectingInvoked).isTrue() + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractorTest.kt index 25efaf10fce7..f6c3adb364ce 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/screenrecord/domain/interactor/ScreenRecordChipInteractorTest.kt @@ -16,8 +16,10 @@ package com.android.systemui.statusbar.chips.screenrecord.domain.interactor +import android.view.View import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase +import com.android.systemui.animation.mockDialogTransitionAnimator import com.android.systemui.common.shared.model.Icon import com.android.systemui.coroutines.collectLastValue import com.android.systemui.kosmos.Kosmos @@ -26,11 +28,21 @@ import com.android.systemui.res.R import com.android.systemui.screenrecord.data.model.ScreenRecordModel import com.android.systemui.screenrecord.data.repository.screenRecordRepository import com.android.systemui.statusbar.chips.domain.model.OngoingActivityChipModel +import com.android.systemui.statusbar.chips.ui.view.ChipBackgroundContainer import com.android.systemui.statusbar.chips.ui.viewmodel.screenRecordChipInteractor +import com.android.systemui.statusbar.phone.SystemUIDialog +import com.android.systemui.statusbar.phone.mockSystemUIDialogFactory import com.android.systemui.util.time.fakeSystemClock import com.google.common.truth.Truth.assertThat import kotlin.test.Test import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.mockito.ArgumentMatchers.anyBoolean +import org.mockito.kotlin.any +import org.mockito.kotlin.eq +import org.mockito.kotlin.mock +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever @SmallTest class ScreenRecordChipInteractorTest : SysuiTestCase() { @@ -38,9 +50,27 @@ class ScreenRecordChipInteractorTest : SysuiTestCase() { private val testScope = kosmos.testScope private val screenRecordRepo = kosmos.screenRecordRepository private val systemClock = kosmos.fakeSystemClock + private val mockSystemUIDialog = mock<SystemUIDialog>() + + private val chipBackgroundView = mock<ChipBackgroundContainer>() + private val chipView = + mock<View>().apply { + whenever( + this.requireViewById<ChipBackgroundContainer>( + R.id.ongoing_activity_chip_background + ) + ) + .thenReturn(chipBackgroundView) + } private val underTest = kosmos.screenRecordChipInteractor + @Before + fun setUp() { + whenever(kosmos.mockSystemUIDialogFactory.create(any<SystemUIDialog.Delegate>())) + .thenReturn(mockSystemUIDialog) + } + @Test fun chip_doingNothingState_isHidden() = testScope.runTest { @@ -70,7 +100,7 @@ class ScreenRecordChipInteractorTest : SysuiTestCase() { assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java) val icon = (latest as OngoingActivityChipModel.Shown).icon - assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.stat_sys_screen_record) + assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.ic_screenrecord) } @Test @@ -93,4 +123,25 @@ class ScreenRecordChipInteractorTest : SysuiTestCase() { assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java) assertThat((latest as OngoingActivityChipModel.Shown).startTimeMs).isEqualTo(5678) } + + @Test + fun chip_clickListenerShowsDialog() = + testScope.runTest { + val latest by collectLastValue(underTest.chip) + screenRecordRepo.screenRecordState.value = ScreenRecordModel.Recording + + val clickListener = ((latest as OngoingActivityChipModel.Shown).onClickListener) + + // Dialogs must be created on the main thread + context.mainExecutor.execute { + clickListener.onClick(chipView) + verify(kosmos.mockDialogTransitionAnimator) + .showFromView( + eq(mockSystemUIDialog), + eq(chipBackgroundView), + eq(null), + anyBoolean(), + ) + } + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/screenrecord/ui/view/EndScreenRecordingDialogDelegateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/screenrecord/ui/view/EndScreenRecordingDialogDelegateTest.kt new file mode 100644 index 000000000000..bca6763a4989 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/screenrecord/ui/view/EndScreenRecordingDialogDelegateTest.kt @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.chips.screenrecord.ui.view + +import android.content.DialogInterface +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.testScope +import com.android.systemui.res.R +import com.android.systemui.screenrecord.data.repository.screenRecordRepository +import com.android.systemui.statusbar.chips.ui.viewmodel.screenRecordChipInteractor +import com.android.systemui.statusbar.phone.SystemUIDialog +import com.android.systemui.statusbar.phone.mockSystemUIDialogFactory +import com.google.common.truth.Truth.assertThat +import kotlin.test.Test +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.mockito.kotlin.argumentCaptor +import org.mockito.kotlin.eq +import org.mockito.kotlin.mock +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever + +@SmallTest +@OptIn(ExperimentalCoroutinesApi::class) +class EndScreenRecordingDialogDelegateTest : SysuiTestCase() { + private val kosmos = Kosmos() + + private val sysuiDialog = mock<SystemUIDialog>() + private val sysuiDialogFactory = kosmos.mockSystemUIDialogFactory + + private val underTest = + EndScreenRecordingDialogDelegate( + sysuiDialogFactory, + kosmos.screenRecordChipInteractor, + ) + + @Before + fun setUp() { + whenever(sysuiDialogFactory.create(eq(underTest), eq(context))).thenReturn(sysuiDialog) + } + + @Test + fun icon() { + underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null) + + verify(sysuiDialog).setIcon(R.drawable.ic_screenrecord) + } + + @Test + fun title() { + underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null) + + verify(sysuiDialog).setTitle(R.string.screenrecord_stop_dialog_title) + } + + @Test + fun message() { + underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null) + + verify(sysuiDialog).setMessage(R.string.screenrecord_stop_dialog_message) + } + + @Test + fun negativeButton() { + underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null) + + verify(sysuiDialog).setNegativeButton(R.string.close_dialog_button, null) + } + + @Test + fun positiveButton() = + kosmos.testScope.runTest { + underTest.beforeCreate(sysuiDialog, /* savedInstanceState= */ null) + + val clickListener = argumentCaptor<DialogInterface.OnClickListener>() + + // Verify the button has the right text + verify(sysuiDialog) + .setPositiveButton( + eq(R.string.screenrecord_stop_dialog_button), + clickListener.capture() + ) + + // Verify that clicking the button stops the recording + assertThat(kosmos.screenRecordRepository.stopRecordingInvoked).isFalse() + + clickListener.firstValue.onClick(mock<DialogInterface>(), 0) + runCurrent() + + assertThat(kosmos.screenRecordRepository.stopRecordingInvoked).isTrue() + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt index 121229c321b6..67129639efaf 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelTest.kt @@ -30,8 +30,11 @@ import com.android.systemui.res.R import com.android.systemui.screenrecord.data.model.ScreenRecordModel import com.android.systemui.screenrecord.data.repository.screenRecordRepository import com.android.systemui.statusbar.chips.domain.model.OngoingActivityChipModel +import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.NORMAL_PACKAGE +import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.setUpPackageManagerForMediaProjection import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.test.runTest +import org.junit.Before import org.junit.Test @SmallTest @@ -46,6 +49,11 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() { private val underTest = kosmos.ongoingActivityChipsViewModel + @Before + fun setUp() { + setUpPackageManagerForMediaProjection(kosmos) + } + @Test fun chip_allHidden_hidden() = testScope.runTest { @@ -91,7 +99,8 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() { fun chip_screenRecordShowAndMediaProjectionShow_screenRecordShown() = testScope.runTest { screenRecordState.value = ScreenRecordModel.Recording - mediaProjectionState.value = MediaProjectionState.EntireScreen + mediaProjectionState.value = + MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE) callState.value = OngoingActivityChipModel.Hidden val latest by collectLastValue(underTest.chip) @@ -103,7 +112,8 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() { fun chip_mediaProjectionShowAndCallShow_mediaProjectionShown() = testScope.runTest { screenRecordState.value = ScreenRecordModel.DoingNothing - mediaProjectionState.value = MediaProjectionState.EntireScreen + mediaProjectionState.value = + MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE) val callChip = OngoingActivityChipModel.Shown( Icon.Resource(R.drawable.ic_call, ContentDescription.Loaded("icon")), @@ -113,7 +123,7 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() { val latest by collectLastValue(underTest.chip) - assertIsMediaProjectionChip(latest) + assertIsShareToAppChip(latest) } @Test @@ -152,10 +162,14 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() { assertThat(latest).isEqualTo(callChip) // WHEN the higher priority media projection chip is added - mediaProjectionState.value = MediaProjectionState.SingleTask(createTask(taskId = 1)) + mediaProjectionState.value = + MediaProjectionState.Projecting.SingleTask( + NORMAL_PACKAGE, + createTask(taskId = 1), + ) // THEN the higher priority media projection chip is used - assertIsMediaProjectionChip(latest) + assertIsShareToAppChip(latest) // WHEN the higher priority screen record chip is added screenRecordState.value = ScreenRecordModel.Recording @@ -169,7 +183,8 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() { testScope.runTest { // WHEN all chips are active screenRecordState.value = ScreenRecordModel.Recording - mediaProjectionState.value = MediaProjectionState.EntireScreen + mediaProjectionState.value = + MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE) val callChip = OngoingActivityChipModel.Shown( @@ -187,7 +202,7 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() { screenRecordState.value = ScreenRecordModel.DoingNothing // THEN the lower priority media projection is used - assertIsMediaProjectionChip(latest) + assertIsShareToAppChip(latest) // WHEN the higher priority media projection is removed mediaProjectionState.value = MediaProjectionState.NotProjecting @@ -200,13 +215,13 @@ class OngoingActivityChipsViewModelTest : SysuiTestCase() { fun assertIsScreenRecordChip(latest: OngoingActivityChipModel?) { assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java) val icon = (latest as OngoingActivityChipModel.Shown).icon - assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.stat_sys_screen_record) + assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.ic_screenrecord) } - fun assertIsMediaProjectionChip(latest: OngoingActivityChipModel?) { + fun assertIsShareToAppChip(latest: OngoingActivityChipModel?) { assertThat(latest).isInstanceOf(OngoingActivityChipModel.Shown::class.java) val icon = (latest as OngoingActivityChipModel.Shown).icon - assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.ic_cast_connected) + assertThat((icon as Icon.Resource).res).isEqualTo(R.drawable.ic_screenshot_share) } } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java index f3d640758cf3..53e643e00be4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java @@ -223,7 +223,8 @@ public class KeyguardStatusBarViewControllerTest extends SysuiTestCase { mFakeExecutor, mBackgroundExecutor, mLogger, - mStatusOverlayHoverListenerFactory + mStatusOverlayHoverListenerFactory, + mKosmos.getCommunalSceneInteractor() ); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt index c9fe44918757..cdb2b883078a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt @@ -37,8 +37,10 @@ import com.android.systemui.mediaprojection.data.repository.fakeMediaProjectionR import com.android.systemui.screenrecord.data.model.ScreenRecordModel import com.android.systemui.screenrecord.data.repository.screenRecordRepository import com.android.systemui.statusbar.chips.domain.model.OngoingActivityChipModel -import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipsViewModelTest.Companion.assertIsMediaProjectionChip +import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.NORMAL_PACKAGE +import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractorTest.Companion.setUpPackageManagerForMediaProjection import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipsViewModelTest.Companion.assertIsScreenRecordChip +import com.android.systemui.statusbar.chips.ui.viewmodel.OngoingActivityChipsViewModelTest.Companion.assertIsShareToAppChip import com.android.systemui.statusbar.chips.ui.viewmodel.ongoingActivityChipsViewModel import com.android.systemui.statusbar.data.model.StatusBarMode import com.android.systemui.statusbar.data.repository.FakeStatusBarModeRepository.Companion.DISPLAY_ID @@ -55,6 +57,7 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.test.UnconfinedTestDispatcher import kotlinx.coroutines.test.runTest +import org.junit.Before import org.junit.Test @SmallTest @@ -77,6 +80,11 @@ class CollapsedStatusBarViewModelImplTest : SysuiTestCase() { kosmos.applicationCoroutineScope, ) + @Before + fun setUp() { + setUpPackageManagerForMediaProjection(kosmos) + } + @Test fun isTransitioningFromLockscreenToOccluded_started_isTrue() = testScope.runTest { @@ -405,9 +413,9 @@ class CollapsedStatusBarViewModelImplTest : SysuiTestCase() { assertThat(latest).isEqualTo(OngoingActivityChipModel.Hidden) kosmos.fakeMediaProjectionRepository.mediaProjectionState.value = - MediaProjectionState.EntireScreen + MediaProjectionState.Projecting.EntireScreen(NORMAL_PACKAGE) - assertIsMediaProjectionChip(latest) + assertIsShareToAppChip(latest) } private fun activeNotificationsStore(notifications: List<ActiveNotificationModel>) = diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/animation/DialogTransitionAnimatorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/animation/DialogTransitionAnimatorKosmos.kt index 62e56beccc93..976a19ce6265 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/animation/DialogTransitionAnimatorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/animation/DialogTransitionAnimatorKosmos.kt @@ -20,6 +20,7 @@ import android.content.applicationContext import com.android.systemui.jank.interactionJankMonitor import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture +import com.android.systemui.util.mockito.mock val Kosmos.dialogTransitionAnimator by Fixture { fakeDialogTransitionAnimator( @@ -29,3 +30,5 @@ val Kosmos.dialogTransitionAnimator by Fixture { interactionJankMonitor = interactionJankMonitor, ) } + +val Kosmos.mockDialogTransitionAnimator by Fixture { mock<DialogTransitionAnimator>() } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorFactory.kt index 02842cc3f56b..b5ca964d6968 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorFactory.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorFactory.kt @@ -24,6 +24,7 @@ import com.android.systemui.flags.FakeFeatureFlags import com.android.systemui.keyguard.data.repository.FakeCommandQueue import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository import com.android.systemui.keyguard.shared.model.KeyguardState +import com.android.systemui.keyguard.shared.model.TransitionStep import com.android.systemui.power.domain.interactor.PowerInteractor import com.android.systemui.power.domain.interactor.PowerInteractorFactory import com.android.systemui.scene.domain.interactor.SceneInteractor @@ -34,6 +35,7 @@ import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.test.TestScope /** @@ -60,9 +62,11 @@ object KeyguardInteractorFactory { ): WithDependencies { // Mock these until they are replaced by kosmos val currentKeyguardStateFlow = MutableSharedFlow<KeyguardState>() + val transitionStateFlow = MutableStateFlow(TransitionStep()) val keyguardTransitionInteractor = mock<KeyguardTransitionInteractor>().also { whenever(it.currentKeyguardState).thenReturn(currentKeyguardStateFlow) + whenever(it.transitionState).thenReturn(transitionStateFlow) } val configurationDimensionFlow = MutableSharedFlow<ConfigurationBasedDimensions>() configurationDimensionFlow.tryEmit( diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelKosmos.kt index 6d46694db650..3c62b44ed2c4 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardClockViewModelKosmos.kt @@ -22,6 +22,7 @@ import com.android.systemui.keyguard.domain.interactor.keyguardClockInteractor import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.shade.domain.interactor.shadeInteractor +import com.android.systemui.statusbar.notification.icon.ui.viewmodel.notificationIconContainerAlwaysOnDisplayViewModel import com.android.systemui.statusbar.notification.stack.domain.interactor.notificationsKeyguardInteractor import com.android.systemui.statusbar.ui.systemBarUtilsProxy @@ -30,6 +31,7 @@ val Kosmos.keyguardClockViewModel by KeyguardClockViewModel( keyguardClockInteractor = keyguardClockInteractor, applicationScope = applicationCoroutineScope, + aodNotificationIconViewModel = notificationIconContainerAlwaysOnDisplayViewModel, notifsKeyguardInteractor = notificationsKeyguardInteractor, shadeInteractor = shadeInteractor, systemBarUtils = systemBarUtilsProxy, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt index 45a14ad69372..67f844340096 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/kosmos/KosmosJavaAdapter.kt @@ -29,6 +29,7 @@ import com.android.systemui.common.ui.data.repository.fakeConfigurationRepositor import com.android.systemui.common.ui.domain.interactor.configurationInteractor import com.android.systemui.communal.data.repository.fakeCommunalSceneRepository import com.android.systemui.communal.domain.interactor.communalInteractor +import com.android.systemui.communal.domain.interactor.communalSceneInteractor import com.android.systemui.communal.ui.viewmodel.communalTransitionViewModel import com.android.systemui.concurrency.fakeExecutor import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor @@ -113,6 +114,7 @@ class KosmosJavaAdapter() { val deviceEntryUdfpsInteractor by lazy { kosmos.deviceEntryUdfpsInteractor } val deviceUnlockedInteractor by lazy { kosmos.deviceUnlockedInteractor } val communalInteractor by lazy { kosmos.communalInteractor } + val communalSceneInteractor by lazy { kosmos.communalSceneInteractor } val sceneContainerPlugin by lazy { kosmos.sceneContainerPlugin } val deviceProvisioningInteractor by lazy { kosmos.deviceProvisioningInteractor } val fakeDeviceProvisioningRepository by lazy { kosmos.fakeDeviceProvisioningRepository } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/mediaprojection/data/repository/FakeMediaProjectionRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/mediaprojection/data/repository/FakeMediaProjectionRepository.kt index c4365c9093d2..d631f926176d 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/mediaprojection/data/repository/FakeMediaProjectionRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/mediaprojection/data/repository/FakeMediaProjectionRepository.kt @@ -25,4 +25,10 @@ class FakeMediaProjectionRepository : MediaProjectionRepository { override val mediaProjectionState: MutableStateFlow<MediaProjectionState> = MutableStateFlow(MediaProjectionState.NotProjecting) + + var stopProjectingInvoked = false + + override suspend fun stopProjecting() { + stopProjectingInvoked = true + } } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneContainerStartableKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneContainerStartableKosmos.kt index d82286fd3569..cf18c0e295ea 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneContainerStartableKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/domain/interactor/SceneContainerStartableKosmos.kt @@ -26,6 +26,7 @@ import com.android.systemui.deviceentry.domain.interactor.deviceEntryFaceAuthInt import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor import com.android.systemui.keyguard.domain.interactor.keyguardInteractor +import com.android.systemui.keyguard.domain.interactor.windowManagerLockscreenVisibilityInteractor import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture import com.android.systemui.kosmos.testScope @@ -67,5 +68,6 @@ val Kosmos.sceneContainerStartable by Fixture { uiEventLogger = uiEventLogger, sceneBackInteractor = sceneBackInteractor, shadeSessionStorage = shadeSessionStorage, + windowMgrLockscreenVisInteractor = windowManagerLockscreenVisibilityInteractor, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/screenrecord/data/repository/FakeScreenRecordRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/screenrecord/data/repository/FakeScreenRecordRepository.kt index fb0e3687bb1a..30b4763118a7 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/screenrecord/data/repository/FakeScreenRecordRepository.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/screenrecord/data/repository/FakeScreenRecordRepository.kt @@ -22,4 +22,10 @@ import kotlinx.coroutines.flow.MutableStateFlow class FakeScreenRecordRepository : ScreenRecordRepository { override val screenRecordState: MutableStateFlow<ScreenRecordModel> = MutableStateFlow(ScreenRecordModel.DoingNothing) + + var stopRecordingInvoked = false + + override suspend fun stopRecording() { + stopRecordingInvoked = true + } } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelKosmos.kt index 8d653f7212aa..0e21698ef271 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/shade/ui/viewmodel/ShadeHeaderViewModelKosmos.kt @@ -21,6 +21,7 @@ import com.android.systemui.broadcast.broadcastDispatcher import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.plugins.activityStarter +import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.shade.domain.interactor.privacyChipInteractor import com.android.systemui.shade.domain.interactor.shadeHeaderClockInteractor import com.android.systemui.shade.domain.interactor.shadeInteractor @@ -33,6 +34,7 @@ val Kosmos.shadeHeaderViewModel: ShadeHeaderViewModel by applicationScope = applicationCoroutineScope, context = applicationContext, activityStarter = activityStarter, + sceneInteractor = sceneInteractor, shadeInteractor = shadeInteractor, mobileIconsInteractor = mobileIconsInteractor, mobileIconsViewModel = mobileIconsViewModel, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractorKosmos.kt new file mode 100644 index 000000000000..062b4484044c --- /dev/null +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/mediaprojection/domain/interactor/MediaProjectionChipInteractorKosmos.kt @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.statusbar.chips.mediaprojection.domain.interactor + +import android.content.packageManager +import com.android.systemui.animation.mockDialogTransitionAnimator +import com.android.systemui.kosmos.Kosmos +import com.android.systemui.kosmos.applicationCoroutineScope +import com.android.systemui.mediaprojection.data.repository.fakeMediaProjectionRepository +import com.android.systemui.statusbar.phone.mockSystemUIDialogFactory +import com.android.systemui.util.time.fakeSystemClock + +val Kosmos.mediaProjectionChipInteractor: MediaProjectionChipInteractor by + Kosmos.Fixture { + MediaProjectionChipInteractor( + scope = applicationCoroutineScope, + mediaProjectionRepository = fakeMediaProjectionRepository, + packageManager = packageManager, + systemClock = fakeSystemClock, + dialogFactory = mockSystemUIDialogFactory, + dialogTransitionAnimator = mockDialogTransitionAnimator, + ) + } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelKosmos.kt index 88bde2ed5d8f..51ec54000261 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/chips/ui/viewmodel/OngoingActivityChipsViewModelKosmos.kt @@ -16,13 +16,14 @@ package com.android.systemui.statusbar.chips.ui.viewmodel +import com.android.systemui.animation.mockDialogTransitionAnimator import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.kosmos.testScope -import com.android.systemui.mediaprojection.data.repository.fakeMediaProjectionRepository import com.android.systemui.screenrecord.data.repository.screenRecordRepository -import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.MediaProjectionChipInteractor +import com.android.systemui.statusbar.chips.mediaprojection.domain.interactor.mediaProjectionChipInteractor import com.android.systemui.statusbar.chips.screenrecord.domain.interactor.ScreenRecordChipInteractor +import com.android.systemui.statusbar.phone.mockSystemUIDialogFactory import com.android.systemui.util.time.fakeSystemClock val Kosmos.screenRecordChipInteractor: ScreenRecordChipInteractor by @@ -30,15 +31,8 @@ val Kosmos.screenRecordChipInteractor: ScreenRecordChipInteractor by ScreenRecordChipInteractor( scope = applicationCoroutineScope, screenRecordRepository = screenRecordRepository, - systemClock = fakeSystemClock, - ) - } - -val Kosmos.mediaProjectionChipInteractor: MediaProjectionChipInteractor by - Kosmos.Fixture { - MediaProjectionChipInteractor( - scope = applicationCoroutineScope, - mediaProjectionRepository = fakeMediaProjectionRepository, + dialogFactory = mockSystemUIDialogFactory, + dialogTransitionAnimator = mockDialogTransitionAnimator, systemClock = fakeSystemClock, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt index d00eedf22efc..299486fc8ef4 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/SharedNotificationContainerViewModelKosmos.kt @@ -31,6 +31,7 @@ import com.android.systemui.keyguard.ui.viewmodel.glanceableHubToLockscreenTrans import com.android.systemui.keyguard.ui.viewmodel.goneToAodTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.goneToDozingTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.goneToDreamingTransitionViewModel +import com.android.systemui.keyguard.ui.viewmodel.goneToLockscreenTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.lockscreenToDreamingTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.lockscreenToGlanceableHubTransitionViewModel import com.android.systemui.keyguard.ui.viewmodel.lockscreenToGoneTransitionViewModel @@ -70,6 +71,7 @@ val Kosmos.sharedNotificationContainerViewModel by Fixture { goneToAodTransitionViewModel = goneToAodTransitionViewModel, goneToDozingTransitionViewModel = goneToDozingTransitionViewModel, goneToDreamingTransitionViewModel = goneToDreamingTransitionViewModel, + goneToLockscreenTransitionViewModel = goneToLockscreenTransitionViewModel, glanceableHubToLockscreenTransitionViewModel = glanceableHubToLockscreenTransitionViewModel, lockscreenToDreamingTransitionViewModel = lockscreenToDreamingTransitionViewModel, lockscreenToGlanceableHubTransitionViewModel = lockscreenToGlanceableHubTransitionViewModel, diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/SystemUIDialogFactoryKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/SystemUIDialogFactoryKosmos.kt index 3bb9580e69fa..1851c89ecd94 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/SystemUIDialogFactoryKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/statusbar/phone/SystemUIDialogFactoryKosmos.kt @@ -21,8 +21,9 @@ import com.android.systemui.animation.dialogTransitionAnimator import com.android.systemui.broadcast.broadcastDispatcher import com.android.systemui.kosmos.Kosmos import com.android.systemui.model.sysUiState +import com.android.systemui.util.mockito.mock -val Kosmos.systemUIDialogFactory by +val Kosmos.systemUIDialogFactory: SystemUIDialogFactory by Kosmos.Fixture { SystemUIDialogFactory( applicationContext, @@ -32,3 +33,6 @@ val Kosmos.systemUIDialogFactory by dialogTransitionAnimator, ) } + +val Kosmos.mockSystemUIDialogFactory: SystemUIDialog.Factory by + Kosmos.Fixture { mock<SystemUIDialog.Factory>() } diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/dagger/NaturalRotation.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/dagger/NaturalRotation.kt new file mode 100644 index 000000000000..be02487c75e6 --- /dev/null +++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/dagger/NaturalRotation.kt @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +package com.android.systemui.unfold.dagger + +import javax.inject.Qualifier + +/** Qualifier annotation for a progress provider that emits animation events only when + * in natural rotation */ +@Qualifier @Retention(AnnotationRetention.RUNTIME) annotation class NaturalRotation diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java index c9cce1568335..0ab6bbc3e0d3 100644 --- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java +++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java @@ -532,7 +532,8 @@ public class CompanionDeviceManagerService extends SystemService { String packageName, int userId) { startObservingDevicePresence_enforcePermission(); - mDevicePresenceProcessor.startObservingDevicePresence(request, packageName, userId); + mDevicePresenceProcessor.startObservingDevicePresence( + request, packageName, userId, /* enforcePermissions */ true); } @Override @@ -541,7 +542,8 @@ public class CompanionDeviceManagerService extends SystemService { String packageName, int userId) { stopObservingDevicePresence_enforcePermission(); - mDevicePresenceProcessor.stopObservingDevicePresence(request, packageName, userId); + mDevicePresenceProcessor.stopObservingDevicePresence( + request, packageName, userId, /* enforcePermissions */ true); } @Override diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java index c892b84ab4d5..3d53deb8d2bb 100644 --- a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java +++ b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java @@ -21,6 +21,7 @@ import static android.companion.CompanionDeviceManager.MESSAGE_REQUEST_CONTEXT_S import android.companion.AssociationInfo; import android.companion.ContextSyncMessage; import android.companion.Flags; +import android.companion.ObservingDevicePresenceRequest; import android.companion.Telecom; import android.companion.datatransfer.PermissionSyncRequest; import android.net.MacAddress; @@ -193,6 +194,43 @@ class CompanionDeviceShellCommand extends ShellCommand { break; } + case "start-observing-device-presence-uuid": { + if (Flags.devicePresence()) { + int userId = getNextIntArgRequired(); + String packageName = getNextArgRequired(); + String uuid = getNextArgRequired(); + if ("null".equals(uuid)) { + out.println("UUID can not be null."); + break; + } + ParcelUuid parcelUuid = ParcelUuid.fromString(uuid); + ObservingDevicePresenceRequest request = new ObservingDevicePresenceRequest + .Builder().setUuid(parcelUuid).build(); + mDevicePresenceProcessor.startObservingDevicePresence( + request, packageName, userId, /* enforcePermissions */ false); + + } + break; + } + + case "stop-observing-device-presence-uuid": { + if (Flags.devicePresence()) { + int userId = getNextIntArgRequired(); + String packageName = getNextArgRequired(); + String uuid = getNextArgRequired(); + if ("null".equals(uuid)) { + out.println("UUID can not be null."); + break; + } + ParcelUuid parcelUuid = ParcelUuid.fromString(uuid); + ObservingDevicePresenceRequest request = new ObservingDevicePresenceRequest + .Builder().setUuid(parcelUuid).build(); + mDevicePresenceProcessor.stopObservingDevicePresence( + request, packageName, userId, /* enforcePermissions */ false); + } + break; + } + case "get-backup-payload": { final int userId = getNextIntArgRequired(); byte[] payload = mBackupRestoreProcessor.getBackupPayload(userId); @@ -515,6 +553,14 @@ class CompanionDeviceShellCommand extends ShellCommand { pw.println(" callback after simulate-device-event-device-locked"); pw.println(" command has been called."); pw.println(" USE FOR DEBUGGING AND/OR TESTING PURPOSES ONLY."); + + pw.println(" start-observing-device-presence-uuid USER_ID PACKAGE_NAME UUID"); + pw.println(" Start observing device presence base on the UUID."); + pw.println(" USE FOR DEBUGGING AND/OR TESTING PURPOSES ONLY."); + + pw.println(" stop-observing-device-presence-uuid USER_ID PACKAGE_NAME UUID"); + pw.println(" Stop observing device presence base on the UUID."); + pw.println(" USE FOR DEBUGGING AND/OR TESTING PURPOSES ONLY."); } pw.println(" remove-inactive-associations"); diff --git a/services/companion/java/com/android/server/companion/devicepresence/DevicePresenceProcessor.java b/services/companion/java/com/android/server/companion/devicepresence/DevicePresenceProcessor.java index af49df69a979..a374d279af0b 100644 --- a/services/companion/java/com/android/server/companion/devicepresence/DevicePresenceProcessor.java +++ b/services/companion/java/com/android/server/companion/devicepresence/DevicePresenceProcessor.java @@ -181,14 +181,16 @@ public class DevicePresenceProcessor implements AssociationStore.OnChangeListene * Process device presence start request. */ public void startObservingDevicePresence(ObservingDevicePresenceRequest request, - String packageName, int userId) { + String packageName, int userId, boolean enforcePermissions) { Slog.i(TAG, "Start observing request=[" + request + "] for userId=[" + userId + "], package=[" + packageName + "]..."); final ParcelUuid requestUuid = request.getUuid(); if (requestUuid != null) { - enforceCallerCanObserveDevicePresenceByUuid(mContext); + if (enforcePermissions) { + enforceCallerCanObserveDevicePresenceByUuid(mContext, packageName, userId); + } // If it's already being observed, then no-op. if (mObservableUuidStore.isUuidBeingObserved(requestUuid, userId, packageName)) { @@ -236,7 +238,7 @@ public class DevicePresenceProcessor implements AssociationStore.OnChangeListene * Process device presence stop request. */ public void stopObservingDevicePresence(ObservingDevicePresenceRequest request, - String packageName, int userId) { + String packageName, int userId, boolean enforcePermissions) { Slog.i(TAG, "Stop observing request=[" + request + "] for userId=[" + userId + "], package=[" + packageName + "]..."); @@ -244,7 +246,9 @@ public class DevicePresenceProcessor implements AssociationStore.OnChangeListene final ParcelUuid requestUuid = request.getUuid(); if (requestUuid != null) { - enforceCallerCanObserveDevicePresenceByUuid(mContext); + if (enforcePermissions) { + enforceCallerCanObserveDevicePresenceByUuid(mContext, packageName, userId); + } if (!mObservableUuidStore.isUuidBeingObserved(requestUuid, userId, packageName)) { Slog.i(TAG, "UUID=[" + requestUuid + "], package=[" + packageName + "], userId=[" @@ -283,7 +287,7 @@ public class DevicePresenceProcessor implements AssociationStore.OnChangeListene * For legacy device presence below Android V. * * @deprecated Use {@link #startObservingDevicePresence(ObservingDevicePresenceRequest, String, - * int)} + * int, boolean)} */ @Deprecated public void startObservingDevicePresence(int userId, String packageName, String deviceAddress) @@ -306,14 +310,14 @@ public class DevicePresenceProcessor implements AssociationStore.OnChangeListene startObservingDevicePresence( new ObservingDevicePresenceRequest.Builder().setAssociationId(association.getId()) - .build(), packageName, userId); + .build(), packageName, userId, /* enforcePermissions */ true); } /** * For legacy device presence below Android V. * * @deprecated Use {@link #stopObservingDevicePresence(ObservingDevicePresenceRequest, String, - * int)} + * int, boolean)} */ @Deprecated public void stopObservingDevicePresence(int userId, String packageName, String deviceAddress) @@ -336,7 +340,7 @@ public class DevicePresenceProcessor implements AssociationStore.OnChangeListene stopObservingDevicePresence( new ObservingDevicePresenceRequest.Builder().setAssociationId(association.getId()) - .build(), packageName, userId); + .build(), packageName, userId, /* enforcePermissions */ true); } /** diff --git a/services/companion/java/com/android/server/companion/utils/PermissionsUtils.java b/services/companion/java/com/android/server/companion/utils/PermissionsUtils.java index 796d2851760f..c927cd04cf64 100644 --- a/services/companion/java/com/android/server/companion/utils/PermissionsUtils.java +++ b/services/companion/java/com/android/server/companion/utils/PermissionsUtils.java @@ -35,6 +35,8 @@ import static android.os.Binder.getCallingUid; import static android.os.Process.SYSTEM_UID; import static android.os.UserHandle.getCallingUserId; +import static com.android.server.companion.utils.RolesUtils.isRoleHolder; + import static java.util.Collections.unmodifiableMap; import android.Manifest; @@ -44,6 +46,7 @@ import android.annotation.UserIdInt; import android.companion.AssociationRequest; import android.companion.CompanionDeviceManager; import android.content.Context; +import android.os.Binder; import android.os.RemoteException; import android.os.ServiceManager; import android.util.ArrayMap; @@ -203,11 +206,9 @@ public final class PermissionsUtils { /** * Require the caller to hold necessary permission to observe device presence by UUID. */ - public static void enforceCallerCanObserveDevicePresenceByUuid(@NonNull Context context) { - if (context.checkCallingPermission(REQUEST_OBSERVE_DEVICE_UUID_PRESENCE) - != PERMISSION_GRANTED - || context.checkCallingPermission(BLUETOOTH_SCAN) != PERMISSION_GRANTED - || context.checkCallingPermission(BLUETOOTH_CONNECT) != PERMISSION_GRANTED) { + public static void enforceCallerCanObserveDevicePresenceByUuid(@NonNull Context context, + String packageName, int userId) { + if (!hasRequirePermissions(context, packageName, userId)) { throw new SecurityException("Caller (uid=" + getCallingUid() + ") does not have " + "permissions to request observing device presence base on the UUID"); } @@ -234,6 +235,17 @@ public final class PermissionsUtils { return sAppOpsService; } + private static boolean hasRequirePermissions( + @NonNull Context context, String packageName, int userId) { + return context.checkCallingPermission( + REQUEST_OBSERVE_DEVICE_UUID_PRESENCE) == PERMISSION_GRANTED + && context.checkCallingPermission(BLUETOOTH_SCAN) == PERMISSION_GRANTED + && context.checkCallingPermission(BLUETOOTH_CONNECT) == PERMISSION_GRANTED + && Boolean.TRUE.equals(Binder.withCleanCallingIdentity( + () -> isRoleHolder(context, userId, packageName, + DEVICE_PROFILE_AUTOMOTIVE_PROJECTION))); + } + // DO NOT USE DIRECTLY! Access via getAppOpsService(). private static IAppOpsService sAppOpsService = null; diff --git a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java index 2607ed3193eb..89c888c6dfc8 100644 --- a/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java +++ b/services/contentcapture/java/com/android/server/contentcapture/ContentCaptureManagerService.java @@ -18,6 +18,7 @@ package com.android.server.contentcapture; import static android.Manifest.permission.MANAGE_CONTENT_CAPTURE; import static android.content.Context.CONTENT_CAPTURE_MANAGER_SERVICE; +import static android.service.contentcapture.ContentCaptureService.ASSIST_CONTENT_ACTIVITY_START_KEY; import static android.service.contentcapture.ContentCaptureService.setClientState; import static android.view.contentcapture.ContentCaptureHelper.toList; import static android.view.contentcapture.ContentCaptureManager.DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_ALLOWLIST_DELAY_MS; @@ -28,6 +29,7 @@ import static android.view.contentcapture.ContentCaptureManager.DEVICE_CONFIG_PR import static android.view.contentcapture.ContentCaptureManager.DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_OPTIONAL_GROUPS_THRESHOLD; import static android.view.contentcapture.ContentCaptureManager.DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_REQUIRED_GROUPS_CONFIG; import static android.view.contentcapture.ContentCaptureManager.DEVICE_CONFIG_PROPERTY_ENABLE_CONTENT_PROTECTION_RECEIVER; +import static android.view.contentcapture.ContentCaptureManager.DEVICE_CONFIG_ENABLE_ACTIVITY_START_ASSIST_CONTENT; import static android.view.contentcapture.ContentCaptureManager.RESULT_CODE_FALSE; import static android.view.contentcapture.ContentCaptureManager.RESULT_CODE_OK; import static android.view.contentcapture.ContentCaptureManager.RESULT_CODE_SECURITY_EXCEPTION; @@ -44,6 +46,7 @@ import static com.android.internal.util.FrameworkStatsLog.CONTENT_CAPTURE_SERVIC import static com.android.internal.util.FrameworkStatsLog.CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__DATA_SHARE_WRITE_FINISHED; import static com.android.internal.util.FrameworkStatsLog.CONTENT_CAPTURE_SERVICE_EVENTS__EVENT__REJECT_DATA_SHARE_REQUEST; import static com.android.internal.util.SyncResultReceiver.bundleFor; +import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_CONTENT; import android.annotation.NonNull; import android.annotation.Nullable; @@ -52,10 +55,12 @@ import android.app.ActivityManagerInternal; import android.app.ActivityThread; import android.app.admin.DevicePolicyCache; import android.app.assist.ActivityId; +import android.app.assist.AssistContent; import android.content.ComponentName; import android.content.ContentCaptureOptions; import android.content.ContentResolver; import android.content.Context; +import android.content.Intent; import android.content.pm.ActivityPresentationInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; @@ -184,6 +189,9 @@ public class ContentCaptureManagerService extends @Nullable private boolean mDisabledByDeviceConfig; + @GuardedBy("mLock") + private boolean activityStartAssistDataEnabled; + // Device-config settings that are cached and passed back to apps @GuardedBy("mLock") int mDevCfgLoggingLevel; @@ -451,6 +459,8 @@ public class ContentCaptureManagerService extends case DEVICE_CONFIG_PROPERTY_CONTENT_PROTECTION_AUTO_DISCONNECT_TIMEOUT: setFineTuneParamsFromDeviceConfig(); return; + case DEVICE_CONFIG_ENABLE_ACTIVITY_START_ASSIST_CONTENT: + setActivityStartAssistDataEnabled(); default: Slog.i(TAG, "Ignoring change on " + key); } @@ -639,6 +649,15 @@ public class ContentCaptureManagerService extends final String enabled = DeviceConfig.getProperty(DeviceConfig.NAMESPACE_CONTENT_CAPTURE, ContentCaptureManager.DEVICE_CONFIG_PROPERTY_SERVICE_EXPLICITLY_ENABLED); setDisabledByDeviceConfig(enabled); + setActivityStartAssistDataEnabled(); + } + + private void setActivityStartAssistDataEnabled() { + synchronized (mLock) { + this.activityStartAssistDataEnabled = DeviceConfig.getBoolean( + DeviceConfig.NAMESPACE_CONTENT_CAPTURE, + DEVICE_CONFIG_ENABLE_ACTIVITY_START_ASSIST_CONTENT, false); + } } private void setDisabledByDeviceConfig(@Nullable String explicitlyEnabled) { @@ -908,6 +927,9 @@ public class ContentCaptureManagerService extends pw.print(prefix2); pw.print("contentProtectionAutoDisconnectTimeoutMs: "); pw.println(mDevCfgContentProtectionAutoDisconnectTimeoutMs); + pw.print(prefix2); + pw.print("activityStartAssistDataEnabled: "); + pw.println(activityStartAssistDataEnabled); pw.print(prefix); pw.println("Global Options:"); mGlobalContentCaptureOptions.dump(prefix2, pw); @@ -1327,6 +1349,33 @@ public class ContentCaptureManagerService extends } @Override + @SuppressWarnings("GuardedBy") + public boolean sendActivityStartAssistData(@UserIdInt int userId, + @NonNull IBinder activityToken, + @NonNull Intent intentData) { + synchronized (mLock) { + if (!activityStartAssistDataEnabled) { + return false; + } + Intent intent = new Intent(intentData); + intent.setFlags(intent.getFlags() & ~(Intent.FLAG_GRANT_WRITE_URI_PERMISSION + | Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION)); + Bundle assistContentExtras = new Bundle(); + assistContentExtras.putBoolean(ASSIST_CONTENT_ACTIVITY_START_KEY, true); + AssistContent assistContent = new AssistContent(assistContentExtras); + assistContent.setDefaultIntent(intent); + + final Bundle activityAssistData = new Bundle(); + activityAssistData.putParcelable(ASSIST_KEY_CONTENT, assistContent); + final ContentCapturePerUserService service = peekServiceForUserLocked(userId); + if (service != null) { + return service.sendActivityAssistDataLocked(activityToken, activityAssistData); + } + } + return false; + } + + @Override public boolean sendActivityAssistData(@UserIdInt int userId, @NonNull IBinder activityToken, @NonNull Bundle data) { synchronized (mLock) { diff --git a/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java b/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java index bc35fea2ce32..38bbfc4d9108 100644 --- a/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java +++ b/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerService.java @@ -47,6 +47,7 @@ import android.app.contextualsearch.ContextualSearchManager; import android.app.contextualsearch.ContextualSearchState; import android.app.contextualsearch.IContextualSearchCallback; import android.app.contextualsearch.IContextualSearchManager; +import android.app.contextualsearch.flags.Flags; import android.content.ComponentName; import android.content.Context; import android.content.Intent; @@ -91,6 +92,8 @@ public class ContextualSearchManagerService extends SystemService { private static final String TAG = ContextualSearchManagerService.class.getSimpleName(); private static final int MSG_RESET_TEMPORARY_PACKAGE = 0; private static final int MAX_TEMP_PACKAGE_DURATION_MS = 1_000 * 60 * 2; // 2 minutes + private static final int MSG_INVALIDATE_TOKEN = 1; + private static final int MAX_TOKEN_VALID_DURATION_MS = 1_000 * 60 * 10; // 10 minutes private final Context mContext; private final ActivityTaskManagerInternal mAtmInternal; @@ -145,6 +148,8 @@ public class ContextualSearchManagerService extends SystemService { private Handler mTemporaryHandler; @GuardedBy("this") private String mTemporaryPackage = null; + @GuardedBy("this") + private long mTokenValidDurationMs = MAX_TOKEN_VALID_DURATION_MS; @GuardedBy("mLock") private IContextualSearchCallback mStateCallback; @@ -212,6 +217,29 @@ public class ContextualSearchManagerService extends SystemService { } } + void resetTokenValidDurationMs() { + setTokenValidDurationMs(MAX_TOKEN_VALID_DURATION_MS); + } + + void setTokenValidDurationMs(int durationMs) { + synchronized (this) { + enforceOverridingPermission("setTokenValidDurationMs"); + if (durationMs > MAX_TOKEN_VALID_DURATION_MS) { + throw new IllegalArgumentException( + "Token max duration is " + MAX_TOKEN_VALID_DURATION_MS + " (called with " + + durationMs + ")"); + } + mTokenValidDurationMs = durationMs; + if (DEBUG_USER) Log.d(TAG, "mTokenValidDurationMs set to " + durationMs); + } + } + + private long getTokenValidDurationMs() { + synchronized (this) { + return mTokenValidDurationMs; + } + } + private Intent getResolvedLaunchIntent() { synchronized (this) { // If mTemporaryPackage is not null, use it to get the ContextualSearch intent. @@ -356,15 +384,51 @@ public class ContextualSearchManagerService extends SystemService { } private class ContextualSearchManagerStub extends IContextualSearchManager.Stub { + @GuardedBy("this") + private Handler mTokenHandler; private @Nullable CallbackToken mToken; + private void invalidateToken() { + synchronized (this) { + if (mTokenHandler != null) { + mTokenHandler.removeMessages(MSG_INVALIDATE_TOKEN); + mTokenHandler = null; + } + if (DEBUG_USER) Log.d(TAG, "mToken invalidated."); + mToken = null; + } + } + + private void issueToken() { + synchronized (this) { + mToken = new CallbackToken(); + if (mTokenHandler == null) { + mTokenHandler = new Handler(Looper.getMainLooper(), null, true) { + @Override + public void handleMessage(Message msg) { + if (msg.what == MSG_INVALIDATE_TOKEN) { + invalidateToken(); + } else { + Slog.wtf(TAG, "invalid token handler msg: " + msg); + } + } + }; + } else { + mTokenHandler.removeMessages(MSG_INVALIDATE_TOKEN); + } + mTokenHandler.sendEmptyMessageDelayed( + MSG_INVALIDATE_TOKEN, getTokenValidDurationMs()); + } + } + @Override public void startContextualSearch(int entrypoint) { synchronized (this) { if (DEBUG_USER) Log.d(TAG, "startContextualSearch"); enforcePermission("startContextualSearch"); mAssistDataRequester.cancel(); - mToken = new CallbackToken(); + // Creates a new CallbackToken at mToken and an expiration handler. + issueToken(); // We get the launch intent with the system server's identity because the system // server has READ_FRAME_BUFFER permission to get the screenshot and because only // the system server can invoke non-exported activities. @@ -397,7 +461,18 @@ public class ContextualSearchManagerService extends SystemService { } return; } - mToken = null; + invalidateToken(); + if (Flags.enableTokenRefresh()) { + issueToken(); + Bundle bundle = new Bundle(); + bundle.putParcelable(ContextualSearchManager.EXTRA_TOKEN, mToken); + try { + callback.onResult( + new ContextualSearchState(null, null, bundle)); + } catch (RemoteException e) { + Log.e(TAG, "Error invoking ContextualSearchCallback", e); + } + } synchronized (mLock) { mStateCallback = callback; } diff --git a/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerShellCommand.java b/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerShellCommand.java index 5777e1d154de..66a4e7b65aba 100644 --- a/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerShellCommand.java +++ b/services/contextualsearch/java/com/android/server/contextualsearch/ContextualSearchManagerShellCommand.java @@ -52,6 +52,19 @@ public class ContextualSearchManagerShellCommand extends ShellCommand { + packageName + " for " + duration + "ms"); break; } + case "token-duration": { + String durationStr = getNextArg(); + if (durationStr == null) { + mService.resetTokenValidDurationMs(); + pw.println("ContextualSearchManagerService token duration reset."); + return 0; + } + final int durationMs = Integer.parseInt(durationStr); + mService.setTokenValidDurationMs(durationMs); + pw.println("ContextualSearchManagerService temporarily set token duration" + + " to " + durationMs + "ms"); + break; + } } } break; @@ -72,6 +85,9 @@ public class ContextualSearchManagerShellCommand extends ShellCommand { pw.println(" Temporarily (for DURATION ms) changes the Contextual Search " + "implementation."); pw.println(" To reset, call without any arguments."); + pw.println(" set token-duration [DURATION]"); + pw.println(" Changes the Contextual Search token duration to DURATION ms."); + pw.println(" To reset, call without any arguments."); pw.println(""); } } diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java index 26aa0535d43e..f5a297bfd4f5 100644 --- a/services/core/java/com/android/server/am/ActivityManagerConstants.java +++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java @@ -169,6 +169,17 @@ final class ActivityManagerConstants extends ContentObserver { */ static final String KEY_ENABLE_NEW_OOMADJ = "enable_new_oom_adj"; + /** + * Whether or not to enable the batching of OOM adjuster calls to LMKD + */ + static final String KEY_ENABLE_BATCHING_OOM_ADJ = "enable_batching_oom_adj"; + + /** + * How long to wait before scheduling another follow-up oomAdjuster update for time based state. + */ + static final String KEY_FOLLOW_UP_OOMADJ_UPDATE_WAIT_DURATION = + "follow_up_oomadj_update_wait_duration"; + private static final int DEFAULT_MAX_CACHED_PROCESSES = 1024; private static final boolean DEFAULT_PRIORITIZE_ALARM_BROADCASTS = true; private static final long DEFAULT_FGSERVICE_MIN_SHOWN_TIME = 2*1000; @@ -231,7 +242,7 @@ final class ActivityManagerConstants extends ContentObserver { static final long DEFAULT_BACKGROUND_SETTLE_TIME = 60 * 1000; static final long DEFAULT_KILL_BG_RESTRICTED_CACHED_IDLE_SETTLE_TIME_MS = 60 * 1000; - static final boolean DEFAULT_KILL_BG_RESTRICTED_CACHED_IDLE = true; + static final boolean DEFAULT_KILL_BG_RESTRICTED_CACHED_IDLE = false; static final int DEFAULT_MAX_SERVICE_CONNECTIONS_PER_PROCESS = 3000; @@ -244,6 +255,16 @@ final class ActivityManagerConstants extends ContentObserver { private static final boolean DEFAULT_ENABLE_NEW_OOM_ADJ = Flags.oomadjusterCorrectnessRewrite(); /** + * The default value to {@link #KEY_ENABLE_BATCHING_OOM_ADJ}. + */ + private static final boolean DEFAULT_ENABLE_BATCHING_OOM_ADJ = Flags.batchingOomAdj(); + + /** + * The default value to {@link #KEY_FOLLOW_UP_OOMADJ_UPDATE_WAIT_DURATION}. + */ + private static final long DEFAULT_FOLLOW_UP_OOMADJ_UPDATE_WAIT_DURATION = 1000L; + + /** * Same as {@link TEMPORARY_ALLOW_LIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED} */ private static final int @@ -1136,6 +1157,13 @@ final class ActivityManagerConstants extends ContentObserver { /** @see #KEY_ENABLE_NEW_OOMADJ */ public boolean ENABLE_NEW_OOMADJ = DEFAULT_ENABLE_NEW_OOM_ADJ; + /** @see #KEY_ENABLE_BATCHING_OOM_ADJ */ + public boolean ENABLE_BATCHING_OOM_ADJ = DEFAULT_ENABLE_BATCHING_OOM_ADJ; + + /** @see #KEY_FOLLOW_UP_OOMADJ_UPDATE_WAIT_DURATION */ + public long FOLLOW_UP_OOMADJ_UPDATE_WAIT_DURATION = + DEFAULT_FOLLOW_UP_OOMADJ_UPDATE_WAIT_DURATION; + /** * Indicates whether PSS profiling in AppProfiler is disabled or not. */ @@ -1346,6 +1374,9 @@ final class ActivityManagerConstants extends ContentObserver { case KEY_PROC_STATE_DEBUG_UIDS: updateProcStateDebugUids(); break; + case KEY_FOLLOW_UP_OOMADJ_UPDATE_WAIT_DURATION: + updateFollowUpOomAdjUpdateWaitDuration(); + break; default: updateFGSPermissionEnforcementFlagsIfNecessary(name); break; @@ -1479,6 +1510,8 @@ final class ActivityManagerConstants extends ContentObserver { private void loadNativeBootDeviceConfigConstants() { ENABLE_NEW_OOMADJ = getDeviceConfigBoolean(KEY_ENABLE_NEW_OOMADJ, DEFAULT_ENABLE_NEW_OOM_ADJ); + ENABLE_BATCHING_OOM_ADJ = getDeviceConfigBoolean(KEY_ENABLE_BATCHING_OOM_ADJ, + DEFAULT_ENABLE_BATCHING_OOM_ADJ); } public void setOverrideMaxCachedProcesses(int value) { @@ -2231,6 +2264,13 @@ final class ActivityManagerConstants extends ContentObserver { DEFAULT_ENABLE_NEW_OOM_ADJ); } + private void updateFollowUpOomAdjUpdateWaitDuration() { + FOLLOW_UP_OOMADJ_UPDATE_WAIT_DURATION = DeviceConfig.getLong( + DeviceConfig.NAMESPACE_ACTIVITY_MANAGER, + KEY_FOLLOW_UP_OOMADJ_UPDATE_WAIT_DURATION, + DEFAULT_FOLLOW_UP_OOMADJ_UPDATE_WAIT_DURATION); + } + private void updateFGSPermissionEnforcementFlagsIfNecessary(@NonNull String name) { ForegroundServiceTypePolicy.getDefaultPolicy() .updatePermissionEnforcementFlagIfNecessary(name); @@ -2248,6 +2288,13 @@ final class ActivityManagerConstants extends ContentObserver { mDefaultPssToRssThresholdModifier); } + private void updateEnableBatchingOomAdj() { + ENABLE_BATCHING_OOM_ADJ = DeviceConfig.getBoolean( + DeviceConfig.NAMESPACE_ACTIVITY_MANAGER_NATIVE_BOOT, + KEY_ENABLE_BATCHING_OOM_ADJ, + DEFAULT_ENABLE_BATCHING_OOM_ADJ); + } + boolean shouldDebugUidForProcState(int uid) { SparseBooleanArray ar = mProcStateDebugUids; final var size = ar.size(); @@ -2476,6 +2523,9 @@ final class ActivityManagerConstants extends ContentObserver { pw.print(" "); pw.print(KEY_MAX_PREVIOUS_TIME); pw.print("="); pw.println(MAX_PREVIOUS_TIME); + pw.print(" "); pw.print(KEY_ENABLE_BATCHING_OOM_ADJ); + pw.print("="); pw.println(ENABLE_BATCHING_OOM_ADJ); + pw.println(); if (mOverrideMaxCachedProcesses >= 0) { pw.print(" mOverrideMaxCachedProcesses="); pw.println(mOverrideMaxCachedProcesses); @@ -2489,6 +2539,9 @@ final class ActivityManagerConstants extends ContentObserver { pw.print(" ENABLE_WAIT_FOR_FINISH_ATTACH_APPLICATION="); pw.println(mEnableWaitForFinishAttachApplication); + pw.print(" "); pw.print(KEY_FOLLOW_UP_OOMADJ_UPDATE_WAIT_DURATION); + pw.print("="); pw.println(FOLLOW_UP_OOMADJ_UPDATE_WAIT_DURATION); + synchronized (mProcStateDebugUids) { pw.print(" "); pw.print(KEY_PROC_STATE_DEBUG_UIDS); pw.print("="); pw.println(mProcStateDebugUids); diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 44e522f4fa2f..8dad9ac01b3c 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -1663,6 +1663,7 @@ public class ActivityManagerService extends IActivityManager.Stub static final int BIND_APPLICATION_TIMEOUT_HARD_MSG = 83; static final int SERVICE_FGS_TIMEOUT_MSG = 84; static final int SERVICE_FGS_CRASH_TIMEOUT_MSG = 85; + static final int FOLLOW_UP_OOMADJUSTER_UPDATE_MSG = 86; static final int FIRST_BROADCAST_QUEUE_MSG = 200; @@ -2036,6 +2037,9 @@ public class ActivityManagerService extends IActivityManager.Stub case SERVICE_FGS_CRASH_TIMEOUT_MSG: { mServices.onFgsCrashTimeout((ServiceRecord) msg.obj); } break; + case FOLLOW_UP_OOMADJUSTER_UPDATE_MSG: { + handleFollowUpOomAdjusterUpdate(); + } break; } } } @@ -5103,6 +5107,15 @@ public class ActivityManagerService extends IActivityManager.Stub mAnrHelper.appNotResponding(app, TimeoutRecord.forAppStart(anrMessage)); } + private void handleFollowUpOomAdjusterUpdate() { + // Remove any existing duplicate messages on the handler here while no lock is being held. + // If another follow up update is needed, it will be scheduled by OomAdjuster. + mHandler.removeMessages(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG); + synchronized (this) { + mOomAdjuster.updateOomAdjFollowUpTargetsLocked(); + } + } + /** * @return The last part of the string of an intent's action. */ diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java index ab34dd4477fd..e8af82efecbc 100644 --- a/services/core/java/com/android/server/am/OomAdjuster.java +++ b/services/core/java/com/android/server/am/OomAdjuster.java @@ -49,6 +49,7 @@ import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_BIND_SERVICE; import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_COMPONENT_DISABLED; import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_EXECUTING_SERVICE; import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_FINISH_RECEIVER; +import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_FOLLOW_UP; import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_GET_PROVIDER; import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_NONE; import static android.app.ActivityManagerInternal.OOM_ADJ_REASON_PROCESS_BEGIN; @@ -91,6 +92,7 @@ import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PSS; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_UID_OBSERVERS; import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_USAGE_STATS; import static com.android.server.am.ActivityManagerService.DISPATCH_OOM_ADJ_OBSERVER_MSG; +import static com.android.server.am.ActivityManagerService.FOLLOW_UP_OOMADJUSTER_UPDATE_MSG; import static com.android.server.am.ActivityManagerService.IDLE_UIDS_MSG; import static com.android.server.am.ActivityManagerService.TAG_BACKUP; import static com.android.server.am.ActivityManagerService.TAG_LRU; @@ -226,6 +228,8 @@ public class OomAdjuster { return AppProtoEnums.OOM_ADJ_REASON_RESTRICTION_CHANGE; case OOM_ADJ_REASON_COMPONENT_DISABLED: return AppProtoEnums.OOM_ADJ_REASON_COMPONENT_DISABLED; + case OOM_ADJ_REASON_FOLLOW_UP: + return AppProtoEnums.OOM_ADJ_REASON_FOLLOW_UP; default: return AppProtoEnums.OOM_ADJ_REASON_UNKNOWN_TO_PROTO; } @@ -280,6 +284,8 @@ public class OomAdjuster { return OOM_ADJ_REASON_METHOD + "_restrictionChange"; case OOM_ADJ_REASON_COMPONENT_DISABLED: return OOM_ADJ_REASON_METHOD + "_componentDisabled"; + case OOM_ADJ_REASON_FOLLOW_UP: + return OOM_ADJ_REASON_METHOD + "_followUp"; default: return "_unknown"; } @@ -370,6 +376,7 @@ public class OomAdjuster { protected final int[] mTmpSchedGroup = new int[1]; final ActivityManagerService mService; + final Injector mInjector; final ProcessList mProcessList; final ActivityManagerGlobalLock mProcLock; @@ -384,6 +391,13 @@ public class OomAdjuster { protected final ArraySet<ProcessRecord> mProcessesInCycle = new ArraySet<>(); /** + * List of processes that we want to batch for LMKD to adjust their respective + * OOM scores. + */ + @GuardedBy("mService") + protected final ArrayList<ProcessRecord> mProcsToOomAdj = new ArrayList<ProcessRecord>(); + + /** * Flag to mark if there is an ongoing oomAdjUpdate: potentially the oomAdjUpdate * could be called recursively because of the indirect calls during the update; * however the oomAdjUpdate itself doesn't support recursion - in this case we'd @@ -413,12 +427,33 @@ public class OomAdjuster { @GuardedBy("mService") protected int mProcessStateCurTop = PROCESS_STATE_TOP; - /** Overrideable by a test */ + @GuardedBy("mService") + private final ArraySet<ProcessRecord> mFollowUpUpdateSet = new ArraySet<>(); + + private static final long NO_FOLLOW_UP_TIME = Long.MAX_VALUE; + @GuardedBy("mService") + private long mNextFollowUpUpdateUptimeMs = NO_FOLLOW_UP_TIME; + @VisibleForTesting - protected boolean isChangeEnabled(@CachedCompatChangeId int cachedCompatChangeId, + public static class Injector { + boolean isChangeEnabled(@CachedCompatChangeId int cachedCompatChangeId, + ApplicationInfo app, boolean defaultValue) { + return PlatformCompatCache.getInstance() + .isChangeEnabled(cachedCompatChangeId, app, defaultValue); + } + + long getUptimeMillis() { + return SystemClock.uptimeMillis(); + } + + long getElapsedRealtimeMillis() { + return SystemClock.elapsedRealtime(); + } + } + + boolean isChangeEnabled(@CachedCompatChangeId int cachedCompatChangeId, ApplicationInfo app, boolean defaultValue) { - return PlatformCompatCache.getInstance() - .isChangeEnabled(cachedCompatChangeId, app, defaultValue); + return mInjector.isChangeEnabled(cachedCompatChangeId, app, defaultValue); } OomAdjuster(ActivityManagerService service, ProcessList processList, ActiveUids activeUids) { @@ -436,7 +471,18 @@ public class OomAdjuster { OomAdjuster(ActivityManagerService service, ProcessList processList, ActiveUids activeUids, ServiceThread adjusterThread) { + this(service, processList, activeUids, adjusterThread, new Injector()); + } + + OomAdjuster(ActivityManagerService service, ProcessList processList, ActiveUids activeUids, + Injector injector) { + this(service, processList, activeUids, createAdjusterThread(), injector); + } + + OomAdjuster(ActivityManagerService service, ProcessList processList, ActiveUids activeUids, + ServiceThread adjusterThread, Injector injector) { mService = service; + mInjector = injector; mProcessList = processList; mProcLock = service.mProcLock; mActiveUids = activeUids; @@ -624,8 +670,8 @@ public class OomAdjuster { // processes, its adj could be still unknown as of now, assign one. processes.add(app); assignCachedAdjIfNecessary(processes); - applyOomAdjLSP(app, false, SystemClock.uptimeMillis(), - SystemClock.elapsedRealtime(), oomAdjReason); + applyOomAdjLSP(app, false, mInjector.getUptimeMillis(), + mInjector.getElapsedRealtimeMillis(), oomAdjReason); } mTmpProcessList.clear(); mService.clearPendingTopAppLocked(); @@ -842,6 +888,40 @@ public class OomAdjuster { } @GuardedBy("mService") + void updateOomAdjFollowUpTargetsLocked() { + final long now = mInjector.getUptimeMillis(); + long nextFollowUpUptimeMs = Long.MAX_VALUE; + mNextFollowUpUpdateUptimeMs = NO_FOLLOW_UP_TIME; + for (int i = mFollowUpUpdateSet.size() - 1; i >= 0; i--) { + final ProcessRecord proc = mFollowUpUpdateSet.valueAtUnchecked(i); + final long followUpUptimeMs = proc.mState.getFollowupUpdateUptimeMs(); + + if (proc.isKilled()) { + // Process is dead, just remove from follow up set. + mFollowUpUpdateSet.removeAt(i); + } else if (followUpUptimeMs <= now) { + // Add processes that need a follow up update. + mPendingProcessSet.add(proc); + proc.mState.setFollowupUpdateUptimeMs(NO_FOLLOW_UP_TIME); + mFollowUpUpdateSet.removeAt(i); + } else if (followUpUptimeMs < nextFollowUpUptimeMs) { + // Figure out when to schedule the next follow up update. + nextFollowUpUptimeMs = followUpUptimeMs; + } else if (followUpUptimeMs == NO_FOLLOW_UP_TIME) { + // The follow up is no longer needed for this process. + mFollowUpUpdateSet.removeAt(i); + } + } + + if (nextFollowUpUptimeMs != Long.MAX_VALUE) { + // There is still at least one process that needs a follow up. + scheduleFollowUpOomAdjusterUpdateLocked(nextFollowUpUptimeMs, now); + } + + updateOomAdjPendingTargetsLocked(OOM_ADJ_REASON_FOLLOW_UP); + } + + @GuardedBy("mService") protected void performUpdateOomAdjPendingTargetsLocked(@OomAdjReason int oomAdjReason) { final ProcessRecord topApp = mService.getTopApp(); @@ -892,8 +972,8 @@ public class OomAdjuster { if (startProfiling) { Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, oomAdjReasonToString(oomAdjReason)); } - final long now = SystemClock.uptimeMillis(); - final long nowElapsed = SystemClock.elapsedRealtime(); + final long now = mInjector.getUptimeMillis(); + final long nowElapsed = mInjector.getElapsedRealtimeMillis(); final long oldTime = now - mConstants.mMaxEmptyTimeMillis; final int numProc = activeProcesses.size(); @@ -1019,7 +1099,7 @@ public class OomAdjuster { updateUidsLSP(activeUids, nowElapsed); synchronized (mService.mProcessStats.mLock) { - final long nowUptime = SystemClock.uptimeMillis(); + final long nowUptime = mInjector.getUptimeMillis(); if (mService.mProcessStats.shouldWriteNowLocked(nowUptime)) { mService.mHandler.post(new ActivityManagerService.ProcStatsRunnable(mService, mService.mProcessStats)); @@ -1030,7 +1110,7 @@ public class OomAdjuster { } if (DEBUG_OOM_ADJ) { - final long duration = SystemClock.uptimeMillis() - now; + final long duration = mInjector.getUptimeMillis() - now; if (false) { Slog.d(TAG_OOM_ADJ, "Did OOM ADJ in " + duration + "ms", new RuntimeException("here").fillInStackTrace()); @@ -1044,7 +1124,7 @@ public class OomAdjuster { protected void assignCachedAdjIfNecessary(ArrayList<ProcessRecord> lruList) { final int numLru = lruList.size(); if (mConstants.USE_TIERED_CACHED_ADJ) { - final long now = SystemClock.uptimeMillis(); + final long now = mInjector.getUptimeMillis(); for (int i = numLru - 1; i >= 0; i--) { ProcessRecord app = lruList.get(i); final ProcessStateRecord state = app.mState; @@ -1246,7 +1326,7 @@ public class OomAdjuster { if (!app.isKilledByAm() && app.getThread() != null) { // We don't need to apply the update for the process which didn't get computed if (state.getCompletedAdjSeq() == mAdjSeq) { - applyOomAdjLSP(app, doingAll, now, nowElapsed, oomAdjReason); + applyOomAdjLSP(app, doingAll, now, nowElapsed, oomAdjReason, true); } if (app.isPendingFinishAttach()) { @@ -1348,6 +1428,11 @@ public class OomAdjuster { } } + if (!mProcsToOomAdj.isEmpty()) { + ProcessList.batchSetOomAdj(mProcsToOomAdj); + mProcsToOomAdj.clear(); + } + if (proactiveKillsEnabled // Proactive kills enabled? && doKillExcessiveProcesses // Should kill excessive processes? && freeSwapPercent < lowSwapThresholdPercent // Swap below threshold? @@ -1722,6 +1807,10 @@ public class OomAdjuster { int prevProcState = getInitialProcState(app); int prevCapability = getInitialCapability(app); + // Remove any follow up update this process might have. It will be rescheduled if still + // needed. + app.mState.setFollowupUpdateUptimeMs(NO_FOLLOW_UP_TIME); + if (app.getThread() == null) { state.setAdjSeq(mAdjSeq); state.setCurrentSchedulingGroup(SCHED_GROUP_BACKGROUND); @@ -1989,6 +2078,8 @@ public class OomAdjuster { if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to recent fg: " + app); } + maybeSetProcessFollowUpUpdateLocked(app, + state.getLastTopTime() + mConstants.TOP_TO_FGS_GRACE_DURATION, now); } // If the app was recently in the foreground and has expedited jobs running, @@ -2009,6 +2100,9 @@ public class OomAdjuster { if (DEBUG_OOM_ADJ_REASON || logUid == appUid) { reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise to recent fg for EJ: " + app); } + maybeSetProcessFollowUpUpdateLocked(app, + state.getLastTopTime() + mConstants.TOP_TO_ALMOST_PERCEPTIBLE_GRACE_DURATION, + now); } if (adj > PERCEPTIBLE_APP_ADJ @@ -2074,7 +2168,7 @@ public class OomAdjuster { // app to be demoted to cached. if (procState >= PROCESS_STATE_LAST_ACTIVITY && state.getSetProcState() == PROCESS_STATE_LAST_ACTIVITY - && (state.getLastStateTime() + mConstants.MAX_PREVIOUS_TIME) < now) { + && (state.getLastStateTime() + mConstants.MAX_PREVIOUS_TIME) <= now) { procState = PROCESS_STATE_LAST_ACTIVITY; schedGroup = SCHED_GROUP_BACKGROUND; state.setAdjType("previous-expired"); @@ -2098,6 +2192,14 @@ public class OomAdjuster { reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise procstate to prev: " + app); } } + final long lastStateTime; + if (state.getSetProcState() == PROCESS_STATE_LAST_ACTIVITY) { + lastStateTime = state.getLastStateTime(); + } else { + lastStateTime = now; + } + maybeSetProcessFollowUpUpdateLocked(app, + lastStateTime + mConstants.MAX_PREVIOUS_TIME, now); } } @@ -2186,6 +2288,8 @@ public class OomAdjuster { reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise adj to started service: " + app); } + maybeSetProcessFollowUpUpdateLocked(app, + s.lastActivity + mConstants.MAX_SERVICE_INACTIVITY, now); } } // If we have let the service slide into the background @@ -2340,6 +2444,8 @@ public class OomAdjuster { reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise adj to recent provider: " + app); } + maybeSetProcessFollowUpUpdateLocked(app, + ppr.getLastProviderTime() + mConstants.CONTENT_PROVIDER_RETAIN_TIME, now); } if (procState > PROCESS_STATE_LAST_ACTIVITY) { procState = PROCESS_STATE_LAST_ACTIVITY; @@ -2348,6 +2454,8 @@ public class OomAdjuster { reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise procstate to recent provider: " + app); } + maybeSetProcessFollowUpUpdateLocked(app, + ppr.getLastProviderTime() + mConstants.CONTENT_PROVIDER_RETAIN_TIME, now); } } @@ -3246,10 +3354,16 @@ public class OomAdjuster { mCachedAppOptimizer.onWakefulnessChanged(wakefulness); } + @GuardedBy({"mService", "mProcLock"}) + protected boolean applyOomAdjLSP(ProcessRecord app, boolean doingAll, long now, + long nowElapsed, @OomAdjReason int oomAdjReason) { + return applyOomAdjLSP(app, doingAll, now, nowElapsed, oomAdjReason, false); + } + /** Applies the computed oomadj, procstate and sched group values and freezes them in set* */ @GuardedBy({"mService", "mProcLock"}) protected boolean applyOomAdjLSP(ProcessRecord app, boolean doingAll, long now, - long nowElapsed, @OomAdjReason int oomAdjReson) { + long nowElapsed, @OomAdjReason int oomAdjReson, boolean isBatchingOomAdj) { boolean success = true; final ProcessStateRecord state = app.mState; final UidRecord uidRec = app.getUidRecord(); @@ -3266,7 +3380,12 @@ public class OomAdjuster { final int oldOomAdj = state.getSetAdj(); if (state.getCurAdj() != state.getSetAdj()) { - ProcessList.setOomAdj(app.getPid(), app.uid, state.getCurAdj()); + if (isBatchingOomAdj && mConstants.ENABLE_BATCHING_OOM_ADJ) { + mProcsToOomAdj.add(app); + } else { + ProcessList.setOomAdj(app.getPid(), app.uid, state.getCurAdj()); + } + if (DEBUG_SWITCH || DEBUG_OOM_ADJ || mService.mCurOomAdjUid == app.info.uid) { String msg = "Set " + app.getPid() + " " + app.processName + " adj " + state.getCurAdj() + ": " + state.getAdjType(); @@ -3649,7 +3768,7 @@ public class OomAdjuster { if (N <= 0) { return; } - final long nowElapsed = SystemClock.elapsedRealtime(); + final long nowElapsed = mInjector.getElapsedRealtimeMillis(); final long maxBgTime = nowElapsed - mConstants.BACKGROUND_SETTLE_TIME; long nextTime = 0; if (mService.mLocalPowerManager != null) { @@ -3884,7 +4003,7 @@ public class OomAdjuster { } // Take a dry run of the computeServiceHostOomAdjLSP, this would't be expensive // since it's only evaluating one service connection. - return computeServiceHostOomAdjLSP(cr, app, client, SystemClock.uptimeMillis(), + return computeServiceHostOomAdjLSP(cr, app, client, mInjector.getUptimeMillis(), mService.getTopApp(), false, false, false, OOM_ADJ_REASON_NONE, CACHED_APP_MIN_ADJ, false, true /* dryRun */); } @@ -3919,7 +4038,7 @@ public class OomAdjuster { // The provider host process has better states than the client, so no change. return false; } - return computeProviderHostOomAdjLSP(null, app, client, SystemClock.uptimeMillis(), + return computeProviderHostOomAdjLSP(null, app, client, mInjector.getUptimeMillis(), mService.getTopApp(), false, false, false, OOM_ADJ_REASON_NONE, CACHED_APP_MIN_ADJ, false, true /* dryRun */); } @@ -3947,4 +4066,43 @@ public class OomAdjuster { } return false; } + + @GuardedBy("mService") + private void maybeSetProcessFollowUpUpdateLocked(ProcessRecord proc, + long updateUptimeMs, long now) { + if (!Flags.followUpOomadjUpdates()) { + return; + } + if (updateUptimeMs <= now) { + // Time sensitive period has already passed. No need to schedule a follow up. + return; + } + + mFollowUpUpdateSet.add(proc); + proc.mState.setFollowupUpdateUptimeMs(updateUptimeMs); + + scheduleFollowUpOomAdjusterUpdateLocked(updateUptimeMs, now); + } + + + @GuardedBy("mService") + private void scheduleFollowUpOomAdjusterUpdateLocked(long updateUptimeMs, + long now) { + if (updateUptimeMs + mConstants.FOLLOW_UP_OOMADJ_UPDATE_WAIT_DURATION + >= mNextFollowUpUpdateUptimeMs) { + // Update time is too close or later than the next follow up update. + return; + } + if (updateUptimeMs < now + mConstants.FOLLOW_UP_OOMADJ_UPDATE_WAIT_DURATION) { + // Use a minimum delay for the follow up to possibly batch multiple process + // evaluations and avoid rapid updates. + updateUptimeMs = now + mConstants.FOLLOW_UP_OOMADJ_UPDATE_WAIT_DURATION; + } + + // Schedulate a follow up update. Don't bother deleting existing handler messages, they + // will be cleared during the message while no locks are being held. + mNextFollowUpUpdateUptimeMs = updateUptimeMs; + mService.mHandler.sendEmptyMessageAtTime(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG, + mNextFollowUpUpdateUptimeMs); + } } diff --git a/services/core/java/com/android/server/am/OomAdjusterModernImpl.java b/services/core/java/com/android/server/am/OomAdjusterModernImpl.java index a67af89ad39d..21842db590b0 100644 --- a/services/core/java/com/android/server/am/OomAdjusterModernImpl.java +++ b/services/core/java/com/android/server/am/OomAdjusterModernImpl.java @@ -68,7 +68,6 @@ import android.app.ActivityManager; import android.app.ActivityManagerInternal.OomAdjReason; import android.content.pm.ServiceInfo; import android.os.IBinder; -import android.os.SystemClock; import android.os.Trace; import android.util.ArrayMap; import android.util.ArraySet; @@ -764,6 +763,11 @@ public class OomAdjusterModernImpl extends OomAdjuster { super(service, processList, activeUids, adjusterThread); } + OomAdjusterModernImpl(ActivityManagerService service, ProcessList processList, + ActiveUids activeUids, Injector injector) { + super(service, processList, activeUids, injector); + } + private final ProcessRecordNodes mProcessRecordProcStateNodes = new ProcessRecordNodes( ProcessRecordNode.NODE_TYPE_PROC_STATE, PROC_STATE_SLOTS.length); private final ProcessRecordNodes mProcessRecordAdjNodes = new ProcessRecordNodes( @@ -924,8 +928,8 @@ public class OomAdjusterModernImpl extends OomAdjuster { @GuardedBy({"mService", "mProcLock"}) private void fullUpdateLSP(@OomAdjReason int oomAdjReason) { final ProcessRecord topApp = mService.getTopApp(); - final long now = SystemClock.uptimeMillis(); - final long nowElapsed = SystemClock.elapsedRealtime(); + final long now = mInjector.getUptimeMillis(); + final long nowElapsed = mInjector.getElapsedRealtimeMillis(); final long oldTime = now - mConstants.mMaxEmptyTimeMillis; mAdjSeq++; @@ -1015,8 +1019,8 @@ public class OomAdjusterModernImpl extends OomAdjuster { @GuardedBy({"mService", "mProcLock"}) private void partialUpdateLSP(@OomAdjReason int oomAdjReason, ArraySet<ProcessRecord> targets) { final ProcessRecord topApp = mService.getTopApp(); - final long now = SystemClock.uptimeMillis(); - final long nowElapsed = SystemClock.elapsedRealtime(); + final long now = mInjector.getUptimeMillis(); + final long nowElapsed = mInjector.getElapsedRealtimeMillis(); final long oldTime = now - mConstants.mMaxEmptyTimeMillis; ActiveUids activeUids = mTmpUidRecords; diff --git a/services/core/java/com/android/server/am/ProcessList.java b/services/core/java/com/android/server/am/ProcessList.java index 219de709fb70..c0947247de4b 100644 --- a/services/core/java/com/android/server/am/ProcessList.java +++ b/services/core/java/com/android/server/am/ProcessList.java @@ -354,6 +354,7 @@ public final class ProcessList { // LMK_KILL_OCCURRED // LMK_START_MONITORING // LMK_BOOT_COMPLETED + // LMK_PROCS_PRIO static final byte LMK_TARGET = 0; static final byte LMK_PROCPRIO = 1; static final byte LMK_PROCREMOVE = 2; @@ -365,6 +366,7 @@ public final class ProcessList { static final byte LMK_KILL_OCCURRED = 8; // Msg to subscribed clients on kill occurred event static final byte LMK_START_MONITORING = 9; // Start monitoring if delayed earlier static final byte LMK_BOOT_COMPLETED = 10; + static final byte LMK_PROCS_PRIO = 11; // Batch option for LMK_PROCPRIO // Low Memory Killer Daemon command codes. // These must be kept in sync with async_event_type definitions in lmkd.h @@ -1561,6 +1563,50 @@ public final class ProcessList { } } + + // The max size for PROCS_PRIO cmd in LMKD + private static final int MAX_PROCS_PRIO_PACKET_SIZE = 3; + + // (4 bytes per field * 4 fields * 3 processes per batch) + 4 bytes for the LMKD cmd + private static final int MAX_OOM_ADJ_BATCH_LENGTH = ((4 * 4) * MAX_PROCS_PRIO_PACKET_SIZE) + 4; + + /** + * Set the out-of-memory badness adjustment for a list of processes. + * + * @param apps App list to adjust their respective oom score. + * + * {@hide} + */ + public static void batchSetOomAdj(ArrayList<ProcessRecord> apps) { + final int totalApps = apps.size(); + if (totalApps == 0) { + return; + } + + ByteBuffer buf = ByteBuffer.allocate(MAX_OOM_ADJ_BATCH_LENGTH); + int total_procs_in_buf = 0; + buf.putInt(LMK_PROCS_PRIO); + for (int i = 0; i < totalApps; i++) { + final int pid = apps.get(i).getPid(); + final int amt = apps.get(i).mState.getCurAdj(); + final int uid = apps.get(i).uid; + if (pid <= 0 || amt == UNKNOWN_ADJ) continue; + if (total_procs_in_buf >= MAX_PROCS_PRIO_PACKET_SIZE) { + writeLmkd(buf, null); + buf.clear(); + total_procs_in_buf = 0; + buf.allocate(MAX_OOM_ADJ_BATCH_LENGTH); + buf.putInt(LMK_PROCS_PRIO); + } + buf.putInt(pid); + buf.putInt(uid); + buf.putInt(amt); + buf.putInt(0); // Default proc type to PROC_TYPE_APP + total_procs_in_buf++; + } + writeLmkd(buf, null); + } + /* * {@hide} */ diff --git a/services/core/java/com/android/server/am/ProcessStateRecord.java b/services/core/java/com/android/server/am/ProcessStateRecord.java index 8de748e37b5e..7c64298a6053 100644 --- a/services/core/java/com/android/server/am/ProcessStateRecord.java +++ b/services/core/java/com/android/server/am/ProcessStateRecord.java @@ -449,6 +449,9 @@ final class ProcessStateRecord { @GuardedBy("mService") private boolean mScheduleLikeTopApp = false; + @GuardedBy("mService") + private long mFollowupUpdateUptimeMs = Long.MAX_VALUE; + ProcessStateRecord(ProcessRecord app) { mApp = app; mService = app.mService; @@ -1164,6 +1167,16 @@ final class ProcessStateRecord { mScheduleLikeTopApp = scheduleLikeTopApp; } + @GuardedBy("mService") + long getFollowupUpdateUptimeMs() { + return mFollowupUpdateUptimeMs; + } + + @GuardedBy("mService") + void setFollowupUpdateUptimeMs(long updateUptimeMs) { + mFollowupUpdateUptimeMs = updateUptimeMs; + } + @GuardedBy(anyOf = {"mService", "mProcLock"}) public String makeAdjReason() { if (mAdjSource != null || mAdjTarget != null) { diff --git a/services/core/java/com/android/server/am/flags.aconfig b/services/core/java/com/android/server/am/flags.aconfig index afde4f71a95f..bb52857a99c4 100644 --- a/services/core/java/com/android/server/am/flags.aconfig +++ b/services/core/java/com/android/server/am/flags.aconfig @@ -134,3 +134,21 @@ flag { purpose: PURPOSE_BUGFIX } } + +flag { + name: "batching_oom_adj" + namespace: "backstage_power" + description: "Batch OOM adjustment calls to LMKD" + bug: "244232958" + is_fixed_read_only: true +} + +flag { + name: "follow_up_oomadj_updates" + namespace: "backstage_power" + description: "Schedule follow up OomAdjuster updates for time sensitive states." + bug: "333450932" + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/services/core/java/com/android/server/broadcastradio/aidl/ConversionUtils.java b/services/core/java/com/android/server/broadcastradio/aidl/ConversionUtils.java index 5b77c5214dd9..077e8eea485b 100644 --- a/services/core/java/com/android/server/broadcastradio/aidl/ConversionUtils.java +++ b/services/core/java/com/android/server/broadcastradio/aidl/ConversionUtils.java @@ -446,6 +446,7 @@ final class ConversionUtils { sel.secondaryIds[i]); if (id == null) { Slogf.e(TAG, "invalid secondary id: %s", sel.secondaryIds[i]); + continue; } secondaryIdList.add(id); } diff --git a/services/core/java/com/android/server/broadcastradio/hal2/Convert.java b/services/core/java/com/android/server/broadcastradio/hal2/Convert.java index 34bfa6cb2d46..02a9f0963e5f 100644 --- a/services/core/java/com/android/server/broadcastradio/hal2/Convert.java +++ b/services/core/java/com/android/server/broadcastradio/hal2/Convert.java @@ -304,10 +304,7 @@ final class Convert { private static boolean isEmpty( @NonNull android.hardware.broadcastradio.V2_0.ProgramSelector sel) { - if (sel.primaryId.type != 0) return false; - if (sel.primaryId.value != 0) return false; - if (!sel.secondaryIds.isEmpty()) return false; - return true; + return sel.primaryId.type == 0 && sel.primaryId.value == 0 && sel.secondaryIds.isEmpty(); } static @Nullable ProgramSelector programSelectorFromHal( diff --git a/services/core/java/com/android/server/camera/CameraServiceProxy.java b/services/core/java/com/android/server/camera/CameraServiceProxy.java index 390ee96a3417..17835b2d085b 100644 --- a/services/core/java/com/android/server/camera/CameraServiceProxy.java +++ b/services/core/java/com/android/server/camera/CameraServiceProxy.java @@ -18,6 +18,10 @@ package com.android.server.camera; import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE; import static android.os.Build.VERSION_CODES.M; +import static com.android.internal.util.FrameworkStatsLog.CAMERA_FEATURE_COMBINATION_QUERY_EVENT__STATUS_CODE__OK; +import static com.android.internal.util.FrameworkStatsLog.CAMERA_FEATURE_COMBINATION_QUERY_EVENT__STATUS_CODE__ERROR_ILLEGAL_ARGUMENT; +import static com.android.internal.util.FrameworkStatsLog.CAMERA_FEATURE_COMBINATION_QUERY_EVENT__STATUS_CODE__ERROR_INVALID_OPERATION; + import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; @@ -40,6 +44,7 @@ import android.content.res.Configuration; import android.graphics.ImageFormat; import android.graphics.Rect; import android.hardware.CameraExtensionSessionStats; +import android.hardware.CameraFeatureCombinationStats; import android.hardware.CameraSessionStats; import android.hardware.CameraStreamStats; import android.hardware.ICameraService; @@ -217,7 +222,7 @@ public class CameraServiceProxy extends SystemService // Map of currently active camera IDs private final ArrayMap<String, CameraUsageEvent> mActiveCameraUsage = new ArrayMap<>(); - private final List<CameraUsageEvent> mCameraUsageHistory = new ArrayList<>(); + private final List<CameraEvent> mCameraEventHistory = new ArrayList<CameraEvent>(); private static final String NFC_NOTIFICATION_PROP = "ro.camera.notify_nfc"; private static final IBinder nfcInterfaceToken = new Binder(); @@ -228,9 +233,16 @@ public class CameraServiceProxy extends SystemService /*corePoolSize*/ 1); /** + * Interface to track camera analytics + */ + private interface CameraEvent { + void logSelf(); + } + + /** * Structure to track camera usage */ - private static class CameraUsageEvent { + private static class CameraUsageEvent implements CameraEvent { public final String mCameraId; public final int mCameraFacing; public final String mClientName; @@ -311,6 +323,214 @@ public class CameraServiceProxy extends SystemService public long getDuration() { return mCompleted ? mDurationOrStartTimeMs : 0; } + + public void logSelf() { + int facing = FrameworkStatsLog.CAMERA_ACTION_EVENT__FACING__UNKNOWN; + switch(mCameraFacing) { + case CameraSessionStats.CAMERA_FACING_BACK: + facing = FrameworkStatsLog.CAMERA_ACTION_EVENT__FACING__BACK; + break; + case CameraSessionStats.CAMERA_FACING_FRONT: + facing = FrameworkStatsLog.CAMERA_ACTION_EVENT__FACING__FRONT; + break; + case CameraSessionStats.CAMERA_FACING_EXTERNAL: + facing = FrameworkStatsLog.CAMERA_ACTION_EVENT__FACING__EXTERNAL; + break; + default: + Slog.w(TAG, "Unknown camera facing: " + mCameraFacing); + } + + int extensionType = FrameworkStatsLog.CAMERA_ACTION_EVENT__EXT_TYPE__EXTENSION_NONE; + boolean extensionIsAdvanced = false; + int extensionCaptureFormat = ImageFormat.UNKNOWN; + if (mExtSessionStats != null) { + switch (mExtSessionStats.type) { + case CameraExtensionSessionStats.Type.EXTENSION_AUTOMATIC: + extensionType = FrameworkStatsLog + .CAMERA_ACTION_EVENT__EXT_TYPE__EXTENSION_AUTOMATIC; + break; + case CameraExtensionSessionStats.Type.EXTENSION_FACE_RETOUCH: + extensionType = FrameworkStatsLog + .CAMERA_ACTION_EVENT__EXT_TYPE__EXTENSION_FACE_RETOUCH; + break; + case CameraExtensionSessionStats.Type.EXTENSION_BOKEH: + extensionType = + FrameworkStatsLog.CAMERA_ACTION_EVENT__EXT_TYPE__EXTENSION_BOKEH; + break; + case CameraExtensionSessionStats.Type.EXTENSION_HDR: + extensionType = + FrameworkStatsLog.CAMERA_ACTION_EVENT__EXT_TYPE__EXTENSION_HDR; + break; + case CameraExtensionSessionStats.Type.EXTENSION_NIGHT: + extensionType = + FrameworkStatsLog.CAMERA_ACTION_EVENT__EXT_TYPE__EXTENSION_NIGHT; + break; + default: + Slog.w(TAG, "Unknown extension type: " + mExtSessionStats.type); + } + extensionIsAdvanced = mExtSessionStats.isAdvanced; + if (Flags.analytics24q3()) { + extensionCaptureFormat = mExtSessionStats.captureFormat; + } + } + + int streamCount = 0; + if (mStreamStats != null) { + streamCount = mStreamStats.size(); + } + if (CameraServiceProxy.DEBUG) { + String ultrawideDebug = Flags.logUltrawideUsage() + ? ", wideAngleUsage " + mUsedUltraWide + : ""; + String zoomOverrideDebug = Flags.logZoomOverrideUsage() + ? ", zoomOverrideUsage " + mUsedZoomOverride + : ""; + String mostRequestedFpsRangeDebug = Flags.analytics24q3() + ? ", mostRequestedFpsRange " + mMostRequestedFpsRange + : ""; + String extensionCaptureFormatDebug = Flags.analytics24q3() + ? " extensionCaptureFormat " + mExtSessionStats.captureFormat + : ""; + + Slog.v(TAG, "CAMERA_ACTION_EVENT: action " + mAction + + " clientName " + mClientName + + ", duration " + getDuration() + + ", APILevel " + mAPILevel + + ", cameraId " + mCameraId + + ", facing " + facing + + ", isNdk " + mIsNdk + + ", latencyMs " + mLatencyMs + + ", operatingMode " + mOperatingMode + + ", internalReconfigure " + mInternalReconfigure + + ", requestCount " + mRequestCount + + ", resultErrorCount " + mResultErrorCount + + ", deviceError " + mDeviceError + + ", streamCount is " + streamCount + + ", userTag is " + mUserTag + + ", videoStabilizationMode " + mVideoStabilizationMode + + ultrawideDebug + + zoomOverrideDebug + + mostRequestedFpsRangeDebug + + ", logId " + mLogId + + ", sessionIndex " + mSessionIndex + + ", mExtSessionStats {type " + extensionType + + " isAdvanced " + extensionIsAdvanced + + extensionCaptureFormatDebug + "}"); + } + + // Convert from CameraStreamStats to CameraStreamProto + CameraStreamProto[] streamProtos = new CameraStreamProto[MAX_STREAM_STATISTICS]; + for (int i = 0; i < MAX_STREAM_STATISTICS; i++) { + streamProtos[i] = new CameraStreamProto(); + if (i < streamCount) { + CameraStreamStats streamStats = mStreamStats.get(i); + streamProtos[i].width = streamStats.getWidth(); + streamProtos[i].height = streamStats.getHeight(); + streamProtos[i].format = streamStats.getFormat(); + streamProtos[i].dataSpace = streamStats.getDataSpace(); + streamProtos[i].usage = streamStats.getUsage(); + streamProtos[i].requestCount = streamStats.getRequestCount(); + streamProtos[i].errorCount = streamStats.getErrorCount(); + streamProtos[i].firstCaptureLatencyMillis = streamStats.getStartLatencyMs(); + streamProtos[i].maxHalBuffers = streamStats.getMaxHalBuffers(); + streamProtos[i].maxAppBuffers = streamStats.getMaxAppBuffers(); + streamProtos[i].histogramType = streamStats.getHistogramType(); + streamProtos[i].histogramBins = streamStats.getHistogramBins(); + streamProtos[i].histogramCounts = streamStats.getHistogramCounts(); + streamProtos[i].dynamicRangeProfile = streamStats.getDynamicRangeProfile(); + streamProtos[i].streamUseCase = streamStats.getStreamUseCase(); + streamProtos[i].colorSpace = streamStats.getColorSpace(); + + if (CameraServiceProxy.DEBUG) { + String histogramTypeName = + cameraHistogramTypeToString(streamProtos[i].histogramType); + Slog.v(TAG, "Stream " + i + ": width " + streamProtos[i].width + + ", height " + streamProtos[i].height + + ", format " + streamProtos[i].format + + ", maxPreviewFps " + streamStats.getMaxPreviewFps() + + ", dataSpace " + streamProtos[i].dataSpace + + ", usage " + streamProtos[i].usage + + ", requestCount " + streamProtos[i].requestCount + + ", errorCount " + streamProtos[i].errorCount + + ", firstCaptureLatencyMillis " + + streamProtos[i].firstCaptureLatencyMillis + + ", maxHalBuffers " + streamProtos[i].maxHalBuffers + + ", maxAppBuffers " + streamProtos[i].maxAppBuffers + + ", histogramType " + histogramTypeName + + ", histogramBins " + + Arrays.toString(streamProtos[i].histogramBins) + + ", histogramCounts " + + Arrays.toString(streamProtos[i].histogramCounts) + + ", dynamicRangeProfile " + streamProtos[i].dynamicRangeProfile + + ", streamUseCase " + streamProtos[i].streamUseCase + + ", colorSpace " + streamProtos[i].colorSpace); + } + } + } + FrameworkStatsLog.write(FrameworkStatsLog.CAMERA_ACTION_EVENT, getDuration(), + mAPILevel, mClientName, facing, mCameraId, mAction, mIsNdk, + mLatencyMs, mOperatingMode, mInternalReconfigure, + mRequestCount, mResultErrorCount, mDeviceError, + streamCount, MessageNano.toByteArray(streamProtos[0]), + MessageNano.toByteArray(streamProtos[1]), + MessageNano.toByteArray(streamProtos[2]), + MessageNano.toByteArray(streamProtos[3]), + MessageNano.toByteArray(streamProtos[4]), + mUserTag, mVideoStabilizationMode, + mLogId, mSessionIndex, + extensionType, extensionIsAdvanced, mUsedUltraWide, + mUsedZoomOverride, + mMostRequestedFpsRange.getLower(), mMostRequestedFpsRange.getUpper(), + extensionCaptureFormat); + + } + } + /** + * Structure to track feature combination query + */ + private static class CameraFeatureCombinationQueryEvent implements CameraEvent { + private CameraFeatureCombinationStats mFeatureCombinationStats; + + CameraFeatureCombinationQueryEvent(CameraFeatureCombinationStats featureCombinationStats) { + mFeatureCombinationStats = featureCombinationStats; + } + + public void logSelf() { + int statusCode = -1; + switch (mFeatureCombinationStats.mStatus) { + case 0: + statusCode = CAMERA_FEATURE_COMBINATION_QUERY_EVENT__STATUS_CODE__OK; + break; + case ICameraService.ERROR_ILLEGAL_ARGUMENT: + statusCode = CAMERA_FEATURE_COMBINATION_QUERY_EVENT__STATUS_CODE__ERROR_ILLEGAL_ARGUMENT; + break; + case ICameraService.ERROR_INVALID_OPERATION: + statusCode = CAMERA_FEATURE_COMBINATION_QUERY_EVENT__STATUS_CODE__ERROR_INVALID_OPERATION; + break; + default: + break; + } + if (statusCode == -1) { + Slog.w(TAG, "Unknown feature combination query status code: " + + mFeatureCombinationStats.mStatus); + return; + } + + if (CameraServiceProxy.DEBUG) { + Slog.v(TAG, "CAMERA_FEATURE_COMBINATION_QUERY_EVENT: uid " + + mFeatureCombinationStats.mUid + + ", cameraId " + mFeatureCombinationStats.mCameraId + + ", queryType " + mFeatureCombinationStats.mQueryType + + ", featureCombination " + mFeatureCombinationStats.mFeatureCombination + + ", status " + statusCode); + } + FrameworkStatsLog.write(FrameworkStatsLog.CAMERA_FEATURE_COMBINATION_QUERY_EVENT, + mFeatureCombinationStats.mUid, + mFeatureCombinationStats.mCameraId, + mFeatureCombinationStats.mQueryType, + mFeatureCombinationStats.mFeatureCombination, + statusCode); + } } private final class DisplayWindowListener extends IDisplayWindowListener.Stub { @@ -625,6 +845,32 @@ public class CameraServiceProxy extends SystemService } @Override + public void notifyFeatureCombinationStats(CameraFeatureCombinationStats featureCombStats) { + if (!Flags.analytics24q3()) { + return; + } + if (Binder.getCallingUid() != Process.CAMERASERVER_UID) { + Slog.e(TAG, "Calling UID: " + Binder.getCallingUid() + " doesn't match expected " + + " camera service UID!"); + return; + } + + if (DEBUG) { + String combinationTypeStr = cameraFeatureCombinationTypeToString( + featureCombStats.mQueryType); + String combinationStr = cameraFeatureCombinationToString( + featureCombStats.mFeatureCombination); + Slog.v(TAG, "Camera " + featureCombStats.mCameraId + + " query " + combinationTypeStr + + " combination " + combinationStr + + " for client UID " + featureCombStats.mUid + + " returns " + featureCombStats.mStatus); + } + + updateFeatureCombinationQuery(featureCombStats); + } + + @Override public boolean isCameraDisabled(int userId) { if (Binder.getCallingUid() != Process.CAMERASERVER_UID) { Slog.e(TAG, "Calling UID: " + Binder.getCallingUid() @@ -684,7 +930,7 @@ public class CameraServiceProxy extends SystemService switch (cmd.replace('-', '_')) { case "dump_events": int eventCount = mCameraServiceProxy.getUsageEventCount(); - mCameraServiceProxy.dumpUsageEvents(); + mCameraServiceProxy.dumpCameraEvents(); pw.println("Camera usage events dumped: " + eventCount); break; default: @@ -865,18 +1111,18 @@ public class CameraServiceProxy extends SystemService } private class EventWriterTask implements Runnable { - private ArrayList<CameraUsageEvent> mEventList; + private List<CameraEvent> mEventList; private static final long WRITER_SLEEP_MS = 100; - public EventWriterTask(ArrayList<CameraUsageEvent> eventList) { + EventWriterTask(List<CameraEvent> eventList) { mEventList = eventList; } @Override public void run() { if (mEventList != null) { - for (CameraUsageEvent event : mEventList) { - logCameraUsageEvent(event); + for (CameraEvent event : mEventList) { + event.logSelf(); try { Thread.sleep(WRITER_SLEEP_MS); } catch (InterruptedException e) {} @@ -884,170 +1130,6 @@ public class CameraServiceProxy extends SystemService mEventList.clear(); } } - - /** - * Write camera usage events to stats log. - * Package-private - */ - private void logCameraUsageEvent(CameraUsageEvent e) { - int facing = FrameworkStatsLog.CAMERA_ACTION_EVENT__FACING__UNKNOWN; - switch(e.mCameraFacing) { - case CameraSessionStats.CAMERA_FACING_BACK: - facing = FrameworkStatsLog.CAMERA_ACTION_EVENT__FACING__BACK; - break; - case CameraSessionStats.CAMERA_FACING_FRONT: - facing = FrameworkStatsLog.CAMERA_ACTION_EVENT__FACING__FRONT; - break; - case CameraSessionStats.CAMERA_FACING_EXTERNAL: - facing = FrameworkStatsLog.CAMERA_ACTION_EVENT__FACING__EXTERNAL; - break; - default: - Slog.w(TAG, "Unknown camera facing: " + e.mCameraFacing); - } - - int extensionType = FrameworkStatsLog.CAMERA_ACTION_EVENT__EXT_TYPE__EXTENSION_NONE; - boolean extensionIsAdvanced = false; - int extensionCaptureFormat = ImageFormat.UNKNOWN; - if (e.mExtSessionStats != null) { - switch (e.mExtSessionStats.type) { - case CameraExtensionSessionStats.Type.EXTENSION_AUTOMATIC: - extensionType = FrameworkStatsLog - .CAMERA_ACTION_EVENT__EXT_TYPE__EXTENSION_AUTOMATIC; - break; - case CameraExtensionSessionStats.Type.EXTENSION_FACE_RETOUCH: - extensionType = FrameworkStatsLog - .CAMERA_ACTION_EVENT__EXT_TYPE__EXTENSION_FACE_RETOUCH; - break; - case CameraExtensionSessionStats.Type.EXTENSION_BOKEH: - extensionType = - FrameworkStatsLog.CAMERA_ACTION_EVENT__EXT_TYPE__EXTENSION_BOKEH; - break; - case CameraExtensionSessionStats.Type.EXTENSION_HDR: - extensionType = - FrameworkStatsLog.CAMERA_ACTION_EVENT__EXT_TYPE__EXTENSION_HDR; - break; - case CameraExtensionSessionStats.Type.EXTENSION_NIGHT: - extensionType = - FrameworkStatsLog.CAMERA_ACTION_EVENT__EXT_TYPE__EXTENSION_NIGHT; - break; - default: - Slog.w(TAG, "Unknown extension type: " + e.mExtSessionStats.type); - } - extensionIsAdvanced = e.mExtSessionStats.isAdvanced; - if (Flags.analytics24q3()) { - extensionCaptureFormat = e.mExtSessionStats.captureFormat; - } - } - - int streamCount = 0; - if (e.mStreamStats != null) { - streamCount = e.mStreamStats.size(); - } - if (CameraServiceProxy.DEBUG) { - String ultrawideDebug = Flags.logUltrawideUsage() - ? ", wideAngleUsage " + e.mUsedUltraWide - : ""; - String zoomOverrideDebug = Flags.logZoomOverrideUsage() - ? ", zoomOverrideUsage " + e.mUsedZoomOverride - : ""; - String mostRequestedFpsRangeDebug = Flags.analytics24q3() - ? ", mostRequestedFpsRange " + e.mMostRequestedFpsRange - : ""; - String extensionCaptureFormatDebug = Flags.analytics24q3() - ? " extensionCaptureFormat " + e.mExtSessionStats.captureFormat - : ""; - - Slog.v(TAG, "CAMERA_ACTION_EVENT: action " + e.mAction - + " clientName " + e.mClientName - + ", duration " + e.getDuration() - + ", APILevel " + e.mAPILevel - + ", cameraId " + e.mCameraId - + ", facing " + facing - + ", isNdk " + e.mIsNdk - + ", latencyMs " + e.mLatencyMs - + ", operatingMode " + e.mOperatingMode - + ", internalReconfigure " + e.mInternalReconfigure - + ", requestCount " + e.mRequestCount - + ", resultErrorCount " + e.mResultErrorCount - + ", deviceError " + e.mDeviceError - + ", streamCount is " + streamCount - + ", userTag is " + e.mUserTag - + ", videoStabilizationMode " + e.mVideoStabilizationMode - + ultrawideDebug - + zoomOverrideDebug - + mostRequestedFpsRangeDebug - + ", logId " + e.mLogId - + ", sessionIndex " + e.mSessionIndex - + ", mExtSessionStats {type " + extensionType - + " isAdvanced " + extensionIsAdvanced - + extensionCaptureFormatDebug + "}"); - } - - // Convert from CameraStreamStats to CameraStreamProto - CameraStreamProto[] streamProtos = new CameraStreamProto[MAX_STREAM_STATISTICS]; - for (int i = 0; i < MAX_STREAM_STATISTICS; i++) { - streamProtos[i] = new CameraStreamProto(); - if (i < streamCount) { - CameraStreamStats streamStats = e.mStreamStats.get(i); - streamProtos[i].width = streamStats.getWidth(); - streamProtos[i].height = streamStats.getHeight(); - streamProtos[i].format = streamStats.getFormat(); - streamProtos[i].dataSpace = streamStats.getDataSpace(); - streamProtos[i].usage = streamStats.getUsage(); - streamProtos[i].requestCount = streamStats.getRequestCount(); - streamProtos[i].errorCount = streamStats.getErrorCount(); - streamProtos[i].firstCaptureLatencyMillis = streamStats.getStartLatencyMs(); - streamProtos[i].maxHalBuffers = streamStats.getMaxHalBuffers(); - streamProtos[i].maxAppBuffers = streamStats.getMaxAppBuffers(); - streamProtos[i].histogramType = streamStats.getHistogramType(); - streamProtos[i].histogramBins = streamStats.getHistogramBins(); - streamProtos[i].histogramCounts = streamStats.getHistogramCounts(); - streamProtos[i].dynamicRangeProfile = streamStats.getDynamicRangeProfile(); - streamProtos[i].streamUseCase = streamStats.getStreamUseCase(); - streamProtos[i].colorSpace = streamStats.getColorSpace(); - - if (CameraServiceProxy.DEBUG) { - String histogramTypeName = - cameraHistogramTypeToString(streamProtos[i].histogramType); - Slog.v(TAG, "Stream " + i + ": width " + streamProtos[i].width - + ", height " + streamProtos[i].height - + ", format " + streamProtos[i].format - + ", maxPreviewFps " + streamStats.getMaxPreviewFps() - + ", dataSpace " + streamProtos[i].dataSpace - + ", usage " + streamProtos[i].usage - + ", requestCount " + streamProtos[i].requestCount - + ", errorCount " + streamProtos[i].errorCount - + ", firstCaptureLatencyMillis " - + streamProtos[i].firstCaptureLatencyMillis - + ", maxHalBuffers " + streamProtos[i].maxHalBuffers - + ", maxAppBuffers " + streamProtos[i].maxAppBuffers - + ", histogramType " + histogramTypeName - + ", histogramBins " - + Arrays.toString(streamProtos[i].histogramBins) - + ", histogramCounts " - + Arrays.toString(streamProtos[i].histogramCounts) - + ", dynamicRangeProfile " + streamProtos[i].dynamicRangeProfile - + ", streamUseCase " + streamProtos[i].streamUseCase - + ", colorSpace " + streamProtos[i].colorSpace); - } - } - } - FrameworkStatsLog.write(FrameworkStatsLog.CAMERA_ACTION_EVENT, e.getDuration(), - e.mAPILevel, e.mClientName, facing, e.mCameraId, e.mAction, e.mIsNdk, - e.mLatencyMs, e.mOperatingMode, e.mInternalReconfigure, - e.mRequestCount, e.mResultErrorCount, e.mDeviceError, - streamCount, MessageNano.toByteArray(streamProtos[0]), - MessageNano.toByteArray(streamProtos[1]), - MessageNano.toByteArray(streamProtos[2]), - MessageNano.toByteArray(streamProtos[3]), - MessageNano.toByteArray(streamProtos[4]), - e.mUserTag, e.mVideoStabilizationMode, - e.mLogId, e.mSessionIndex, - extensionType, extensionIsAdvanced, e.mUsedUltraWide, - e.mUsedZoomOverride, - e.mMostRequestedFpsRange.getLower(), e.mMostRequestedFpsRange.getUpper(), - extensionCaptureFormat); - } } /** @@ -1055,22 +1137,22 @@ public class CameraServiceProxy extends SystemService */ int getUsageEventCount() { synchronized (mLock) { - return mCameraUsageHistory.size(); + return mCameraEventHistory.size(); } } /** - * Dump camera usage events to log. + * Dump camera events to log. * Package-private */ - void dumpUsageEvents() { + void dumpCameraEvents() { synchronized(mLock) { // Randomize order of events so that it's not meaningful - Collections.shuffle(mCameraUsageHistory); + Collections.shuffle(mCameraEventHistory); mLogWriterService.execute(new EventWriterTask( - new ArrayList<CameraUsageEvent>(mCameraUsageHistory))); + new ArrayList(mCameraEventHistory))); - mCameraUsageHistory.clear(); + mCameraEventHistory.clear(); } final long ident = Binder.clearCallingIdentity(); try { @@ -1288,7 +1370,7 @@ public class CameraServiceProxy extends SystemService cameraId, facing, clientName, apiLevel, isNdk, FrameworkStatsLog.CAMERA_ACTION_EVENT__ACTION__OPEN, latencyMs, sessionType, deviceError, logId, sessionIdx); - mCameraUsageHistory.add(openEvent); + mCameraEventHistory.add(openEvent); break; case CameraSessionStats.CAMERA_STATE_ACTIVE: // Check current active camera IDs to see if this package is already talking to @@ -1323,7 +1405,7 @@ public class CameraServiceProxy extends SystemService /*userTag*/"", /*videoStabilizationMode*/-1, /*usedUltraWide*/false, /*usedZoomOverride*/false, new Range<Integer>(0, 0), new CameraExtensionSessionStats()); - mCameraUsageHistory.add(oldEvent); + mCameraEventHistory.add(oldEvent); } break; case CameraSessionStats.CAMERA_STATE_IDLE: @@ -1335,7 +1417,7 @@ public class CameraServiceProxy extends SystemService resultErrorCount, deviceError, streamStats, userTag, videoStabilizationMode, usedUltraWide, usedZoomOverride, mostRequestedFpsRange, extSessionStats); - mCameraUsageHistory.add(doneEvent); + mCameraEventHistory.add(doneEvent); // Do not double count device error deviceError = false; @@ -1362,11 +1444,11 @@ public class CameraServiceProxy extends SystemService cameraId, facing, clientName, apiLevel, isNdk, FrameworkStatsLog.CAMERA_ACTION_EVENT__ACTION__CLOSE, latencyMs, sessionType, deviceError, logId, sessionIdx); - mCameraUsageHistory.add(closeEvent); + mCameraEventHistory.add(closeEvent); } - if (mCameraUsageHistory.size() > MAX_USAGE_HISTORY) { - dumpUsageEvents(); + if (mCameraEventHistory.size() > MAX_USAGE_HISTORY) { + dumpCameraEvents(); } break; @@ -1378,6 +1460,18 @@ public class CameraServiceProxy extends SystemService } } + private void updateFeatureCombinationQuery(CameraFeatureCombinationStats featureCombStats) { + synchronized (mLock) { + CameraFeatureCombinationQueryEvent e = + new CameraFeatureCombinationQueryEvent(featureCombStats); + mCameraEventHistory.add(e); + + if (mCameraEventHistory.size() > MAX_USAGE_HISTORY) { + dumpCameraEvents(); + } + } + } + private void notifyNfcService(boolean enablePolling) { NfcManager nfcManager = mContext.getSystemService(NfcManager.class); if (nfcManager == null) { @@ -1434,4 +1528,41 @@ public class CameraServiceProxy extends SystemService return "HISTOGRAM_TYPE_UNKNOWN"; } + private static String cameraFeatureCombinationTypeToString(int featureCombinationType) { + switch (featureCombinationType) { + case CameraFeatureCombinationStats.QueryType.QUERY_FEATURE_COMBINATION: + return "QUERY_FEATURE_COMBINATION"; + case CameraFeatureCombinationStats.QueryType.QUERY_SESSION_CHARACTERISTICS: + return "QUERY_SESSION_CHARACTERISTICS"; + default: + break; + } + return "FEATURE_COMBINATION_TYPE_UNKNOWN"; + } + + private static String cameraFeatureCombinationToString(long featureCombination) { + StringBuilder combinationStr = new StringBuilder("{"); + if ((featureCombination & CameraFeatureCombinationStats.CAMERA_FEATURE_60_FPS) != 0) { + combinationStr.append("60fps "); + } + if ((featureCombination & CameraFeatureCombinationStats.CAMERA_FEATURE_STABILIZATION) + != 0) { + combinationStr.append("stabilization "); + } + if ((featureCombination & CameraFeatureCombinationStats.CAMERA_FEATURE_HLG10) != 0) { + combinationStr.append("hlg10 "); + } + if ((featureCombination & CameraFeatureCombinationStats.CAMERA_FEATURE_JPEG) != 0) { + combinationStr.append("jpeg "); + } + if ((featureCombination & CameraFeatureCombinationStats.CAMERA_FEATURE_JPEG_R) != 0) { + combinationStr.append("jpeg_r "); + } + if ((featureCombination & CameraFeatureCombinationStats.CAMERA_FEATURE_4K) != 0) { + combinationStr.append("4k "); + } + combinationStr.append("}"); + + return combinationStr.toString(); + } } diff --git a/services/core/java/com/android/server/camera/CameraStatsJobService.java b/services/core/java/com/android/server/camera/CameraStatsJobService.java index b8a6846ced76..1227ca7ef057 100644 --- a/services/core/java/com/android/server/camera/CameraStatsJobService.java +++ b/services/core/java/com/android/server/camera/CameraStatsJobService.java @@ -21,14 +21,13 @@ import android.app.job.JobParameters; import android.app.job.JobScheduler; import android.app.job.JobService; import android.content.ComponentName; -import android.content.ContentResolver; import android.content.Context; import android.util.Slog; -import java.util.concurrent.TimeUnit; - import com.android.server.LocalServices; +import java.util.concurrent.TimeUnit; + /** * A JobService to periodically collect camera usage stats. */ @@ -50,7 +49,7 @@ public class CameraStatsJobService extends JobService { return false; } - serviceProxy.dumpUsageEvents(); + serviceProxy.dumpCameraEvents(); return false; } diff --git a/services/core/java/com/android/server/contentcapture/ContentCaptureManagerInternal.java b/services/core/java/com/android/server/contentcapture/ContentCaptureManagerInternal.java index 0812fd9fe36c..de7341d2acfb 100644 --- a/services/core/java/com/android/server/contentcapture/ContentCaptureManagerInternal.java +++ b/services/core/java/com/android/server/contentcapture/ContentCaptureManagerInternal.java @@ -21,6 +21,7 @@ import android.annotation.UserIdInt; import android.app.assist.ActivityId; import android.content.ComponentName; import android.content.ContentCaptureOptions; +import android.content.Intent; import android.os.Bundle; import android.os.IBinder; import android.service.contentcapture.ActivityEvent.ActivityEventType; @@ -40,6 +41,14 @@ public abstract class ContentCaptureManagerInternal { public abstract boolean isContentCaptureServiceForUser(int uid, @UserIdInt int userId); /** + * Notifies the intelligence service of new intent data associated with an activity start event. + * + * @return {@code false} if there was no service set for the given user + */ + public abstract boolean sendActivityStartAssistData(@UserIdInt int userId, + @NonNull IBinder activityToken, @NonNull Intent intentData); + + /** * Notifies the intelligence service of new assist data for the given activity. * * @return {@code false} if there was no service set for the given user diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index 5fd025399b12..2d5f38e2dd8c 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -3406,6 +3406,31 @@ public final class DisplayManagerService extends SystemService { } } + boolean requestDisplayPower(int displayId, boolean on) { + synchronized (mSyncRoot) { + final var display = mLogicalDisplayMapper.getDisplayLocked(displayId); + if (display == null) { + Slog.w(TAG, "requestDisplayPower: Cannot find a display with displayId=" + + displayId); + return false; + } + final BrightnessPair brightnessPair = mDisplayBrightnesses.get(displayId); + var runnable = display.getPrimaryDisplayDeviceLocked().requestDisplayStateLocked( + on ? Display.STATE_ON : Display.STATE_OFF, + on ? brightnessPair.brightness : PowerManager.BRIGHTNESS_OFF_FLOAT, + brightnessPair.sdrBrightness, + display.getDisplayOffloadSessionLocked()); + if (runnable == null) { + Slog.w(TAG, "requestDisplayPower: Cannot update the power state to ON=" + on + + " for a display with displayId=" + displayId + ", runnable is null"); + return false; + } + runnable.run(); + Slog.i(TAG, "requestDisplayPower(displayId=" + displayId + ", on=" + on + ")"); + } + return true; + } + /** * This is the object that everything in the display manager locks on. * We make it an inner class within the {@link DisplayManagerService} to so that it is @@ -4629,6 +4654,12 @@ public final class DisplayManagerService extends SystemService { DisplayManagerService.this.enableConnectedDisplay(displayId, false); } + @EnforcePermission(MANAGE_DISPLAYS) + public boolean requestDisplayPower(int displayId, boolean on) { + requestDisplayPower_enforcePermission(); + return DisplayManagerService.this.requestDisplayPower(displayId, on); + } + @EnforcePermission(RESTRICT_DISPLAY_MODES) @Override // Binder call public void requestDisplayModes(IBinder token, int displayId, @Nullable int[] modeIds) { diff --git a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java index 8c39d7de54f7..d973b71366b1 100644 --- a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java +++ b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java @@ -106,6 +106,10 @@ class DisplayManagerShellCommand extends ShellCommand { return setDisplayEnabled(true); case "disable-display": return setDisplayEnabled(false); + case "power-on": + return requestDisplayPower(true); + case "power-off": + return requestDisplayPower(false); default: return handleDefaultCommands(cmd); } @@ -592,4 +596,21 @@ class DisplayManagerShellCommand extends ShellCommand { mService.enableConnectedDisplay(displayId, enable); return 0; } + + private int requestDisplayPower(boolean enable) { + final String displayIdText = getNextArg(); + if (displayIdText == null) { + getErrPrintWriter().println("Error: no displayId specified"); + return 1; + } + final int displayId; + try { + displayId = Integer.parseInt(displayIdText); + } catch (NumberFormatException e) { + getErrPrintWriter().println("Error: invalid displayId: '" + displayIdText + "'"); + return 1; + } + mService.requestDisplayPower(displayId, enable); + return 0; + } } diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java index 0bd40d16ba4c..e5dbce9931a1 100644 --- a/services/core/java/com/android/server/input/InputManagerService.java +++ b/services/core/java/com/android/server/input/InputManagerService.java @@ -3352,6 +3352,10 @@ public class InputManagerService extends IInputManager.Stub mPointerIconCache.setPointerFillStyle(fillStyle); } + void setPointerScale(float scale) { + mPointerIconCache.setPointerScale(scale); + } + interface KeyboardBacklightControllerInterface { default void incrementKeyboardBacklight(int deviceId) {} default void decrementKeyboardBacklight(int deviceId) {} diff --git a/services/core/java/com/android/server/input/InputSettingsObserver.java b/services/core/java/com/android/server/input/InputSettingsObserver.java index 9585b49be3a9..593b0917efc7 100644 --- a/services/core/java/com/android/server/input/InputSettingsObserver.java +++ b/services/core/java/com/android/server/input/InputSettingsObserver.java @@ -16,6 +16,7 @@ package com.android.server.input; +import static android.view.PointerIcon.DEFAULT_POINTER_SCALE; import static android.view.PointerIcon.POINTER_ICON_VECTOR_STYLE_FILL_BLACK; import static android.view.flags.Flags.enableVectorCursorA11ySettings; @@ -101,7 +102,9 @@ class InputSettingsObserver extends ContentObserver { Map.entry(Settings.Secure.getUriFor(Settings.Secure.STYLUS_POINTER_ICON_ENABLED), (reason) -> updateStylusPointerIconEnabled()), Map.entry(Settings.System.getUriFor(Settings.System.POINTER_FILL_STYLE), - (reason) -> updatePointerFillStyleFromSettings())); + (reason) -> updatePointerFillStyleFromSettings()), + Map.entry(Settings.System.getUriFor(Settings.System.POINTER_SCALE), + (reason) -> updatePointerScaleFromSettings())); } /** @@ -277,4 +280,14 @@ class InputSettingsObserver extends ContentObserver { UserHandle.USER_CURRENT); mService.setPointerFillStyle(pointerFillStyle); } + + private void updatePointerScaleFromSettings() { + if (!enableVectorCursorA11ySettings()) { + return; + } + final float pointerScale = Settings.System.getFloatForUser(mContext.getContentResolver(), + Settings.System.POINTER_SCALE, DEFAULT_POINTER_SCALE, + UserHandle.USER_CURRENT); + mService.setPointerScale(pointerScale); + } } diff --git a/services/core/java/com/android/server/input/PointerIconCache.java b/services/core/java/com/android/server/input/PointerIconCache.java index 936e17f51919..44622d80da0e 100644 --- a/services/core/java/com/android/server/input/PointerIconCache.java +++ b/services/core/java/com/android/server/input/PointerIconCache.java @@ -16,6 +16,7 @@ package com.android.server.input; +import static android.view.PointerIcon.DEFAULT_POINTER_SCALE; import static android.view.PointerIcon.POINTER_ICON_VECTOR_STYLE_FILL_BLACK; import android.annotation.NonNull; @@ -63,6 +64,8 @@ final class PointerIconCache { @GuardedBy("mLoadedPointerIconsByDisplayAndType") private @PointerIcon.PointerIconVectorStyleFill int mPointerIconFillStyle = POINTER_ICON_VECTOR_STYLE_FILL_BLACK; + @GuardedBy("mLoadedPointerIconsByDisplayAndType") + private float mPointerIconScale = DEFAULT_POINTER_SCALE; private final DisplayManager.DisplayListener mDisplayListener = new DisplayManager.DisplayListener() { @@ -117,6 +120,11 @@ final class PointerIconCache { mUiThreadHandler.post(() -> handleSetPointerFillStyle(fillStyle)); } + /** Set the scale for vector pointer icons. */ + public void setPointerScale(float scale) { + mUiThreadHandler.post(() -> handleSetPointerScale(scale)); + } + /** * Get a loaded system pointer icon. This will fetch the icon from the cache, or load it if * it isn't already cached. @@ -137,7 +145,7 @@ final class PointerIconCache { theme.applyStyle(PointerIcon.vectorFillStyleToResource(mPointerIconFillStyle), /* force= */ true); icon = PointerIcon.getLoadedSystemIcon(new ContextThemeWrapper(context, theme), - type, mUseLargePointerIcons); + type, mUseLargePointerIcons, mPointerIconScale); iconsByType.put(type, icon); } return Objects.requireNonNull(icon); @@ -215,6 +223,19 @@ final class PointerIconCache { mNative.reloadPointerIcons(); } + @android.annotation.UiThread + private void handleSetPointerScale(float scale) { + synchronized (mLoadedPointerIconsByDisplayAndType) { + if (mPointerIconScale == scale) { + return; + } + mPointerIconScale = scale; + // Clear all cached icons on all displays. + mLoadedPointerIconsByDisplayAndType.clear(); + } + mNative.reloadPointerIcons(); + } + // Updates the cached display density for the given displayId, and returns true if // the cached density changed. @GuardedBy("mLoadedPointerIconsByDisplayAndType") diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 7f39232c1393..6b15bcee7b5b 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -5241,7 +5241,8 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. return; } - final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId); + final int userId = mCurrentUserId; + final InputMethodSettings settings = InputMethodSettingsRepository.get(userId); boolean reenableMinimumNonAuxSystemImes = false; // TODO: The following code should find better place to live. @@ -5304,18 +5305,18 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. updateDefaultVoiceImeIfNeededLocked(); // TODO: Instantiate mSwitchingController for each user. - if (settings.getUserId() == mSwitchingController.getUserId()) { + if (userId == mSwitchingController.getUserId()) { mSwitchingController.resetCircularListLocked(settings.getMethodMap()); } else { mSwitchingController = InputMethodSubtypeSwitchingController.createInstanceLocked( mContext, settings.getMethodMap(), mCurrentUserId); } // TODO: Instantiate mHardwareKeyboardShortcutController for each user. - if (settings.getUserId() == mHardwareKeyboardShortcutController.getUserId()) { + if (userId == mHardwareKeyboardShortcutController.getUserId()) { mHardwareKeyboardShortcutController.reset(settings.getMethodMap()); } else { mHardwareKeyboardShortcutController = new HardwareKeyboardShortcutController( - settings.getMethodMap(), settings.getUserId()); + settings.getMethodMap(), userId); } sendOnNavButtonFlagsChangedLocked(); @@ -5323,7 +5324,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. // Notify InputMethodListListeners of the new installed InputMethods. final List<InputMethodInfo> inputMethodList = settings.getMethodList(); mHandler.obtainMessage(MSG_DISPATCH_ON_INPUT_METHOD_LIST_UPDATED, - settings.getUserId(), 0 /* unused */, inputMethodList).sendToTarget(); + userId, 0 /* unused */, inputMethodList).sendToTarget(); } @GuardedBy("ImfLock.class") @@ -5519,44 +5520,18 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. */ @GuardedBy("ImfLock.class") InputMethodSubtype getCurrentInputMethodSubtypeLocked() { - String selectedMethodId = getSelectedMethodIdLocked(); + final int userId = mCurrentUserId; + final var selectedMethodId = getInputMethodBindingController(userId).getSelectedMethodId(); if (selectedMethodId == null) { return null; } - final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId); - final boolean subtypeIsSelected = settings.isSubtypeSelected(); + final InputMethodSettings settings = InputMethodSettingsRepository.get(userId); final InputMethodInfo imi = settings.getMethodMap().get(selectedMethodId); if (imi == null || imi.getSubtypeCount() == 0) { return null; } - if (!subtypeIsSelected || mCurrentSubtype == null - || !SubtypeUtils.isValidSubtypeId(imi, mCurrentSubtype.hashCode())) { - int subtypeId = settings.getSelectedInputMethodSubtypeId(selectedMethodId); - if (subtypeId == NOT_A_SUBTYPE_ID) { - // If there are no selected subtypes, the framework will try to find - // the most applicable subtype from explicitly or implicitly enabled - // subtypes. - List<InputMethodSubtype> explicitlyOrImplicitlyEnabledSubtypes = - settings.getEnabledInputMethodSubtypeList(imi, true); - // If there is only one explicitly or implicitly enabled subtype, - // just returns it. - if (explicitlyOrImplicitlyEnabledSubtypes.size() == 1) { - mCurrentSubtype = explicitlyOrImplicitlyEnabledSubtypes.get(0); - } else if (explicitlyOrImplicitlyEnabledSubtypes.size() > 1) { - final String locale = SystemLocaleWrapper.get(settings.getUserId()) - .get(0).toString(); - mCurrentSubtype = SubtypeUtils.findLastResortApplicableSubtype( - explicitlyOrImplicitlyEnabledSubtypes, - SubtypeUtils.SUBTYPE_MODE_KEYBOARD, locale, true); - if (mCurrentSubtype == null) { - mCurrentSubtype = SubtypeUtils.findLastResortApplicableSubtype( - explicitlyOrImplicitlyEnabledSubtypes, null, locale, true); - } - } - } else { - mCurrentSubtype = SubtypeUtils.getSubtypes(imi).get(subtypeId); - } - } + mCurrentSubtype = SubtypeUtils.getCurrentInputMethodSubtype(imi, settings, + mCurrentSubtype); return mCurrentSubtype; } diff --git a/services/core/java/com/android/server/inputmethod/InputMethodSettings.java b/services/core/java/com/android/server/inputmethod/InputMethodSettings.java index a558838172f8..7ce4074bb1d0 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodSettings.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodSettings.java @@ -51,8 +51,23 @@ final class InputMethodSettings { public static final boolean DEBUG = false; private static final String TAG = "InputMethodSettings"; - private static final int NOT_A_SUBTYPE_ID = InputMethodUtils.NOT_A_SUBTYPE_ID; - private static final String NOT_A_SUBTYPE_ID_STR = String.valueOf(NOT_A_SUBTYPE_ID); + /** + * An integer code that represents "no subtype" when a subtype hashcode is used. + * + * <p>Due to historical confusions with {@link InputMethodUtils#NOT_A_SUBTYPE_ID}, we have + * used {@code -1} here. We cannot change this value as it's already saved into secure settings. + * </p> + */ + private static final int INVALID_SUBTYPE_HASHCODE = -1; + /** + * A string code that represents "no subtype" when a subtype hashcode is used. + * + * <p>Due to historical confusions with {@link InputMethodUtils#NOT_A_SUBTYPE_ID}, we have + * used {@code "-1"} here. We cannot change this value as it's already saved into secure + * settings.</p> + */ + private static final String INVALID_SUBTYPE_HASHCODE_STR = + String.valueOf(INVALID_SUBTYPE_HASHCODE); private static final char INPUT_METHOD_SEPARATOR = InputMethodUtils.INPUT_METHOD_SEPARATOR; private static final char INPUT_METHOD_SUBTYPE_SEPARATOR = InputMethodUtils.INPUT_METHOD_SUBTYPE_SEPARATOR; @@ -259,34 +274,33 @@ final class InputMethodSettings { } private void saveSubtypeHistory( - List<Pair<String, String>> savedImes, String newImeId, String newSubtypeId) { + List<Pair<String, String>> savedImes, String newImeId, String newSubtypeHashCodeStr) { final StringBuilder builder = new StringBuilder(); boolean isImeAdded = false; - if (!TextUtils.isEmpty(newImeId) && !TextUtils.isEmpty(newSubtypeId)) { + if (!TextUtils.isEmpty(newImeId) && !TextUtils.isEmpty(newSubtypeHashCodeStr)) { builder.append(newImeId).append(INPUT_METHOD_SUBTYPE_SEPARATOR).append( - newSubtypeId); + newSubtypeHashCodeStr); isImeAdded = true; } for (int i = 0; i < savedImes.size(); ++i) { final Pair<String, String> ime = savedImes.get(i); final String imeId = ime.first; - String subtypeId = ime.second; - if (TextUtils.isEmpty(subtypeId)) { - subtypeId = NOT_A_SUBTYPE_ID_STR; + String subtypeHashCodeStr = ime.second; + if (TextUtils.isEmpty(subtypeHashCodeStr)) { + subtypeHashCodeStr = INVALID_SUBTYPE_HASHCODE_STR; } if (isImeAdded) { builder.append(INPUT_METHOD_SEPARATOR); } else { isImeAdded = true; } - builder.append(imeId).append(INPUT_METHOD_SUBTYPE_SEPARATOR).append( - subtypeId); + builder.append(imeId).append(INPUT_METHOD_SUBTYPE_SEPARATOR).append(subtypeHashCodeStr); } // Remove the last INPUT_METHOD_SEPARATOR putSubtypeHistoryStr(builder.toString()); } - private void addSubtypeToHistory(String imeId, String subtypeId) { + private void addSubtypeToHistory(String imeId, String subtypeHashCodeStr) { final List<Pair<String, String>> subtypeHistory = loadInputMethodAndSubtypeHistory(); for (int i = 0; i < subtypeHistory.size(); ++i) { final Pair<String, String> ime = subtypeHistory.get(i); @@ -301,9 +315,9 @@ final class InputMethodSettings { } } if (DEBUG) { - Slog.v(TAG, "Add subtype to the history: " + imeId + ", " + subtypeId); + Slog.v(TAG, "Add subtype to the history: " + imeId + ", " + subtypeHashCodeStr); } - saveSubtypeHistory(subtypeHistory, imeId, subtypeId); + saveSubtypeHistory(subtypeHistory, imeId, subtypeHashCodeStr); } private void putSubtypeHistoryStr(@NonNull String str) { @@ -413,26 +427,26 @@ final class InputMethodSettings { for (int j = 0; j < explicitlyEnabledSubtypes.size(); ++j) { final String s = explicitlyEnabledSubtypes.get(j); if (s.equals(subtypeHashCode)) { - // If both imeId and subtypeId are enabled, return subtypeId. + // If both imeId and subtype are enabled, return subtypeId. try { final int hashCode = Integer.parseInt(subtypeHashCode); - // Check whether the subtype id is valid or not - if (SubtypeUtils.isValidSubtypeId(imi, hashCode)) { + // Check whether the subtype is valid or not + if (SubtypeUtils.isValidSubtypeHashCode(imi, hashCode)) { return s; } else { - return NOT_A_SUBTYPE_ID_STR; + return INVALID_SUBTYPE_HASHCODE_STR; } } catch (NumberFormatException e) { - return NOT_A_SUBTYPE_ID_STR; + return INVALID_SUBTYPE_HASHCODE_STR; } } } } - // If imeId was enabled but subtypeId was disabled. - return NOT_A_SUBTYPE_ID_STR; + // If imeId was enabled but subtype was disabled. + return INVALID_SUBTYPE_HASHCODE_STR; } } - // If both imeId and subtypeId are disabled, return null + // If both imeId and subtype are disabled, return null return null; } @@ -451,14 +465,14 @@ final class InputMethodSettings { String nextImsStr = inputMethodSplitter.next(); subtypeSplitter.setString(nextImsStr); if (subtypeSplitter.hasNext()) { - String subtypeId = NOT_A_SUBTYPE_ID_STR; + String subtypeHashCodeStr = INVALID_SUBTYPE_HASHCODE_STR; // The first element is ime id. String imeId = subtypeSplitter.next(); while (subtypeSplitter.hasNext()) { - subtypeId = subtypeSplitter.next(); + subtypeHashCodeStr = subtypeSplitter.next(); break; } - imsList.add(new Pair<>(imeId, subtypeId)); + imsList.add(new Pair<>(imeId, subtypeHashCodeStr)); } } return imsList; @@ -528,13 +542,8 @@ final class InputMethodSettings { return imi; } - boolean isSubtypeSelected() { - return getSelectedInputMethodSubtypeHashCode() != NOT_A_SUBTYPE_ID; - } - private int getSelectedInputMethodSubtypeHashCode() { - return getInt(Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE, - NOT_A_SUBTYPE_ID); + return getInt(Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE, INVALID_SUBTYPE_HASHCODE); } @UserIdInt @@ -545,7 +554,7 @@ final class InputMethodSettings { int getSelectedInputMethodSubtypeId(String selectedImiId) { final InputMethodInfo imi = mMethodMap.get(selectedImiId); if (imi == null) { - return NOT_A_SUBTYPE_ID; + return InputMethodUtils.NOT_A_SUBTYPE_ID; } final int subtypeHashCode = getSelectedInputMethodSubtypeHashCode(); return SubtypeUtils.getSubtypeIdFromHashCode(imi, subtypeHashCode); @@ -553,12 +562,12 @@ final class InputMethodSettings { void saveCurrentInputMethodAndSubtypeToHistory(String curMethodId, InputMethodSubtype currentSubtype) { - String subtypeId = NOT_A_SUBTYPE_ID_STR; + String subtypeHashCodeStr = INVALID_SUBTYPE_HASHCODE_STR; if (currentSubtype != null) { - subtypeId = String.valueOf(currentSubtype.hashCode()); + subtypeHashCodeStr = String.valueOf(currentSubtype.hashCode()); } if (InputMethodUtils.canAddToLastInputMethod(currentSubtype)) { - addSubtypeToHistory(curMethodId, subtypeId); + addSubtypeToHistory(curMethodId, subtypeHashCodeStr); } } @@ -583,7 +592,7 @@ final class InputMethodSettings { } final int subtypeHashCode = getSelectedInputMethodSubtypeHashCode(); - if (subtypeHashCode != NOT_A_SUBTYPE_ID) { + if (subtypeHashCode != INVALID_SUBTYPE_HASHCODE) { final int subtypeIndex = SubtypeUtils.getSubtypeIdFromHashCode(imi, subtypeHashCode); if (subtypeIndex >= 0) { @@ -645,10 +654,10 @@ final class InputMethodSettings { final IntArray validSubtypeHashCodes = new IntArray(subtypeHashCodes.length); for (int subtypeHashCode : subtypeHashCodes) { - if (subtypeHashCode == NOT_A_SUBTYPE_ID) { - continue; // NOT_A_SUBTYPE_ID must not be saved + if (subtypeHashCode == INVALID_SUBTYPE_HASHCODE) { + continue; // INVALID_SUBTYPE_HASHCODE must not be saved } - if (!SubtypeUtils.isValidSubtypeId(imi, subtypeHashCode)) { + if (!SubtypeUtils.isValidSubtypeHashCode(imi, subtypeHashCode)) { continue; // this subtype does not exist in InputMethodInfo. } if (validSubtypeHashCodes.indexOf(subtypeHashCode) >= 0) { diff --git a/services/core/java/com/android/server/inputmethod/SubtypeUtils.java b/services/core/java/com/android/server/inputmethod/SubtypeUtils.java index 3d5c867768ac..1b4c0d6ef4d5 100644 --- a/services/core/java/com/android/server/inputmethod/SubtypeUtils.java +++ b/services/core/java/com/android/server/inputmethod/SubtypeUtils.java @@ -16,9 +16,11 @@ package com.android.server.inputmethod; +import android.annotation.AnyThread; import android.annotation.NonNull; import android.annotation.Nullable; import android.os.LocaleList; +import android.provider.Settings; import android.text.TextUtils; import android.util.ArrayMap; import android.util.Slog; @@ -100,7 +102,7 @@ final class SubtypeUtils { return subtypes; } - static boolean isValidSubtypeId(InputMethodInfo imi, int subtypeHashCode) { + static boolean isValidSubtypeHashCode(InputMethodInfo imi, int subtypeHashCode) { return getSubtypeIdFromHashCode(imi, subtypeHashCode) != NOT_A_SUBTYPE_ID; } @@ -289,4 +291,54 @@ final class SubtypeUtils { } return applicableSubtype; } + + /** + * Returns a {@link InputMethodSubtype} available in {@code imi} based on + * {@link Settings.Secure#SELECTED_INPUT_METHOD_SUBTYPE}. + * + * @param imi {@link InputMethodInfo} to find out the current + * {@link InputMethodSubtype} + * @param settings {@link InputMethodSettings} to be used to find out the current + * {@link InputMethodSubtype} + * @param currentSubtype the current value that will be used as fallback + * @return {@link InputMethodSubtype} to be used as the current {@link InputMethodSubtype} + */ + @AnyThread + @Nullable + static InputMethodSubtype getCurrentInputMethodSubtype( + @NonNull InputMethodInfo imi, @NonNull InputMethodSettings settings, + @Nullable InputMethodSubtype currentSubtype) { + final int userId = settings.getUserId(); + final int selectedSubtypeHashCode = SecureSettingsWrapper.getInt( + Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE, NOT_A_SUBTYPE_ID, userId); + if (selectedSubtypeHashCode != NOT_A_SUBTYPE_ID && currentSubtype != null + && isValidSubtypeHashCode(imi, currentSubtype.hashCode())) { + return currentSubtype; + } + + final int subtypeId = settings.getSelectedInputMethodSubtypeId(imi.getId()); + if (subtypeId != NOT_A_SUBTYPE_ID) { + return imi.getSubtypeAt(subtypeId); + } + + // If there are no selected subtypes, the framework will try to find the most applicable + // subtype from explicitly or implicitly enabled subtypes. + final List<InputMethodSubtype> subtypes = settings.getEnabledInputMethodSubtypeList(imi, + true); + if (subtypes.isEmpty()) { + return currentSubtype; + } + // If there is only one explicitly or implicitly enabled subtype, + // just returns it. + if (subtypes.size() == 1) { + return subtypes.get(0); + } + final String locale = SystemLocaleWrapper.get(userId).get(0).toString(); + final var subtype = findLastResortApplicableSubtype(subtypes, SUBTYPE_MODE_KEYBOARD, locale, + true); + if (subtype != null) { + return subtype; + } + return findLastResortApplicableSubtype(subtypes, null, locale, true); + } } diff --git a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java index f03c639c103c..d9e22c5a270f 100644 --- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java @@ -28,6 +28,7 @@ import static android.Manifest.permission.READ_PHONE_STATE; import static android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE; import static android.app.ActivityManager.PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK; import static android.app.ActivityManager.PROCESS_CAPABILITY_USER_RESTRICTED_NETWORK; +import static android.app.ActivityManager.PROCESS_STATE_LAST_ACTIVITY; import static android.app.ActivityManager.PROCESS_STATE_UNKNOWN; import static android.app.ActivityManager.isProcStateConsideredInteraction; import static android.app.ActivityManager.printCapabilitiesSummary; @@ -523,6 +524,12 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { */ private boolean mUseMeteredFirewallChains; + /** + * Whether or not sensitive process states and non-sensitive process-states have different + * delays before network is blocked after transitioning to background. + */ + private boolean mUseDifferentDelaysForBackgroundChain; + // See main javadoc for instructions on how to use these locks. final Object mUidRulesFirstLock = new Object(); final Object mNetworkPoliciesSecondLock = new Object(); @@ -552,10 +559,43 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { * {@link NetworkPolicyManager#BACKGROUND_THRESHOLD_STATE} will lose network access. * The delay is meant to prevent churn due to quick process-state changes. * Note that there is no delay while granting network access. + * + * This is only used when the flag {@link #mUseDifferentDelaysForBackgroundChain} is disabled. */ @VisibleForTesting long mBackgroundRestrictionDelayMs = TimeUnit.SECONDS.toMillis(5); + /** + * Short delay after which a uid going into a process state having priority equal to + * {@link NetworkPolicyManager#BACKGROUND_THRESHOLD_STATE} or lower will lose network access. + * + * This will apply to apps that should be fine with losing network access immediately. + * It is only meant as a debounce to prevent churn due to quick process-state changes. + * Note that there is no delay while granting network access. + * + * This is only used when the flag {@link #mUseDifferentDelaysForBackgroundChain} is enabled. + */ + @VisibleForTesting + long mBackgroundRestrictionShortDelayMs = TimeUnit.SECONDS.toMillis(2); + + /** + * Long delay after which a uid going into a process state having priority equal to + * {@link NetworkPolicyManager#BACKGROUND_THRESHOLD_STATE} or lower will lose network access. + * + * Unlike {@link #mBackgroundRestrictionShortDelayMs}, this is meant to be applied to apps + * in sensitive proc-states like {@link ActivityManager#PROCESS_STATE_TOP_SLEEPING} and + * {@link ActivityManager#PROCESS_STATE_LAST_ACTIVITY}, where the user may switch to this app + * before this period and any latency in granting network access before resuming app activities + * may degrade experience. + * + * This is only used when the flag {@link #mUseDifferentDelaysForBackgroundChain} is enabled. + */ + @VisibleForTesting + long mBackgroundRestrictionLongDelayMs = TimeUnit.SECONDS.toMillis(20); + + @GuardedBy("mUidRulesFirstLock") + private long mNextProcessBackgroundUidsTime = Long.MAX_VALUE; + /** Defined network policies. */ @GuardedBy("mNetworkPoliciesSecondLock") final ArrayMap<NetworkTemplate, NetworkPolicy> mNetworkPolicy = new ArrayMap<>(); @@ -1007,6 +1047,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class); mUseMeteredFirewallChains = Flags.useMeteredFirewallChains(); + mUseDifferentDelaysForBackgroundChain = Flags.useDifferentDelaysForBackgroundChain(); synchronized (mUidRulesFirstLock) { synchronized (mNetworkPoliciesSecondLock) { @@ -1241,11 +1282,21 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { // different chains may change. return true; } - if (mBackgroundNetworkRestricted && (previousProcState >= BACKGROUND_THRESHOLD_STATE) + if (mBackgroundNetworkRestricted) { + if ((previousProcState >= BACKGROUND_THRESHOLD_STATE) != (newProcState >= BACKGROUND_THRESHOLD_STATE)) { - // Proc-state change crossed BACKGROUND_THRESHOLD_STATE: Network rules for the - // BACKGROUND chain may change. - return true; + // Proc-state change crossed BACKGROUND_THRESHOLD_STATE: The network rules will + // need to be re-evaluated for the background chain. + return true; + } + if (mUseDifferentDelaysForBackgroundChain + && newProcState >= BACKGROUND_THRESHOLD_STATE + && getBackgroundTransitioningDelay(newProcState) + < getBackgroundTransitioningDelay(previousProcState)) { + // The old and new proc-state both are in the blocked state but the background + // transition delay is reduced, so we may have to update the rules sooner. + return true; + } } final int networkCapabilities = PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK | PROCESS_CAPABILITY_USER_RESTRICTED_NETWORK; @@ -4045,6 +4096,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { + mBackgroundNetworkRestricted); fout.println(Flags.FLAG_USE_METERED_FIREWALL_CHAINS + ": " + mUseMeteredFirewallChains); + fout.println(Flags.FLAG_USE_DIFFERENT_DELAYS_FOR_BACKGROUND_CHAIN + ": " + + mUseDifferentDelaysForBackgroundChain); fout.println(); fout.println("mRestrictBackgroundLowPowerMode: " + mRestrictBackgroundLowPowerMode); @@ -4188,20 +4241,34 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { fout.decreaseIndent(); } - size = mBackgroundTransitioningUids.size(); - if (size > 0) { - final long nowUptime = SystemClock.uptimeMillis(); - fout.println("Uids transitioning to background:"); - fout.increaseIndent(); - for (int i = 0; i < size; i++) { - fout.print("UID="); - fout.print(mBackgroundTransitioningUids.keyAt(i)); - fout.print(", "); - TimeUtils.formatDuration(mBackgroundTransitioningUids.valueAt(i), nowUptime, - fout); + if (mBackgroundNetworkRestricted) { + fout.println(); + if (mUseDifferentDelaysForBackgroundChain) { + fout.print("Background restrictions short delay: "); + TimeUtils.formatDuration(mBackgroundRestrictionShortDelayMs, fout); + fout.println(); + + fout.print("Background restrictions long delay: "); + TimeUtils.formatDuration(mBackgroundRestrictionLongDelayMs, fout); fout.println(); } - fout.decreaseIndent(); + + size = mBackgroundTransitioningUids.size(); + if (size > 0) { + final long nowUptime = SystemClock.uptimeMillis(); + fout.println("Uids transitioning to background:"); + fout.increaseIndent(); + for (int i = 0; i < size; i++) { + fout.print("UID="); + fout.print(mBackgroundTransitioningUids.keyAt(i)); + fout.print(", "); + TimeUtils.formatDuration(mBackgroundTransitioningUids.valueAt(i), + nowUptime, fout); + fout.println(); + } + fout.decreaseIndent(); + } + fout.println(); } final SparseBooleanArray knownUids = new SparseBooleanArray(); @@ -4337,6 +4404,15 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { || isProcStateAllowedNetworkWhileBackground(mUidState.get(uid)); } + private long getBackgroundTransitioningDelay(int procState) { + if (mUseDifferentDelaysForBackgroundChain) { + return procState <= PROCESS_STATE_LAST_ACTIVITY ? mBackgroundRestrictionLongDelayMs + : mBackgroundRestrictionShortDelayMs; + } else { + return mBackgroundRestrictionDelayMs; + } + } + /** * Process state of UID changed; if needed, will trigger * {@link #updateRulesForDataUsageRestrictionsUL(int)} and @@ -4387,19 +4463,41 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { mBackgroundTransitioningUids.delete(uid); updateRuleForBackgroundUL(uid); updatePowerRestrictionRules = true; - } else if (wasAllowed && !isAllowed) { + } else if (!isAllowed) { + final int transitionIdx = mBackgroundTransitioningUids.indexOfKey(uid); final long completionTimeMs = SystemClock.uptimeMillis() - + mBackgroundRestrictionDelayMs; - if (mBackgroundTransitioningUids.indexOfKey(uid) < 0) { - // This is just a defensive check in case the upstream code ever makes - // multiple calls for the same process state change. - mBackgroundTransitioningUids.put(uid, completionTimeMs); + + getBackgroundTransitioningDelay(procState); + boolean completionTimeUpdated = false; + if (wasAllowed) { + // Rules need to transition from allowed to blocked after the respective + // delay. + if (transitionIdx < 0) { + // This is just a defensive check in case the upstream code ever + // makes multiple calls for the same process state change. + mBackgroundTransitioningUids.put(uid, completionTimeMs); + completionTimeUpdated = true; + } + } else if (mUseDifferentDelaysForBackgroundChain) { + // wasAllowed was false, but the transition delay may have reduced. + // Currently, this can happen when the uid transitions from + // LAST_ACTIVITY to CACHED_ACTIVITY, for example. + if (transitionIdx >= 0 + && completionTimeMs < mBackgroundTransitioningUids.valueAt( + transitionIdx)) { + mBackgroundTransitioningUids.setValueAt(transitionIdx, + completionTimeMs); + completionTimeUpdated = true; + } } - if (!mHandler.hasMessages(MSG_PROCESS_BACKGROUND_TRANSITIONING_UIDS)) { - // Many uids may be in this "transitioning" state at the same time, so - // using one message at a time to avoid congestion in the MessageQueue. + if (completionTimeUpdated + && completionTimeMs < mNextProcessBackgroundUidsTime) { + // Many uids may be in this "transitioning" state at the same time, + // so we always keep one message to process transition completion at + // the earliest time. + mHandler.removeMessages(MSG_PROCESS_BACKGROUND_TRANSITIONING_UIDS); mHandler.sendEmptyMessageAtTime( MSG_PROCESS_BACKGROUND_TRANSITIONING_UIDS, completionTimeMs); + mNextProcessBackgroundUidsTime = completionTimeMs; } } } @@ -5750,10 +5848,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { updateRuleForBackgroundUL(uid); updateRulesForPowerRestrictionsUL(uid, false); } - } - if (nextCheckTime < Long.MAX_VALUE) { - mHandler.sendEmptyMessageAtTime(MSG_PROCESS_BACKGROUND_TRANSITIONING_UIDS, - nextCheckTime); + mNextProcessBackgroundUidsTime = nextCheckTime; + if (nextCheckTime < Long.MAX_VALUE) { + mHandler.sendEmptyMessageAtTime( + MSG_PROCESS_BACKGROUND_TRANSITIONING_UIDS, nextCheckTime); + } } return true; } diff --git a/services/core/java/com/android/server/net/flags.aconfig b/services/core/java/com/android/server/net/flags.aconfig index e986dd81b94b..586baf022897 100644 --- a/services/core/java/com/android/server/net/flags.aconfig +++ b/services/core/java/com/android/server/net/flags.aconfig @@ -17,3 +17,13 @@ flag { purpose: PURPOSE_BUGFIX } } + +flag { + name: "use_different_delays_for_background_chain" + namespace: "backstage_power" + description: "Grant longer grace periods for sensitive process-states before blocking network using the background chain" + bug: "323963467" + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java index cd1d7996fbac..ea71953d7cb3 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java @@ -165,6 +165,7 @@ import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Optional; import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; @@ -368,18 +369,32 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt return false; } + int userId = UserHandle.getUserId(uid); + + boolean isInSetup = + getSecureInt(Settings.Secure.USER_SETUP_COMPLETE, userId) + .map(setupState -> setupState == 0) + .orElse(false); + if (isInSetup) { + return true; + } + + boolean isInDeferredSetup = + getSecureInt(Settings.Secure.USER_SETUP_PERSONALIZATION_STATE, userId) + .map(state -> + state == Settings.Secure.USER_SETUP_PERSONALIZATION_STARTED) + .orElse(false); + return isInDeferredSetup; + } + + private Optional<Integer> getSecureInt(String settingName, int userId) { try { - int userId = UserHandle.getUserId(uid); - boolean isInSetup = Settings.Secure.getIntForUser(mContext.getContentResolver(), - Settings.Secure.USER_SETUP_COMPLETE, userId) == 0; - boolean isInDeferredSetup = Settings.Secure.getIntForUser( - mContext.getContentResolver(), - Settings.Secure.USER_SETUP_PERSONALIZATION_STATE, userId) - == Settings.Secure.USER_SETUP_PERSONALIZATION_STARTED; - return isInSetup || isInDeferredSetup; + return Optional.of( + Settings.Secure.getIntForUser( + mContext.getContentResolver(), settingName, userId)); } catch (Settings.SettingNotFoundException e) { - Slog.w(LOG_TAG, "Failed to check if the user is in restore: " + e); - return false; + Slog.i(LOG_TAG, "Setting " + settingName + " not found", e); + return Optional.empty(); } } diff --git a/services/core/java/com/android/server/power/Notifier.java b/services/core/java/com/android/server/power/Notifier.java index 11b9e776204c..80c262a9b708 100644 --- a/services/core/java/com/android/server/power/Notifier.java +++ b/services/core/java/com/android/server/power/Notifier.java @@ -17,6 +17,7 @@ package com.android.server.power; import android.annotation.Nullable; +import android.annotation.SuppressLint; import android.annotation.UserIdInt; import android.app.ActivityManagerInternal; import android.app.AppOpsManager; @@ -197,9 +198,10 @@ public class Notifier { FaceDownDetector faceDownDetector, ScreenUndimDetector screenUndimDetector, Executor backgroundExecutor, PowerManagerFlags powerManagerFlags, Injector injector) { mContext = context; + mInjector = (injector == null) ? new RealInjector() : injector; mFlags = powerManagerFlags; mBatteryStats = batteryStats; - mAppOps = mContext.getSystemService(AppOpsManager.class); + mAppOps = mInjector.getAppOpsManager(context); mSuspendBlocker = suspendBlocker; mPolicy = policy; mFaceDownDetector = faceDownDetector; @@ -230,7 +232,6 @@ public class Notifier { mShowWirelessChargingAnimationConfig = context.getResources().getBoolean( com.android.internal.R.bool.config_showBuiltinWirelessChargingAnim); - mInjector = (injector == null) ? new RealInjector() : injector; mWakeLockLog = mInjector.getWakeLockLog(context); // Initialize interactive state for battery stats. try { @@ -264,6 +265,7 @@ public class Notifier { /** * Called when a wake lock is acquired. */ + @SuppressLint("AndroidFrameworkRequiresPermission") public void onWakeLockAcquired(int flags, String tag, String packageName, int ownerUid, int ownerPid, WorkSource workSource, String historyTag, IWakeLockCallback callback) { @@ -273,27 +275,28 @@ public class Notifier { + ", ownerUid=" + ownerUid + ", ownerPid=" + ownerPid + ", workSource=" + workSource); } - notifyWakeLockListener(callback, tag, true, ownerUid, flags); - final int monitorType = getBatteryStatsWakeLockMonitorType(flags); - if (monitorType >= 0) { - try { - final boolean unimportantForLogging = ownerUid == Process.SYSTEM_UID - && (flags & PowerManager.UNIMPORTANT_FOR_LOGGING) != 0; - if (workSource != null) { - mBatteryStats.noteStartWakelockFromSource(workSource, ownerPid, tag, - historyTag, monitorType, unimportantForLogging); - } else { - mBatteryStats.noteStartWakelock(ownerUid, ownerPid, tag, historyTag, - monitorType, unimportantForLogging); - // XXX need to deal with disabled operations. - mAppOps.startOpNoThrow(AppOpsManager.OP_WAKE_LOCK, ownerUid, packageName); + notifyWakeLockListener(callback, tag, true, ownerUid, ownerPid, flags, workSource, + packageName, historyTag); + if (!mFlags.improveWakelockLatency()) { + final int monitorType = getBatteryStatsWakeLockMonitorType(flags); + if (monitorType >= 0) { + try { + final boolean unimportantForLogging = ownerUid == Process.SYSTEM_UID + && (flags & PowerManager.UNIMPORTANT_FOR_LOGGING) != 0; + if (workSource != null) { + mBatteryStats.noteStartWakelockFromSource(workSource, ownerPid, tag, + historyTag, monitorType, unimportantForLogging); + } else { + mBatteryStats.noteStartWakelock(ownerUid, ownerPid, tag, historyTag, + monitorType, unimportantForLogging); + // XXX need to deal with disabled operations. + mAppOps.startOpNoThrow(AppOpsManager.OP_WAKE_LOCK, ownerUid, packageName, + false, null, null); + } + } catch (RemoteException ex) { + // Ignore } - } catch (RemoteException ex) { - // Ignore } - } - - if (!mFlags.improveWakelockLatency()) { mWakeLockLog.onWakeLockAcquired(tag, ownerUid, flags, /*eventTime=*/ -1); } mWakefulnessSessionObserver.onWakeLockAcquired(flags); @@ -404,6 +407,7 @@ public class Notifier { /** * Called when a wake lock is released. */ + @SuppressLint("AndroidFrameworkRequiresPermission") public void onWakeLockReleased(int flags, String tag, String packageName, int ownerUid, int ownerPid, WorkSource workSource, String historyTag, IWakeLockCallback callback, int releaseReason) { @@ -413,23 +417,24 @@ public class Notifier { + ", ownerUid=" + ownerUid + ", ownerPid=" + ownerPid + ", workSource=" + workSource); } - notifyWakeLockListener(callback, tag, false, ownerUid, flags); - final int monitorType = getBatteryStatsWakeLockMonitorType(flags); - if (monitorType >= 0) { - try { - if (workSource != null) { - mBatteryStats.noteStopWakelockFromSource(workSource, ownerPid, tag, - historyTag, monitorType); - } else { - mBatteryStats.noteStopWakelock(ownerUid, ownerPid, tag, - historyTag, monitorType); - mAppOps.finishOp(AppOpsManager.OP_WAKE_LOCK, ownerUid, packageName); + notifyWakeLockListener(callback, tag, false, ownerUid, ownerPid, flags, workSource, + packageName, historyTag); + if (!mFlags.improveWakelockLatency()) { + final int monitorType = getBatteryStatsWakeLockMonitorType(flags); + if (monitorType >= 0) { + try { + if (workSource != null) { + mBatteryStats.noteStopWakelockFromSource(workSource, ownerPid, tag, + historyTag, monitorType); + } else { + mBatteryStats.noteStopWakelock(ownerUid, ownerPid, tag, + historyTag, monitorType); + mAppOps.finishOp(AppOpsManager.OP_WAKE_LOCK, ownerUid, packageName, null); + } + } catch (RemoteException ex) { + // Ignore } - } catch (RemoteException ex) { - // Ignore } - } - if (!mFlags.improveWakelockLatency()) { mWakeLockLog.onWakeLockReleased(tag, ownerUid, /*eventTime=*/ -1); } mWakefulnessSessionObserver.onWakeLockReleased(flags, releaseReason); @@ -1049,24 +1054,75 @@ public class Notifier { } private void notifyWakeLockListener(IWakeLockCallback callback, String tag, boolean isEnabled, - int ownerUid, int flags) { - if (callback != null) { - long currentTime = mInjector.currentTimeMillis(); - mHandler.post(() -> { - try { - if (mFlags.improveWakelockLatency()) { + int ownerUid, int ownerPid, int flags, WorkSource workSource, String packageName, + String historyTag) { + if (mFlags.improveWakelockLatency()) { + if (callback != null) { + long currentTime = mInjector.currentTimeMillis(); + mHandler.post(() -> { + try { if (isEnabled) { - mWakeLockLog.onWakeLockAcquired(tag, ownerUid, flags, currentTime); + notifyWakelockAcquisition(tag, ownerUid, ownerPid, flags, + workSource, packageName, historyTag, currentTime); } else { - mWakeLockLog.onWakeLockReleased(tag, ownerUid, currentTime); + notifyWakelockRelease(tag, ownerUid, ownerPid, flags, + workSource, packageName, historyTag, currentTime); } + callback.onStateChanged(isEnabled); + } catch (RemoteException e) { + Slog.e(TAG, "Wakelock.mCallback [" + tag + "] is already dead.", e); } - callback.onStateChanged(isEnabled); - } catch (RemoteException e) { - Slog.e(TAG, "Wakelock.mCallback [" + tag + "] is already dead.", e); + }); + } + } + + } + + @SuppressLint("AndroidFrameworkRequiresPermission") + private void notifyWakelockAcquisition(String tag, int ownerUid, int ownerPid, int flags, + WorkSource workSource, String packageName, String historyTag, long currentTime) { + final int monitorType = getBatteryStatsWakeLockMonitorType(flags); + if (monitorType >= 0) { + try { + final boolean unimportantForLogging = ownerUid == Process.SYSTEM_UID + && (flags & PowerManager.UNIMPORTANT_FOR_LOGGING) != 0; + if (workSource != null) { + mBatteryStats.noteStartWakelockFromSource(workSource, ownerPid, tag, + historyTag, monitorType, unimportantForLogging); + } else { + mBatteryStats.noteStartWakelock(ownerUid, ownerPid, tag, historyTag, + monitorType, unimportantForLogging); + // XXX need to deal with disabled operations. + mAppOps.startOpNoThrow( + AppOpsManager.OP_WAKE_LOCK, ownerUid, packageName, + false, null, null); } - }); + } catch (RemoteException ex) { + // Do Nothing + } } + mWakeLockLog.onWakeLockAcquired(tag, ownerUid, flags, currentTime); + } + + @SuppressLint("AndroidFrameworkRequiresPermission") + private void notifyWakelockRelease(String tag, int ownerUid, int ownerPid, int flags, + WorkSource workSource, String packageName, String historyTag, long currentTime) { + final int monitorType = getBatteryStatsWakeLockMonitorType(flags); + if (monitorType >= 0) { + try { + if (workSource != null) { + mBatteryStats.noteStopWakelockFromSource(workSource, ownerPid, tag, + historyTag, monitorType); + } else { + mBatteryStats.noteStopWakelock(ownerUid, ownerPid, tag, + historyTag, monitorType); + mAppOps.finishOp(AppOpsManager.OP_WAKE_LOCK, ownerUid, packageName, null); + } + } catch (RemoteException ex) { + // Ignore + } + } + mWakeLockLog.onWakeLockReleased(tag, ownerUid, currentTime); } private final class NotifierHandler extends Handler { @@ -1114,6 +1170,11 @@ public class Notifier { * Gets the WakeLockLog object */ WakeLockLog getWakeLockLog(Context context); + + /** + * Gets the AppOpsManager system service + */ + AppOpsManager getAppOpsManager(Context context); } static class RealInjector implements Injector { @@ -1126,5 +1187,10 @@ public class Notifier { public WakeLockLog getWakeLockLog(Context context) { return new WakeLockLog(context); } + + @Override + public AppOpsManager getAppOpsManager(Context context) { + return context.getSystemService(AppOpsManager.class); + } } } diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java index d38cd885529a..15f4c8ca105f 100644 --- a/services/core/java/com/android/server/wm/ActivityRecord.java +++ b/services/core/java/com/android/server/wm/ActivityRecord.java @@ -156,6 +156,7 @@ import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANG import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__STATE__LETTERBOXED_FOR_SIZE_COMPAT_MODE; import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__STATE__NOT_LETTERBOXED; import static com.android.internal.util.FrameworkStatsLog.APP_COMPAT_STATE_CHANGED__STATE__NOT_VISIBLE; +import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage; import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_ANIM; import static com.android.server.policy.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER; import static com.android.server.wm.ActivityRecord.State.DESTROYED; @@ -4033,6 +4034,10 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A if (next == null) { mRootWindowContainer.ensureVisibilityAndConfig(null /* starting */, mDisplayContent, true /* deferResume */); + if (mDisplayContent.topRunningActivity() == null) { + // The transition is ready on a display with no running activities. + mTransitionController.setReady(mDisplayContent); + } } if (activityRemoved) { mRootWindowContainer.resumeFocusedTasksTopActivities(); @@ -6028,14 +6033,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A true /* activityChange */, true /* updateOomAdj */, true /* addPendingTopUid */); } - final ContentCaptureManagerInternal contentCaptureService = - LocalServices.getService(ContentCaptureManagerInternal.class); - if (contentCaptureService != null) { - contentCaptureService.notifyActivityEvent(mUserId, mActivityComponent, - ActivityEvent.TYPE_ACTIVITY_STARTED, - new ActivityId(getTask() != null ? getTask().mTaskId : INVALID_TASK_ID, - shareableActivityToken)); - } + mAtmService.mH.post(this::notifyActivityStartedToContentCaptureService); break; case PAUSED: mAtmService.updateBatteryStats(this, false); @@ -6067,6 +6065,22 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A } } + private void notifyActivityStartedToContentCaptureService() { + final ContentCaptureManagerInternal contentCaptureService = + LocalServices.getService(ContentCaptureManagerInternal.class); + if (contentCaptureService != null) { + // For ACTIVITY_STARTED content capture is directly invoked to avoid persisting + // to UsageStats. + contentCaptureService.notifyActivityEvent(mUserId, mActivityComponent, + ActivityEvent.TYPE_ACTIVITY_STARTED, + new ActivityId(getTask() != null ? getTask().mTaskId : INVALID_TASK_ID, + shareableActivityToken)); + + contentCaptureService.sendActivityStartAssistData(mUserId, + shareableActivityToken, intent); + } + } + State getState() { return mState; } diff --git a/services/core/java/com/android/server/wm/EmbeddedWindowController.java b/services/core/java/com/android/server/wm/EmbeddedWindowController.java index 0978cb49e863..a21ba2603ec6 100644 --- a/services/core/java/com/android/server/wm/EmbeddedWindowController.java +++ b/services/core/java/com/android/server/wm/EmbeddedWindowController.java @@ -77,12 +77,14 @@ class EmbeddedWindowController { mWindows.put(inputToken, window); final InputTransferToken inputTransferToken = window.getInputTransferToken(); mWindowsByInputTransferToken.put(inputTransferToken, window); - mWindowsByWindowToken.put(window.getWindowToken(), window); + final IBinder windowToken = window.getWindowToken(); + mWindowsByWindowToken.put(windowToken, window); updateProcessController(window); window.mClient.linkToDeath(()-> { synchronized (mGlobalLock) { mWindows.remove(inputToken); mWindowsByInputTransferToken.remove(inputTransferToken); + mWindowsByWindowToken.remove(windowToken); } }, 0); } catch (RemoteException e) { diff --git a/services/core/java/com/android/server/wm/RunningTasks.java b/services/core/java/com/android/server/wm/RunningTasks.java index 1cc1a57756e9..7510180f54a6 100644 --- a/services/core/java/com/android/server/wm/RunningTasks.java +++ b/services/core/java/com/android/server/wm/RunningTasks.java @@ -157,7 +157,7 @@ class RunningTasks implements Consumer<Task> { // home & recent tasks return; } - if (task.isVisible()) { + if (task.isVisibleRequested()) { mTmpVisibleTasks.add(task); } else { mTmpInvisibleTasks.add(task); diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java index c02a43786b39..22f718ddbd22 100644 --- a/services/core/java/com/android/server/wm/Task.java +++ b/services/core/java/com/android/server/wm/Task.java @@ -4786,6 +4786,12 @@ class Task extends TaskFragment { if (com.android.window.flags.Flags.removePrepareSurfaceInPlacement() && lastParentBeforePip.mSyncState == SYNC_STATE_NONE) { lastParentBeforePip.prepareSurfaces(); + // If the moveToFront is a part of finishing transition, then make sure + // the z-order of tasks are up-to-date. + if (topActivity.mTransitionController.inFinishingTransition(topActivity)) { + Transition.assignLayers(taskDisplayArea, + taskDisplayArea.getPendingTransaction()); + } } } if (isPip2ExperimentEnabled) { diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java index 4aa3e3644daa..63ca46991c73 100644 --- a/services/core/java/com/android/server/wm/Transition.java +++ b/services/core/java/com/android/server/wm/Transition.java @@ -1048,7 +1048,7 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { // the animation played. This puts the layers back into the correct order. for (int i = displays.size() - 1; i >= 0; --i) { if (displays.valueAt(i) == null) continue; - updateDisplayLayers(displays.valueAt(i), t); + assignLayers(displays.valueAt(i), t); } for (int i = 0; i < info.getRootCount(); ++i) { @@ -1056,12 +1056,13 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { } } - private static void updateDisplayLayers(DisplayContent dc, SurfaceControl.Transaction t) { - dc.mTransitionController.mBuildingFinishLayers = true; + /** Assigns the layers for the start or end state of transition. */ + static void assignLayers(WindowContainer<?> wc, SurfaceControl.Transaction t) { + wc.mTransitionController.mBuildingFinishLayers = true; try { - dc.assignChildLayers(t); + wc.assignChildLayers(t); } finally { - dc.mTransitionController.mBuildingFinishLayers = false; + wc.mTransitionController.mBuildingFinishLayers = false; } } @@ -2717,7 +2718,7 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { rootLeash.setUnreleasedWarningCallSite("Transition.calculateTransitionRoots"); // Update layers to start transaction because we prevent assignment during collect, so // the layer of transition root can be correct. - updateDisplayLayers(dc, startT); + assignLayers(dc, startT); startT.setLayer(rootLeash, leashReference.getLastLayer()); outInfo.addRootLeash(endDisplayId, rootLeash, ancestor.getBounds().left, ancestor.getBounds().top); diff --git a/services/incremental/Android.bp b/services/incremental/Android.bp index 957d7c352bdf..ce05c820cbbc 100644 --- a/services/incremental/Android.bp +++ b/services/incremental/Android.bp @@ -66,7 +66,6 @@ cc_defaults { "libprotobuf-cpp-lite", "service.incremental.proto", "libvold_binder", - "libc++fs", "libziparchive_for_incfs", ], shared_libs: [ diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index cfe4e17eb1be..107c2947c550 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -1196,11 +1196,12 @@ public final class SystemServer implements Dumpable { mSystemServiceManager.startService(RecoverySystemService.Lifecycle.class); t.traceEnd(); + // Initialize RescueParty. + RescueParty.registerHealthObserver(mSystemContext); if (!Flags.recoverabilityDetection()) { // Now that we have the bare essentials of the OS up and running, take // note that we just booted, which might send out a rescue party if // we're stuck in a runtime restart loop. - RescueParty.registerHealthObserver(mSystemContext); PackageWatchdog.getInstance(mSystemContext).noteBoot(); } @@ -2917,10 +2918,10 @@ public final class SystemServer implements Dumpable { t.traceEnd(); if (Flags.recoverabilityDetection()) { - // Now that we have the essential services needed for rescue party, initialize - // RescuParty. note that we just booted, which might send out a rescue party if - // we're stuck in a runtime restart loop. - RescueParty.registerHealthObserver(mSystemContext); + // Now that we have the essential services needed for mitigations, register the boot + // with package watchdog. + // Note that we just booted, which might send out a rescue party if we're stuck in a + // runtime restart loop. PackageWatchdog.getInstance(mSystemContext).noteBoot(); } diff --git a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt index 649955614851..78dbc60dbae0 100644 --- a/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt +++ b/services/permission/java/com/android/server/permission/access/permission/PermissionService.kt @@ -533,8 +533,7 @@ class PermissionService(private val service: AccessCheckingService) : val packageState = packageManagerLocal.withFilteredSnapshot(Binder.getCallingUid(), userId).use { it.getPackageState(packageName) - } - ?: return PackageManager.PERMISSION_DENIED + } ?: return PackageManager.PERMISSION_DENIED val isPermissionGranted = service.getState { isPermissionGranted(packageState, userId, permissionName, deviceId) } @@ -1164,8 +1163,7 @@ class PermissionService(private val service: AccessCheckingService) : val packageState = packageManagerLocal.withFilteredSnapshot(Binder.getCallingUid(), userId).use { it.getPackageState(packageName) - } - ?: return false + } ?: return false service.getState { if (isPermissionGranted(packageState, userId, permissionName, deviceId)) { @@ -1216,8 +1214,7 @@ class PermissionService(private val service: AccessCheckingService) : val packageState = packageManagerLocal.withFilteredSnapshot(callingUid, userId).use { it.getPackageState(packageName) - } - ?: return false + } ?: return false val appId = packageState.appId if (UserHandle.getAppId(callingUid) != appId) { return false @@ -1546,8 +1543,7 @@ class PermissionService(private val service: AccessCheckingService) : val packageState = packageManagerLocal.withFilteredSnapshot(callingUid, userId).use { it.getPackageState(packageName) - } - ?: return null + } ?: return null val androidPackage = packageState.androidPackage ?: return null val isCallerPrivileged = @@ -1710,8 +1706,7 @@ class PermissionService(private val service: AccessCheckingService) : PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER, userId ) - ?.let { ArraySet(permissionNames).apply { this += it }.toList() } - ?: permissionNames + ?.let { ArraySet(permissionNames).apply { this += it }.toList() } ?: permissionNames setAllowlistedRestrictedPermissionsUnchecked( androidPackage, @@ -2753,27 +2748,25 @@ class PermissionService(private val service: AccessCheckingService) : ) { return false } - return try { - val contentResolver = context.contentResolver - val userId = UserHandle.getUserId(uid) - val isInSetup = - Settings.Secure.getIntForUser( - contentResolver, - Settings.Secure.USER_SETUP_COMPLETE, - userId - ) == 0 - val isInDeferredSetup = - Settings.Secure.getIntForUser( - contentResolver, - Settings.Secure.USER_SETUP_PERSONALIZATION_STATE, - userId - ) == Settings.Secure.USER_SETUP_PERSONALIZATION_STARTED - isInSetup || isInDeferredSetup + + val userId = UserHandle.getUserId(uid) + + val isInSetup = getSecureInt(Settings.Secure.USER_SETUP_COMPLETE, userId) == 0 + if (isInSetup) return true + + val isInDeferredSetup = + getSecureInt(Settings.Secure.USER_SETUP_PERSONALIZATION_STATE, userId) == + Settings.Secure.USER_SETUP_PERSONALIZATION_STARTED + return isInDeferredSetup + } + + private fun getSecureInt(settingName: String, userId: Int): Int? = + try { + Settings.Secure.getIntForUser(context.contentResolver, settingName, userId) } catch (e: Settings.SettingNotFoundException) { - Slog.w(LOG_TAG, "Failed to check if the user is in restore: $e") - false + Slog.i(LOG_TAG, "Setting $settingName not found", e) + null } - } } private class OnPermissionsChangeListeners(looper: Looper) : Handler(looper) { diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java index d0eb83aa986b..211ab03813b3 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java @@ -2569,6 +2569,63 @@ public class DisplayManagerServiceTest { } @Test + public void testPowerOnAndOffInternalDisplay() { + manageDisplaysPermission(/* granted= */ true); + DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector); + DisplayManagerService.BinderService bs = displayManager.new BinderService(); + LogicalDisplayMapper logicalDisplayMapper = displayManager.getLogicalDisplayMapper(); + FakeDisplayManagerCallback callback = new FakeDisplayManagerCallback(); + bs.registerCallbackWithEventMask(callback, STANDARD_AND_CONNECTION_DISPLAY_EVENTS); + + callback.expectsEvent(EVENT_DISPLAY_ADDED); + FakeDisplayDevice displayDevice = + createFakeDisplayDevice(displayManager, new float[]{60f}, Display.TYPE_INTERNAL); + callback.waitForExpectedEvent(); + + LogicalDisplay display = + logicalDisplayMapper.getDisplayLocked(displayDevice, /* includeDisabled= */ true); + + assertThat(displayDevice.getDisplayDeviceInfoLocked().committedState) + .isEqualTo(Display.STATE_ON); + + assertThat(displayManager.requestDisplayPower(display.getDisplayIdLocked(), false)) + .isTrue(); + + assertThat(displayDevice.getDisplayDeviceInfoLocked().committedState) + .isEqualTo(Display.STATE_OFF); + + assertThat(displayManager.requestDisplayPower(display.getDisplayIdLocked(), true)) + .isTrue(); + + assertThat(displayDevice.getDisplayDeviceInfoLocked().committedState) + .isEqualTo(Display.STATE_ON); + } + + @Test + public void testPowerOnAndOffInternalDisplay_withoutPermission_shouldThrowException() { + DisplayManagerService displayManager = new DisplayManagerService(mContext, mBasicInjector); + DisplayManagerService.BinderService bs = displayManager.new BinderService(); + LogicalDisplayMapper logicalDisplayMapper = displayManager.getLogicalDisplayMapper(); + FakeDisplayManagerCallback callback = new FakeDisplayManagerCallback(); + bs.registerCallbackWithEventMask(callback, STANDARD_AND_CONNECTION_DISPLAY_EVENTS); + + callback.expectsEvent(EVENT_DISPLAY_ADDED); + FakeDisplayDevice displayDevice = + createFakeDisplayDevice(displayManager, new float[]{60f}, Display.TYPE_INTERNAL); + callback.waitForExpectedEvent(); + + LogicalDisplay display = + logicalDisplayMapper.getDisplayLocked(displayDevice, /* includeDisabled= */ true); + var displayId = display.getDisplayIdLocked(); + + assertThat(displayDevice.getDisplayDeviceInfoLocked().committedState) + .isEqualTo(Display.STATE_ON); + + assertThrows(SecurityException.class, () -> bs.requestDisplayPower(displayId, true)); + assertThrows(SecurityException.class, () -> bs.requestDisplayPower(displayId, false)); + } + + @Test public void testEnableExternalDisplay_withDisplayManagement_shouldSignalDisplayAdded() { when(mMockFlags.isConnectedDisplayManagementEnabled()).thenReturn(true); manageDisplaysPermission(/* granted= */ true); @@ -3529,6 +3586,7 @@ public class DisplayManagerServiceTest { public void setDisplayDeviceInfo(DisplayDeviceInfo displayDeviceInfo) { mDisplayDeviceInfo = displayDeviceInfo; + mDisplayDeviceInfo.committedState = Display.STATE_ON; } @Override @@ -3558,5 +3616,14 @@ public class DisplayManagerServiceTest { public Display.Mode getUserPreferredDisplayModeLocked() { return mPreferredMode; } + + @Override + public Runnable requestDisplayStateLocked( + final int state, + final float brightnessState, + final float sdrBrightnessState, + @Nullable DisplayOffloadSessionImpl displayOffloadSession) { + return () -> mDisplayDeviceInfo.committedState = state; + } } } diff --git a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java index 28c7fb2396dd..488ce66ca99b 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/MockingOomAdjusterTests.java @@ -44,6 +44,7 @@ import static android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_SHORT_SERVI import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; +import static com.android.server.am.ActivityManagerService.FOLLOW_UP_OOMADJUSTER_UPDATE_MSG; import static com.android.server.am.ProcessList.BACKUP_APP_ADJ; import static com.android.server.am.ProcessList.CACHED_APP_MAX_ADJ; import static com.android.server.am.ProcessList.CACHED_APP_MIN_ADJ; @@ -77,6 +78,7 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyBoolean; import static org.mockito.Mockito.anyInt; +import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doCallRealMethod; import static org.mockito.Mockito.doNothing; @@ -113,11 +115,10 @@ import com.android.server.wm.ActivityTaskManagerService; import com.android.server.wm.WindowProcessController; import org.junit.After; -import org.junit.AfterClass; import org.junit.Before; -import org.junit.BeforeClass; import org.junit.Rule; import org.junit.Test; +import org.mockito.ArgumentCaptor; import java.io.File; import java.lang.reflect.Field; @@ -164,92 +165,86 @@ public class MockingOomAdjusterTests { private static int sFirstCachedAdj = ProcessList.CACHED_APP_MIN_ADJ + ProcessList.CACHED_APP_IMPORTANCE_LEVELS; - private static Context sContext; - private static PackageManagerInternal sPackageManagerInternal; - private static ActivityManagerService sService; + private Context mContext; + private PackageManagerInternal mPackageManagerInternal; + private ActivityManagerService mService; + private OomAdjusterInjector mInjector = new OomAdjusterInjector(); @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); @SuppressWarnings("GuardedBy") - @BeforeClass - public static void setUpOnce() { - sContext = getInstrumentation().getTargetContext(); + @Before + public void setUp() { + mContext = getInstrumentation().getTargetContext(); System.setProperty("dexmaker.share_classloader", "true"); - sPackageManagerInternal = mock(PackageManagerInternal.class); - doReturn(new ComponentName("", "")).when(sPackageManagerInternal) + mPackageManagerInternal = mock(PackageManagerInternal.class); + doReturn(new ComponentName("", "")).when(mPackageManagerInternal) .getSystemUiServiceComponent(); // Remove stale instance of PackageManagerInternal if there is any LocalServices.removeServiceForTest(PackageManagerInternal.class); - LocalServices.addService(PackageManagerInternal.class, sPackageManagerInternal); - - sService = mock(ActivityManagerService.class); - sService.mActivityTaskManager = new ActivityTaskManagerService(sContext); - sService.mActivityTaskManager.initialize(null, null, sContext.getMainLooper()); - sService.mPackageManagerInt = sPackageManagerInternal; - sService.mAtmInternal = spy(sService.mActivityTaskManager.getAtmInternal()); - - sService.mConstants = new ActivityManagerConstants(sContext, sService, - sContext.getMainThreadHandler()); - setFieldValue(ActivityManagerService.class, sService, "mContext", - sContext); + LocalServices.addService(PackageManagerInternal.class, mPackageManagerInternal); + + mService = mock(ActivityManagerService.class); + mService.mActivityTaskManager = new ActivityTaskManagerService(mContext); + mService.mActivityTaskManager.initialize(null, null, mContext.getMainLooper()); + mService.mPackageManagerInt = mPackageManagerInternal; + mService.mAtmInternal = spy(mService.mActivityTaskManager.getAtmInternal()); + + mService.mConstants = new ActivityManagerConstants(mContext, mService, + mContext.getMainThreadHandler()); + setFieldValue(ActivityManagerService.class, mService, "mContext", + mContext); ProcessList pr = spy(new ProcessList()); - pr.mService = sService; + pr.mService = mService; AppProfiler profiler = mock(AppProfiler.class); - setFieldValue(ActivityManagerService.class, sService, "mProcessList", + setFieldValue(ActivityManagerService.class, mService, "mProcessList", pr); - setFieldValue(ActivityManagerService.class, sService, "mHandler", + setFieldValue(ActivityManagerService.class, mService, "mHandler", mock(ActivityManagerService.MainHandler.class)); - setFieldValue(ActivityManagerService.class, sService, "mProcessStats", - new ProcessStatsService(sService, new File(sContext.getFilesDir(), "procstats"))); - setFieldValue(ActivityManagerService.class, sService, "mBackupTargets", + setFieldValue(ActivityManagerService.class, mService, "mProcessStats", + new ProcessStatsService(mService, new File(mContext.getFilesDir(), "procstats"))); + setFieldValue(ActivityManagerService.class, mService, "mBackupTargets", mock(SparseArray.class)); - setFieldValue(ActivityManagerService.class, sService, "mUserController", + setFieldValue(ActivityManagerService.class, mService, "mUserController", mock(UserController.class)); - setFieldValue(ActivityManagerService.class, sService, "mAppProfiler", profiler); - setFieldValue(ActivityManagerService.class, sService, "mProcLock", + setFieldValue(ActivityManagerService.class, mService, "mAppProfiler", profiler); + setFieldValue(ActivityManagerService.class, mService, "mProcLock", new ActivityManagerProcLock()); - setFieldValue(ActivityManagerService.class, sService, "mServices", - spy(new ActiveServices(sService))); - setFieldValue(ActivityManagerService.class, sService, "mInternal", + setFieldValue(ActivityManagerService.class, mService, "mServices", + spy(new ActiveServices(mService))); + setFieldValue(ActivityManagerService.class, mService, "mInternal", mock(ActivityManagerService.LocalService.class)); - setFieldValue(ActivityManagerService.class, sService, "mBatteryStatsService", + setFieldValue(ActivityManagerService.class, mService, "mBatteryStatsService", mock(BatteryStatsService.class)); - setFieldValue(ActivityManagerService.class, sService, "mInjector", - new ActivityManagerService.Injector(sContext)); - doReturn(mock(AppOpsManager.class)).when(sService).getAppOpsManager(); - doCallRealMethod().when(sService).enqueueOomAdjTargetLocked(any(ProcessRecord.class)); - doCallRealMethod().when(sService).updateOomAdjPendingTargetsLocked(OOM_ADJ_REASON_ACTIVITY); + setFieldValue(ActivityManagerService.class, mService, "mInjector", + new ActivityManagerService.Injector(mContext)); + doReturn(mock(AppOpsManager.class)).when(mService).getAppOpsManager(); + doCallRealMethod().when(mService).enqueueOomAdjTargetLocked(any(ProcessRecord.class)); + doCallRealMethod().when(mService).updateOomAdjPendingTargetsLocked(OOM_ADJ_REASON_ACTIVITY); setFieldValue(AppProfiler.class, profiler, "mProfilerLock", new Object()); doReturn(new ActivityManagerService.ProcessChangeItem()).when(pr) .enqueueProcessChangeItemLocked(anyInt(), anyInt()); - sService.mOomAdjuster = sService.mConstants.ENABLE_NEW_OOMADJ - ? new OomAdjusterModernImpl(sService, sService.mProcessList, - new ActiveUids(sService, false)) - : new OomAdjuster(sService, sService.mProcessList, new ActiveUids(sService, false)); - sService.mOomAdjuster.mAdjSeq = 10000; - sService.mWakefulness = new AtomicInteger(PowerManagerInternal.WAKEFULNESS_AWAKE); - if (sService.mConstants.USE_TIERED_CACHED_ADJ) { + mService.mOomAdjuster = mService.mConstants.ENABLE_NEW_OOMADJ + ? new OomAdjusterModernImpl(mService, mService.mProcessList, + new ActiveUids(mService, false), mInjector) + : new OomAdjuster(mService, mService.mProcessList, new ActiveUids(mService, false), + mInjector); + mService.mOomAdjuster.mAdjSeq = 10000; + mService.mWakefulness = new AtomicInteger(PowerManagerInternal.WAKEFULNESS_AWAKE); + if (mService.mConstants.USE_TIERED_CACHED_ADJ) { sFirstCachedAdj = ProcessList.CACHED_APP_MIN_ADJ + 10; } - } - - @Before - public void setUp() { mSetFlagsRule.enableFlags(Flags.FLAG_NEW_FGS_RESTRICTION_LOGIC); } - @AfterClass - public static void tearDownOnce() { - LocalServices.removeServiceForTest(PackageManagerInternal.class); - } - @SuppressWarnings("GuardedBy") @After public void tearDown() { - sService.mOomAdjuster.resetInternal(); - sService.mOomAdjuster.mActiveUids.clear(); + mService.mOomAdjuster.resetInternal(); + mService.mOomAdjuster.mActiveUids.clear(); + LocalServices.removeServiceForTest(PackageManagerInternal.class); } private static <T> void setFieldValue(Class clazz, Object obj, String fieldName, T val) { @@ -278,7 +273,7 @@ public class MockingOomAdjusterTests { */ @SuppressWarnings("GuardedBy") private void setProcessesToLru(ProcessRecord... apps) { - ArrayList<ProcessRecord> lru = sService.mProcessList.getLruProcessesLOSP(); + ArrayList<ProcessRecord> lru = mService.mProcessList.getLruProcessesLOSP(); lru.clear(); Collections.addAll(lru, apps); } @@ -292,20 +287,20 @@ public class MockingOomAdjusterTests { @SuppressWarnings("GuardedBy") private void updateOomAdj(ProcessRecord... apps) { if (apps.length == 0) { - updateProcessRecordNodes(sService.mProcessList.getLruProcessesLOSP()); - sService.mOomAdjuster.updateOomAdjLocked(OOM_ADJ_REASON_NONE); + updateProcessRecordNodes(mService.mProcessList.getLruProcessesLOSP()); + mService.mOomAdjuster.updateOomAdjLocked(OOM_ADJ_REASON_NONE); } else { updateProcessRecordNodes(Arrays.asList(apps)); if (apps.length == 1) { final ProcessRecord app = apps[0]; - if (!sService.mProcessList.getLruProcessesLOSP().contains(app)) { - sService.mProcessList.getLruProcessesLOSP().add(app); + if (!mService.mProcessList.getLruProcessesLOSP().contains(app)) { + mService.mProcessList.getLruProcessesLOSP().add(app); } - sService.mOomAdjuster.updateOomAdjLocked(apps[0], OOM_ADJ_REASON_NONE); + mService.mOomAdjuster.updateOomAdjLocked(apps[0], OOM_ADJ_REASON_NONE); } else { setProcessesToLru(apps); - sService.mOomAdjuster.updateOomAdjLocked(OOM_ADJ_REASON_NONE); - sService.mProcessList.getLruProcessesLOSP().clear(); + mService.mOomAdjuster.updateOomAdjLocked(OOM_ADJ_REASON_NONE); + mService.mProcessList.getLruProcessesLOSP().clear(); } } } @@ -318,10 +313,10 @@ public class MockingOomAdjusterTests { private void updateOomAdjPending(ProcessRecord... apps) { setProcessesToLru(apps); for (ProcessRecord app : apps) { - sService.mOomAdjuster.enqueueOomAdjTargetLocked(app); + mService.mOomAdjuster.enqueueOomAdjTargetLocked(app); } - sService.mOomAdjuster.updateOomAdjPendingTargetsLocked(OOM_ADJ_REASON_NONE); - sService.mProcessList.getLruProcessesLOSP().clear(); + mService.mOomAdjuster.updateOomAdjPendingTargetsLocked(OOM_ADJ_REASON_NONE); + mService.mProcessList.getLruProcessesLOSP().clear(); } /** @@ -343,9 +338,9 @@ public class MockingOomAdjusterTests { MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); app.mState.setMaxAdj(PERSISTENT_PROC_ADJ); app.mState.setHasTopUi(true); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_ASLEEP); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_ASLEEP); updateOomAdj(app); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); assertProcStates(app, PROCESS_STATE_BOUND_FOREGROUND_SERVICE, PERSISTENT_PROC_ADJ, SCHED_GROUP_RESTRICTED); @@ -359,7 +354,7 @@ public class MockingOomAdjusterTests { MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); app.mState.setMaxAdj(PERSISTENT_PROC_ADJ); app.mState.setHasTopUi(true); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); assertProcStates(app, PROCESS_STATE_PERSISTENT_UI, PERSISTENT_PROC_ADJ, @@ -372,10 +367,10 @@ public class MockingOomAdjusterTests { ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); app.mState.setMaxAdj(PERSISTENT_PROC_ADJ); - doReturn(app).when(sService).getTopApp(); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + doReturn(app).when(mService).getTopApp(); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); - doReturn(null).when(sService).getTopApp(); + doReturn(null).when(mService).getTopApp(); assertProcStates(app, PROCESS_STATE_PERSISTENT_UI, PERSISTENT_PROC_ADJ, SCHED_GROUP_TOP_APP); @@ -386,11 +381,11 @@ public class MockingOomAdjusterTests { public void testUpdateOomAdj_DoOne_TopApp_Awake() { ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); - doReturn(PROCESS_STATE_TOP).when(sService.mAtmInternal).getTopProcessState(); - doReturn(app).when(sService).getTopApp(); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + doReturn(PROCESS_STATE_TOP).when(mService.mAtmInternal).getTopProcessState(); + doReturn(app).when(mService).getTopApp(); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); - doReturn(null).when(sService).getTopApp(); + doReturn(null).when(mService).getTopApp(); assertProcStates(app, PROCESS_STATE_TOP, FOREGROUND_APP_ADJ, SCHED_GROUP_TOP_APP); } @@ -400,11 +395,11 @@ public class MockingOomAdjusterTests { public void testUpdateOomAdj_DoOne_RunningAnimations() { ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); - doReturn(PROCESS_STATE_TOP_SLEEPING).when(sService.mAtmInternal).getTopProcessState(); + doReturn(PROCESS_STATE_TOP_SLEEPING).when(mService.mAtmInternal).getTopProcessState(); app.mState.setRunningRemoteAnimation(true); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); - doReturn(PROCESS_STATE_TOP).when(sService.mAtmInternal).getTopProcessState(); + doReturn(PROCESS_STATE_TOP).when(mService.mAtmInternal).getTopProcessState(); assertProcStates(app, PROCESS_STATE_TOP_SLEEPING, VISIBLE_APP_ADJ, SCHED_GROUP_TOP_APP); } @@ -415,7 +410,7 @@ public class MockingOomAdjusterTests { ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); doReturn(mock(ActiveInstrumentation.class)).when(app).getActiveInstrumentation(); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); doCallRealMethod().when(app).getActiveInstrumentation(); @@ -429,11 +424,11 @@ public class MockingOomAdjusterTests { public void testUpdateOomAdj_DoOne_ReceivingBroadcast() { ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); - doReturn(true).when(sService).isReceivingBroadcastLocked(any(ProcessRecord.class), + doReturn(true).when(mService).isReceivingBroadcastLocked(any(ProcessRecord.class), any(int[].class)); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); - doReturn(false).when(sService).isReceivingBroadcastLocked(any(ProcessRecord.class), + doReturn(false).when(mService).isReceivingBroadcastLocked(any(ProcessRecord.class), any(int[].class)); assertProcStates(app, PROCESS_STATE_RECEIVER, FOREGROUND_APP_ADJ, SCHED_GROUP_BACKGROUND); @@ -445,7 +440,7 @@ public class MockingOomAdjusterTests { ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); app.mServices.startExecutingService(mock(ServiceRecord.class)); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); assertProcStates(app, PROCESS_STATE_SERVICE, FOREGROUND_APP_ADJ, SCHED_GROUP_BACKGROUND); @@ -456,13 +451,13 @@ public class MockingOomAdjusterTests { public void testUpdateOomAdj_DoOne_TopApp_Sleeping() { ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); - doReturn(PROCESS_STATE_TOP_SLEEPING).when(sService.mAtmInternal).getTopProcessState(); - doReturn(app).when(sService).getTopApp(); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_ASLEEP); + doReturn(PROCESS_STATE_TOP_SLEEPING).when(mService.mAtmInternal).getTopProcessState(); + doReturn(app).when(mService).getTopApp(); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_ASLEEP); updateOomAdj(app); - doReturn(null).when(sService).getTopApp(); - doReturn(PROCESS_STATE_TOP).when(sService.mAtmInternal).getTopProcessState(); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + doReturn(null).when(mService).getTopApp(); + doReturn(PROCESS_STATE_TOP).when(mService.mAtmInternal).getTopProcessState(); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); assertProcStates(app, PROCESS_STATE_TOP_SLEEPING, FOREGROUND_APP_ADJ, SCHED_GROUP_BACKGROUND); @@ -475,8 +470,8 @@ public class MockingOomAdjusterTests { MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); app.mState.setCurRawAdj(CACHED_APP_MIN_ADJ); app.mState.setCurAdj(CACHED_APP_MIN_ADJ); - doReturn(null).when(sService).getTopApp(); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + doReturn(null).when(mService).getTopApp(); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); final int expectedAdj = sFirstCachedAdj; @@ -505,7 +500,7 @@ public class MockingOomAdjusterTests { return 0; })).when(wpc).computeOomAdjFromActivities( any(WindowProcessController.ComputeOomAdjCallback.class)); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); assertProcStates(app, PROCESS_STATE_TOP, VISIBLE_APP_ADJ, SCHED_GROUP_TOP_APP); @@ -522,7 +517,7 @@ public class MockingOomAdjusterTests { WindowProcessController wpc = app.getWindowProcessController(); doReturn(true).when(wpc).hasRecentTasks(); app.mState.setLastTopTime(SystemClock.uptimeMillis()); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); doCallRealMethod().when(wpc).hasRecentTasks(); @@ -536,7 +531,7 @@ public class MockingOomAdjusterTests { MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); app.mServices.setHasForegroundServices(true, ServiceInfo.FOREGROUND_SERVICE_TYPE_LOCATION, /* hasNoneType=*/false); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, @@ -550,7 +545,7 @@ public class MockingOomAdjusterTests { ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); app.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, @@ -561,10 +556,10 @@ public class MockingOomAdjusterTests { @SuppressWarnings("GuardedBy") @Test public void testUpdateOomAdj_DoOne_FgService_ShortFgs() { - sService.mConstants.TOP_TO_FGS_GRACE_DURATION = 100_000; - sService.mConstants.mShortFgsProcStateExtraWaitDuration = 200_000; + mService.mConstants.TOP_TO_FGS_GRACE_DURATION = 100_000; + mService.mConstants.mShortFgsProcStateExtraWaitDuration = 200_000; - ServiceRecord s = ServiceRecord.newEmptyInstanceForTest(sService); + ServiceRecord s = ServiceRecord.newEmptyInstanceForTest(mService); s.appInfo = new ApplicationInfo(); s.startRequested = true; s.isForeground = true; @@ -579,7 +574,7 @@ public class MockingOomAdjusterTests { app.mServices.setHasForegroundServices(true, FOREGROUND_SERVICE_TYPE_SHORT_SERVICE, /* hasNoneType=*/false); app.mState.setLastTopTime(SystemClock.uptimeMillis()); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); @@ -596,8 +591,8 @@ public class MockingOomAdjusterTests { FOREGROUND_SERVICE_TYPE_SHORT_SERVICE, /* hasNoneType=*/false); app.mServices.startService(s); app.mState.setLastTopTime(SystemClock.uptimeMillis() - - sService.mConstants.TOP_TO_FGS_GRACE_DURATION); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + - mService.mConstants.TOP_TO_FGS_GRACE_DURATION); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); @@ -607,14 +602,14 @@ public class MockingOomAdjusterTests { } // SHORT_SERVICE, timed out already. - s = ServiceRecord.newEmptyInstanceForTest(sService); + s = ServiceRecord.newEmptyInstanceForTest(mService); s.appInfo = new ApplicationInfo(); s.startRequested = true; s.isForeground = true; s.foregroundServiceType = FOREGROUND_SERVICE_TYPE_SHORT_SERVICE; s.setShortFgsInfo(SystemClock.uptimeMillis() - - sService.mConstants.mShortFgsTimeoutDuration - - sService.mConstants.mShortFgsProcStateExtraWaitDuration); + - mService.mConstants.mShortFgsTimeoutDuration + - mService.mConstants.mShortFgsProcStateExtraWaitDuration); { ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); @@ -622,8 +617,8 @@ public class MockingOomAdjusterTests { FOREGROUND_SERVICE_TYPE_SHORT_SERVICE, /* hasNoneType=*/false); app.mServices.startService(s); app.mState.setLastTopTime(SystemClock.uptimeMillis() - - sService.mConstants.TOP_TO_FGS_GRACE_DURATION); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + - mService.mConstants.TOP_TO_FGS_GRACE_DURATION); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); @@ -639,7 +634,7 @@ public class MockingOomAdjusterTests { ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); app.mState.setHasOverlayUi(true); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); assertProcStates(app, PROCESS_STATE_IMPORTANT_FOREGROUND, PERCEPTIBLE_APP_ADJ, @@ -653,12 +648,26 @@ public class MockingOomAdjusterTests { MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); app.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); app.mState.setLastTopTime(SystemClock.uptimeMillis()); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, - PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ, SCHED_GROUP_DEFAULT); + PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ, SCHED_GROUP_DEFAULT, "fg-service-act"); assertBfsl(app); + + if (!Flags.followUpOomadjUpdates()) return; + + final ArgumentCaptor<Long> followUpTimeCaptor = ArgumentCaptor.forClass(Long.class); + verify(mService.mHandler).sendEmptyMessageAtTime( + eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG), followUpTimeCaptor.capture()); + mInjector.jumpUptimeAheadTo(followUpTimeCaptor.getValue()); + mService.mOomAdjuster.updateOomAdjFollowUpTargetsLocked(); + + assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, + SCHED_GROUP_DEFAULT, "fg-service"); + // Follow up should not have been called again. + verify(mService.mHandler).sendEmptyMessageAtTime(eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG), + followUpTimeCaptor.capture()); } @SuppressWarnings("GuardedBy") @@ -678,12 +687,24 @@ public class MockingOomAdjusterTests { s.lastTopAlmostPerceptibleBindRequestUptimeMs = nowUptime; s.getConnections().clear(); app.mServices.updateHasTopStartedAlmostPerceptibleServices(); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); assertEquals(PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ + 2, app.mState.getSetAdj()); - sService.mOomAdjuster.resetInternal(); + if (!Flags.followUpOomadjUpdates()) return; + + final ArgumentCaptor<Long> followUpTimeCaptor = ArgumentCaptor.forClass(Long.class); + verify(mService.mHandler).sendEmptyMessageAtTime( + eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG), followUpTimeCaptor.capture()); + mInjector.jumpUptimeAheadTo(followUpTimeCaptor.getValue()); + mService.mOomAdjuster.updateOomAdjFollowUpTargetsLocked(); + + assertEquals(sFirstCachedAdj, app.mState.getSetAdj()); + // Follow up should not have been called again. + verify(mService.mHandler).sendEmptyMessageAtTime(eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG), + followUpTimeCaptor.capture()); + } // Out of grace period but valid binding allows the adjustment. @@ -698,14 +719,14 @@ public class MockingOomAdjusterTests { ServiceRecord s = bindService(app, system, null, null, Context.BIND_ALMOST_PERCEPTIBLE + 2, mock(IBinder.class)); s.lastTopAlmostPerceptibleBindRequestUptimeMs = - nowUptime - 2 * sService.mConstants.mServiceBindAlmostPerceptibleTimeoutMs; + nowUptime - 2 * mService.mConstants.mServiceBindAlmostPerceptibleTimeoutMs; app.mServices.updateHasTopStartedAlmostPerceptibleServices(); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); assertEquals(PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ + 2, app.mState.getSetAdj()); - sService.mOomAdjuster.resetInternal(); + mService.mOomAdjuster.resetInternal(); } // Out of grace period and no valid binding so no adjustment. @@ -720,15 +741,15 @@ public class MockingOomAdjusterTests { ServiceRecord s = bindService(app, system, null, null, Context.BIND_ALMOST_PERCEPTIBLE, mock(IBinder.class)); s.lastTopAlmostPerceptibleBindRequestUptimeMs = - nowUptime - 2 * sService.mConstants.mServiceBindAlmostPerceptibleTimeoutMs; + nowUptime - 2 * mService.mConstants.mServiceBindAlmostPerceptibleTimeoutMs; s.getConnections().clear(); app.mServices.updateHasTopStartedAlmostPerceptibleServices(); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); assertNotEquals(PERCEPTIBLE_RECENT_FOREGROUND_APP_ADJ + 2, app.mState.getSetAdj()); - sService.mOomAdjuster.resetInternal(); + mService.mOomAdjuster.resetInternal(); } } @@ -744,7 +765,7 @@ public class MockingOomAdjusterTests { // Simulate the system starting and binding to a service in the app. ServiceRecord s = bindService(app, system, null, null, Context.BIND_ALMOST_PERCEPTIBLE, mock(IBinder.class)); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(system, app); assertProcStates(app, PROCESS_STATE_IMPORTANT_FOREGROUND, @@ -757,7 +778,7 @@ public class MockingOomAdjusterTests { ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); app.mState.setForcingToImportant(new Object()); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); assertProcStates(app, PROCESS_STATE_TRANSIENT_BACKGROUND, PERCEPTIBLE_APP_ADJ, @@ -771,7 +792,7 @@ public class MockingOomAdjusterTests { MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); WindowProcessController wpc = app.getWindowProcessController(); doReturn(true).when(wpc).isHeavyWeightProcess(); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); doReturn(false).when(wpc).isHeavyWeightProcess(); @@ -786,7 +807,7 @@ public class MockingOomAdjusterTests { MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); WindowProcessController wpc = app.getWindowProcessController(); doReturn(true).when(wpc).isHomeProcess(); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); assertProcStates(app, PROCESS_STATE_HOME, HOME_APP_ADJ, SCHED_GROUP_BACKGROUND); @@ -800,11 +821,25 @@ public class MockingOomAdjusterTests { WindowProcessController wpc = app.getWindowProcessController(); doReturn(true).when(wpc).isPreviousProcess(); doReturn(true).when(wpc).hasActivities(); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); assertProcStates(app, PROCESS_STATE_LAST_ACTIVITY, PREVIOUS_APP_ADJ, - SCHED_GROUP_BACKGROUND); + SCHED_GROUP_BACKGROUND, "previous"); + + if (!Flags.followUpOomadjUpdates()) return; + + final ArgumentCaptor<Long> followUpTimeCaptor = ArgumentCaptor.forClass(Long.class); + verify(mService.mHandler).sendEmptyMessageAtTime(eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG), + followUpTimeCaptor.capture()); + mInjector.jumpUptimeAheadTo(followUpTimeCaptor.getValue()); + mService.mOomAdjuster.updateOomAdjFollowUpTargetsLocked(); + + assertProcStates(app, PROCESS_STATE_LAST_ACTIVITY, CACHED_APP_MIN_ADJ, + SCHED_GROUP_BACKGROUND, "previous-expired"); + // Follow up should not have been called again. + verify(mService.mHandler).sendEmptyMessageAtTime(eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG), + followUpTimeCaptor.capture()); } @SuppressWarnings("GuardedBy") @@ -814,10 +849,10 @@ public class MockingOomAdjusterTests { MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); BackupRecord backupTarget = new BackupRecord(null, 0, 0, 0); backupTarget.app = app; - doReturn(backupTarget).when(sService.mBackupTargets).get(anyInt()); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + doReturn(backupTarget).when(mService.mBackupTargets).get(anyInt()); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); - doReturn(null).when(sService.mBackupTargets).get(anyInt()); + doReturn(null).when(mService.mBackupTargets).get(anyInt()); assertProcStates(app, PROCESS_STATE_TRANSIENT_BACKGROUND, BACKUP_APP_ADJ, SCHED_GROUP_BACKGROUND); @@ -829,7 +864,7 @@ public class MockingOomAdjusterTests { ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); app.mServices.setHasClientActivities(true); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); assertEquals(PROCESS_STATE_CACHED_ACTIVITY_CLIENT, app.mState.getSetProcState()); @@ -841,7 +876,7 @@ public class MockingOomAdjusterTests { ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); app.mServices.setTreatLikeActivity(true); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); assertEquals(PROCESS_STATE_CACHED_ACTIVITY, app.mState.getSetProcState()); @@ -858,7 +893,7 @@ public class MockingOomAdjusterTests { s.startRequested = true; s.lastActivity = SystemClock.uptimeMillis(); app.mServices.startService(s); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); assertProcStates(app, PROCESS_STATE_SERVICE, SERVICE_B_ADJ, SCHED_GROUP_BACKGROUND); @@ -870,7 +905,7 @@ public class MockingOomAdjusterTests { ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false)); app.mState.setMaxAdj(PERCEPTIBLE_LOW_APP_ADJ); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, PERCEPTIBLE_LOW_APP_ADJ, @@ -884,8 +919,8 @@ public class MockingOomAdjusterTests { MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false)); app.mState.setCurRawAdj(SERVICE_ADJ); app.mState.setCurAdj(SERVICE_ADJ); - doReturn(null).when(sService).getTopApp(); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + doReturn(null).when(mService).getTopApp(); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); assertTrue(ProcessList.CACHED_APP_MIN_ADJ <= app.mState.getSetAdj()); @@ -902,7 +937,7 @@ public class MockingOomAdjusterTests { s.startRequested = true; s.lastActivity = SystemClock.uptimeMillis(); app.mServices.startService(s); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); assertProcStates(app, PROCESS_STATE_SERVICE, SERVICE_ADJ, SCHED_GROUP_BACKGROUND); @@ -918,11 +953,11 @@ public class MockingOomAdjusterTests { ServiceRecord s = bindService(app, client, null, null, Context.BIND_WAIVE_PRIORITY, mock(IBinder.class)); s.startRequested = true; - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); - doReturn(PROCESS_STATE_TOP).when(sService.mAtmInternal).getTopProcessState(); - doReturn(client).when(sService).getTopApp(); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + doReturn(PROCESS_STATE_TOP).when(mService.mAtmInternal).getTopProcessState(); + doReturn(client).when(mService).getTopApp(); updateOomAdj(client, app); - doReturn(null).when(sService).getTopApp(); + doReturn(null).when(mService).getTopApp(); assertProcStates(app, PROCESS_STATE_SERVICE, sFirstCachedAdj, SCHED_GROUP_BACKGROUND); } @@ -937,7 +972,7 @@ public class MockingOomAdjusterTests { client.mServices.setTreatLikeActivity(true); bindService(app, client, null, null, Context.BIND_WAIVE_PRIORITY | Context.BIND_TREAT_LIKE_ACTIVITY, mock(IBinder.class)); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, app); assertEquals(PROCESS_STATE_CACHED_ACTIVITY, app.mState.getSetProcState()); @@ -956,9 +991,9 @@ public class MockingOomAdjusterTests { ConnectionRecord cr = s.getConnections().get(binder).get(0); setFieldValue(ConnectionRecord.class, cr, "activity", mock(ActivityServiceConnectionsHolder.class)); - doReturn(client).when(sService).getTopApp(); + doReturn(client).when(mService).getTopApp(); doReturn(true).when(cr.activity).isActivityVisible(); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, app); assertEquals(FOREGROUND_APP_ADJ, app.mState.getSetAdj()); @@ -971,7 +1006,7 @@ public class MockingOomAdjusterTests { ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false)); bindService(app, app, null, null, 0, mock(IBinder.class)); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, sFirstCachedAdj, SCHED_GROUP_BACKGROUND); @@ -986,7 +1021,7 @@ public class MockingOomAdjusterTests { MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); client.mServices.setTreatLikeActivity(true); bindService(app, client, null, null, 0, mock(IBinder.class)); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, app); assertEquals(PROCESS_STATE_CACHED_EMPTY, app.mState.getSetProcState()); @@ -1005,11 +1040,11 @@ public class MockingOomAdjusterTests { MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); bindService(app, client, null, null, Context.BIND_ALLOW_OOM_MANAGEMENT, mock(IBinder.class)); - doReturn(PROCESS_STATE_TOP).when(sService.mAtmInternal).getTopProcessState(); - doReturn(client).when(sService).getTopApp(); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + doReturn(PROCESS_STATE_TOP).when(mService.mAtmInternal).getTopProcessState(); + doReturn(client).when(mService).getTopApp(); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, app); - doReturn(null).when(sService).getTopApp(); + doReturn(null).when(mService).getTopApp(); assertEquals(PREVIOUS_APP_ADJ, app.mState.getSetAdj()); } @@ -1024,7 +1059,7 @@ public class MockingOomAdjusterTests { bindService(app, client, null, null, Context.BIND_FOREGROUND_SERVICE, mock(IBinder.class)); client.mState.setMaxAdj(PERSISTENT_PROC_ADJ); client.mState.setHasTopUi(true); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, app); assertProcStates(app, PROCESS_STATE_BOUND_FOREGROUND_SERVICE, VISIBLE_APP_ADJ, @@ -1041,7 +1076,7 @@ public class MockingOomAdjusterTests { MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); bindService(app, client, null, null, Context.BIND_IMPORTANT, mock(IBinder.class)); client.mServices.startExecutingService(mock(ServiceRecord.class)); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, app); assertEquals(FOREGROUND_APP_ADJ, app.mState.getSetAdj()); @@ -1056,11 +1091,11 @@ public class MockingOomAdjusterTests { ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID, MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); bindService(app, client, null, null, 0, mock(IBinder.class)); - doReturn(PROCESS_STATE_TOP).when(sService.mAtmInternal).getTopProcessState(); - doReturn(client).when(sService).getTopApp(); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + doReturn(PROCESS_STATE_TOP).when(mService.mAtmInternal).getTopProcessState(); + doReturn(client).when(mService).getTopApp(); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, app); - doReturn(null).when(sService).getTopApp(); + doReturn(null).when(mService).getTopApp(); assertProcStates(app, PROCESS_STATE_BOUND_TOP, VISIBLE_APP_ADJ, SCHED_GROUP_DEFAULT); } @@ -1074,7 +1109,7 @@ public class MockingOomAdjusterTests { MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); bindService(app, client, null, null, Context.BIND_FOREGROUND_SERVICE, mock(IBinder.class)); client.mState.setMaxAdj(PERSISTENT_PROC_ADJ); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, app); assertEquals(PROCESS_STATE_BOUND_FOREGROUND_SERVICE, app.mState.getSetProcState()); @@ -1092,7 +1127,7 @@ public class MockingOomAdjusterTests { MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); bindService(app, client, null, null, Context.BIND_NOT_FOREGROUND, mock(IBinder.class)); client.mState.setMaxAdj(PERSISTENT_PROC_ADJ); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, app); assertEquals(PROCESS_STATE_TRANSIENT_BACKGROUND, app.mState.getSetProcState()); @@ -1108,7 +1143,7 @@ public class MockingOomAdjusterTests { MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); bindService(app, client, null, null, 0, mock(IBinder.class)); client.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, app); assertEquals(PROCESS_STATE_FOREGROUND_SERVICE, client.mState.getSetProcState()); @@ -1128,7 +1163,7 @@ public class MockingOomAdjusterTests { bindService(app, client, null, null, 0, mock(IBinder.class)); // In order to trick OomAdjuster to think it has a short-service, we need this logic. - ServiceRecord s = ServiceRecord.newEmptyInstanceForTest(sService); + ServiceRecord s = ServiceRecord.newEmptyInstanceForTest(mService); s.appInfo = new ApplicationInfo(); s.startRequested = true; s.isForeground = true; @@ -1139,7 +1174,7 @@ public class MockingOomAdjusterTests { client.mServices.setHasForegroundServices(true, FOREGROUND_SERVICE_TYPE_SHORT_SERVICE, /* hasNoneType=*/false); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, app); // Client only has a SHORT_FGS, so it doesn't have BFSL, and that's propagated. @@ -1159,7 +1194,7 @@ public class MockingOomAdjusterTests { MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false)); // In order to trick OomAdjuster to think it has a short-service, we need this logic. - ServiceRecord s = ServiceRecord.newEmptyInstanceForTest(sService); + ServiceRecord s = ServiceRecord.newEmptyInstanceForTest(mService); s.appInfo = new ApplicationInfo(); s.startRequested = true; s.isForeground = true; @@ -1170,7 +1205,7 @@ public class MockingOomAdjusterTests { app2.mServices.setHasForegroundServices(true, FOREGROUND_SERVICE_TYPE_SHORT_SERVICE, /* hasNoneType=*/false); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app2); // Client only has a SHORT_FGS, so it doesn't have BFSL, and that's propagated. @@ -1211,11 +1246,11 @@ public class MockingOomAdjusterTests { bindService(app, client, null, null, Context.BIND_ABOVE_CLIENT, mock(IBinder.class)); BackupRecord backupTarget = new BackupRecord(null, 0, 0, 0); backupTarget.app = client; - doReturn(backupTarget).when(sService.mBackupTargets).get(anyInt()); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + doReturn(backupTarget).when(mService.mBackupTargets).get(anyInt()); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, app); - doReturn(null).when(sService.mBackupTargets).get(anyInt()); + doReturn(null).when(mService.mBackupTargets).get(anyInt()); assertEquals(BACKUP_APP_ADJ, app.mState.getSetAdj()); assertNoBfsl(app); @@ -1236,7 +1271,7 @@ public class MockingOomAdjusterTests { MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); bindService(app, client, null, null, Context.BIND_NOT_PERCEPTIBLE, mock(IBinder.class)); client.mState.setRunningRemoteAnimation(true); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, app); assertEquals(PERCEPTIBLE_LOW_APP_ADJ, app.mState.getSetAdj()); @@ -1251,7 +1286,7 @@ public class MockingOomAdjusterTests { MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); bindService(app, client, null, null, Context.BIND_NOT_VISIBLE, mock(IBinder.class)); client.mState.setRunningRemoteAnimation(true); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, app); assertEquals(PERCEPTIBLE_APP_ADJ, app.mState.getSetAdj()); @@ -1266,7 +1301,7 @@ public class MockingOomAdjusterTests { MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); bindService(app, client, null, null, 0, mock(IBinder.class)); client.mState.setHasOverlayUi(true); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, app); assertEquals(PERCEPTIBLE_APP_ADJ, app.mState.getSetAdj()); @@ -1284,12 +1319,12 @@ public class MockingOomAdjusterTests { Context.BIND_ALMOST_PERCEPTIBLE | Context.BIND_NOT_FOREGROUND, mock(IBinder.class)); client.mState.setMaxAdj(PERSISTENT_PROC_ADJ); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, app); assertEquals(PERCEPTIBLE_MEDIUM_APP_ADJ + 2, app.mState.getSetAdj()); - sService.mOomAdjuster.resetInternal(); + mService.mOomAdjuster.resetInternal(); } { @@ -1303,13 +1338,13 @@ public class MockingOomAdjusterTests { Context.BIND_ALMOST_PERCEPTIBLE | Context.BIND_NOT_FOREGROUND, mock(IBinder.class)); client.mState.setMaxAdj(PERSISTENT_PROC_ADJ); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, app); doReturn(false).when(wpc).isHeavyWeightProcess(); assertEquals(PERCEPTIBLE_MEDIUM_APP_ADJ + 2, app.mState.getSetAdj()); - sService.mOomAdjuster.resetInternal(); + mService.mOomAdjuster.resetInternal(); } { @@ -1321,12 +1356,12 @@ public class MockingOomAdjusterTests { Context.BIND_ALMOST_PERCEPTIBLE, mock(IBinder.class)); client.mState.setMaxAdj(PERSISTENT_PROC_ADJ); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, app); assertEquals(PERCEPTIBLE_APP_ADJ + 1, app.mState.getSetAdj()); - sService.mOomAdjuster.resetInternal(); + mService.mOomAdjuster.resetInternal(); } { @@ -1340,13 +1375,13 @@ public class MockingOomAdjusterTests { Context.BIND_ALMOST_PERCEPTIBLE, mock(IBinder.class)); client.mState.setMaxAdj(PERSISTENT_PROC_ADJ); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, app); doReturn(false).when(wpc).isHeavyWeightProcess(); assertEquals(PERCEPTIBLE_APP_ADJ + 1, app.mState.getSetAdj()); - sService.mOomAdjuster.resetInternal(); + mService.mOomAdjuster.resetInternal(); } } @@ -1359,7 +1394,7 @@ public class MockingOomAdjusterTests { MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); bindService(app, client, null, null, 0, mock(IBinder.class)); client.mState.setRunningRemoteAnimation(true); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, app); assertEquals(VISIBLE_APP_ADJ, app.mState.getSetAdj()); @@ -1375,7 +1410,7 @@ public class MockingOomAdjusterTests { bindService(app, client, null, null, Context.BIND_IMPORTANT_BACKGROUND, mock(IBinder.class)); client.mState.setHasOverlayUi(true); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, app); assertEquals(PROCESS_STATE_IMPORTANT_BACKGROUND, app.mState.getSetProcState()); @@ -1402,7 +1437,7 @@ public class MockingOomAdjusterTests { MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); bindProvider(app, client, null, null, false); client.mServices.setTreatLikeActivity(true); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app, client); assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, sFirstCachedAdj, SCHED_GROUP_BACKGROUND); @@ -1416,11 +1451,11 @@ public class MockingOomAdjusterTests { ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID, MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); bindProvider(app, client, null, null, false); - doReturn(PROCESS_STATE_TOP).when(sService.mAtmInternal).getTopProcessState(); - doReturn(client).when(sService).getTopApp(); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + doReturn(PROCESS_STATE_TOP).when(mService.mAtmInternal).getTopProcessState(); + doReturn(client).when(mService).getTopApp(); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, app); - doReturn(null).when(sService).getTopApp(); + doReturn(null).when(mService).getTopApp(); assertProcStates(app, PROCESS_STATE_BOUND_TOP, FOREGROUND_APP_ADJ, SCHED_GROUP_DEFAULT); } @@ -1434,7 +1469,7 @@ public class MockingOomAdjusterTests { MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); client.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); bindProvider(app, client, null, null, false); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, app); assertProcStates(app, PROCESS_STATE_BOUND_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, @@ -1452,7 +1487,7 @@ public class MockingOomAdjusterTests { MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); // In order to trick OomAdjuster to think it has a short-service, we need this logic. - ServiceRecord s = ServiceRecord.newEmptyInstanceForTest(sService); + ServiceRecord s = ServiceRecord.newEmptyInstanceForTest(mService); s.appInfo = new ApplicationInfo(); s.startRequested = true; s.isForeground = true; @@ -1464,7 +1499,7 @@ public class MockingOomAdjusterTests { client.mServices.setHasForegroundServices(true, FOREGROUND_SERVICE_TYPE_SHORT_SERVICE, /* hasNoneType=*/false); bindProvider(app, client, null, null, false); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, app); // Client only has a SHORT_FGS, so it doesn't have BFSL, and that's propagated. @@ -1486,7 +1521,7 @@ public class MockingOomAdjusterTests { ProcessRecord client = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID, MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); bindProvider(app, client, null, null, true); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, app); assertProcStates(app, PROCESS_STATE_IMPORTANT_FOREGROUND, FOREGROUND_APP_ADJ, @@ -1499,11 +1534,25 @@ public class MockingOomAdjusterTests { ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false)); app.mProviders.setLastProviderTime(SystemClock.uptimeMillis()); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); assertProcStates(app, PROCESS_STATE_LAST_ACTIVITY, PREVIOUS_APP_ADJ, - SCHED_GROUP_BACKGROUND); + SCHED_GROUP_BACKGROUND, "recent-provider"); + + if (!Flags.followUpOomadjUpdates()) return; + + final ArgumentCaptor<Long> followUpTimeCaptor = ArgumentCaptor.forClass(Long.class); + verify(mService.mHandler).sendEmptyMessageAtTime(eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG), + followUpTimeCaptor.capture()); + mInjector.jumpUptimeAheadTo(followUpTimeCaptor.getValue()); + mService.mOomAdjuster.updateOomAdjFollowUpTargetsLocked(); + + assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, sFirstCachedAdj, SCHED_GROUP_BACKGROUND, + "cch-empty"); + // Follow up should not have been called again. + verify(mService.mHandler).sendEmptyMessageAtTime(eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG), + followUpTimeCaptor.capture()); } @SuppressWarnings("GuardedBy") @@ -1517,11 +1566,11 @@ public class MockingOomAdjusterTests { ProcessRecord client2 = spy(makeDefaultProcessRecord(MOCKAPP3_PID, MOCKAPP3_UID, MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false)); bindService(client, client2, null, null, 0, mock(IBinder.class)); - doReturn(PROCESS_STATE_TOP).when(sService.mAtmInternal).getTopProcessState(); - doReturn(client2).when(sService).getTopApp(); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + doReturn(PROCESS_STATE_TOP).when(mService.mAtmInternal).getTopProcessState(); + doReturn(client2).when(mService).getTopApp(); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, client2, app); - doReturn(null).when(sService).getTopApp(); + doReturn(null).when(mService).getTopApp(); assertProcStates(app, PROCESS_STATE_BOUND_TOP, VISIBLE_APP_ADJ, SCHED_GROUP_DEFAULT); @@ -1539,7 +1588,7 @@ public class MockingOomAdjusterTests { MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false)); bindService(app, client2, null, null, 0, mock(IBinder.class)); client2.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, client2, app); assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, @@ -1559,7 +1608,7 @@ public class MockingOomAdjusterTests { MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false)); bindService(client, client2, null, null, 0, mock(IBinder.class)); client2.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, client2, app); assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, @@ -1584,7 +1633,7 @@ public class MockingOomAdjusterTests { // Note: We add processes to LRU but still call updateOomAdjLocked() with a specific // processes. setProcessesToLru(app, client, client2); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, @@ -1598,7 +1647,7 @@ public class MockingOomAdjusterTests { assertBfsl(client2); client2.mServices.setHasForegroundServices(false, 0, /* hasNoneType=*/false); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client2); assertEquals(PROCESS_STATE_CACHED_EMPTY, client2.mState.getSetProcState()); @@ -1622,7 +1671,7 @@ public class MockingOomAdjusterTests { MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false)); bindService(client2, client, null, null, 0, mock(IBinder.class)); client.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app, client, client2); assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, @@ -1649,7 +1698,7 @@ public class MockingOomAdjusterTests { bindService(client, client2, null, null, 0, mock(IBinder.class)); bindService(client2, client, null, null, 0, mock(IBinder.class)); client.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app, client, client2); assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, @@ -1683,7 +1732,7 @@ public class MockingOomAdjusterTests { bindService(client3, client4, null, null, 0, mock(IBinder.class)); bindService(client4, client3, null, null, 0, mock(IBinder.class)); client.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app, client, client2, client3, client4); assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, @@ -1720,7 +1769,7 @@ public class MockingOomAdjusterTests { MOCKAPP4_PROCESSNAME, MOCKAPP4_PACKAGENAME, false)); client3.mState.setForcingToImportant(new Object()); bindService(app, client3, null, null, 0, mock(IBinder.class)); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app, client, client2, client3); assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, @@ -1746,7 +1795,7 @@ public class MockingOomAdjusterTests { MOCKAPP4_PROCESSNAME, MOCKAPP4_PACKAGENAME, false)); client3.mState.setForcingToImportant(new Object()); bindService(app, client3, null, null, 0, mock(IBinder.class)); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app, client, client2, client3); assertProcStates(app, PROCESS_STATE_TRANSIENT_BACKGROUND, PERCEPTIBLE_APP_ADJ, @@ -1773,7 +1822,7 @@ public class MockingOomAdjusterTests { MOCKAPP5_PROCESSNAME, MOCKAPP5_PACKAGENAME, false)); client4.mState.setForcingToImportant(new Object()); bindService(app, client4, null, null, 0, mock(IBinder.class)); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app, client, client2, client3, client4); assertProcStates(app, PROCESS_STATE_TRANSIENT_BACKGROUND, PERCEPTIBLE_APP_ADJ, @@ -1802,7 +1851,7 @@ public class MockingOomAdjusterTests { MOCKAPP5_PROCESSNAME, MOCKAPP5_PACKAGENAME, false)); client4.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); bindService(app, client4, null, null, 0, mock(IBinder.class)); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app, client, client2, client3, client4); assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, @@ -1828,7 +1877,7 @@ public class MockingOomAdjusterTests { MOCKAPP4_PROCESSNAME, MOCKAPP4_PACKAGENAME, false)); client3.mState.setForcingToImportant(new Object()); bindService(app, client3, null, null, 0, mock(IBinder.class)); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, client2, client3, app); assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, @@ -1848,7 +1897,7 @@ public class MockingOomAdjusterTests { MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false)); bindProvider(client, client2, null, null, false); client2.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, client2, app); assertProcStates(app, PROCESS_STATE_BOUND_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, @@ -1869,7 +1918,7 @@ public class MockingOomAdjusterTests { bindProvider(client, client2, null, null, false); client2.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); bindService(client2, app, null, null, 0, mock(IBinder.class)); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app, client, client2); assertProcStates(app, PROCESS_STATE_BOUND_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, @@ -1889,7 +1938,7 @@ public class MockingOomAdjusterTests { MOCKAPP3_PROCESSNAME, MOCKAPP3_PACKAGENAME, false)); bindProvider(client, client2, null, null, false); client2.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client, client2, app); assertProcStates(app, PROCESS_STATE_BOUND_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, @@ -1910,7 +1959,7 @@ public class MockingOomAdjusterTests { bindProvider(client, client2, null, null, false); client2.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); bindProvider(client2, app, null, null, false); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app, client, client2); assertProcStates(app, PROCESS_STATE_BOUND_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, @@ -1936,7 +1985,7 @@ public class MockingOomAdjusterTests { client1.mState.setMaxAdj(PERSISTENT_PROC_ADJ); client2.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client1, client2, app1, app2); assertProcStates(app1, PROCESS_STATE_BOUND_FOREGROUND_SERVICE, VISIBLE_APP_ADJ, @@ -1957,7 +2006,7 @@ public class MockingOomAdjusterTests { assertProcStates(app2, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, SCHED_GROUP_DEFAULT); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_ASLEEP); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_ASLEEP); updateOomAdj(client1, client2, app1, app2); assertProcStates(app1, PROCESS_STATE_IMPORTANT_FOREGROUND, VISIBLE_APP_ADJ, SCHED_GROUP_TOP_APP); @@ -2028,9 +2077,9 @@ public class MockingOomAdjusterTests { SCHED_GROUP_DEFAULT); client2.mState.setHasOverlayUi(false); - doReturn(PROCESS_STATE_TOP).when(sService.mAtmInternal).getTopProcessState(); - doReturn(client2).when(sService).getTopApp(); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + doReturn(PROCESS_STATE_TOP).when(mService.mAtmInternal).getTopProcessState(); + doReturn(client2).when(mService).getTopApp(); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(client2, app2); assertProcStates(app2, PROCESS_STATE_BOUND_TOP, VISIBLE_APP_ADJ, @@ -2047,7 +2096,7 @@ public class MockingOomAdjusterTests { client1.mState.setMaxAdj(PERSISTENT_PROC_ADJ); app1.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); bindService(app1, client1, null, null, Context.BIND_NOT_PERCEPTIBLE, mock(IBinder.class)); @@ -2068,7 +2117,7 @@ public class MockingOomAdjusterTests { client1.mState.setMaxAdj(PERSISTENT_PROC_ADJ); app1.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); bindService(app1, client1, null, null, Context.BIND_ALMOST_PERCEPTIBLE, mock(IBinder.class)); @@ -2088,7 +2137,7 @@ public class MockingOomAdjusterTests { app.setPendingFinishAttach(true); app.mState.setHasForegroundActivities(false); - sService.mOomAdjuster.setAttachingProcessStatesLSP(app); + mService.mOomAdjuster.setAttachingProcessStatesLSP(app); updateOomAdj(app); assertProcStates(app, PROCESS_STATE_CACHED_EMPTY, FOREGROUND_APP_ADJ, @@ -2102,9 +2151,9 @@ public class MockingOomAdjusterTests { MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false)); app.setPendingFinishAttach(true); app.mState.setHasForegroundActivities(true); - doReturn(app).when(sService).getTopApp(); + doReturn(app).when(mService).getTopApp(); - sService.mOomAdjuster.setAttachingProcessStatesLSP(app); + mService.mOomAdjuster.setAttachingProcessStatesLSP(app); updateOomAdj(app); assertProcStates(app, PROCESS_STATE_TOP, FOREGROUND_APP_ADJ, @@ -2124,10 +2173,10 @@ public class MockingOomAdjusterTests { MOCKAPP4_PROCESSNAME, MOCKAPP3_PACKAGENAME, false)); final ProcessRecord app3 = spy(makeDefaultProcessRecord(MOCKAPP5_PID, MOCKAPP5_UID, MOCKAPP5_PROCESSNAME, MOCKAPP5_PACKAGENAME, false)); - final UidRecord app1UidRecord = new UidRecord(MOCKAPP_UID, sService); - final UidRecord app2UidRecord = new UidRecord(MOCKAPP2_UID, sService); - final UidRecord app3UidRecord = new UidRecord(MOCKAPP5_UID, sService); - final UidRecord clientUidRecord = new UidRecord(MOCKAPP3_UID, sService); + final UidRecord app1UidRecord = new UidRecord(MOCKAPP_UID, mService); + final UidRecord app2UidRecord = new UidRecord(MOCKAPP2_UID, mService); + final UidRecord app3UidRecord = new UidRecord(MOCKAPP5_UID, mService); + final UidRecord clientUidRecord = new UidRecord(MOCKAPP3_UID, mService); app1.setUidRecord(app1UidRecord); app2.setUidRecord(app2UidRecord); app3.setUidRecord(app3UidRecord); @@ -2137,7 +2186,7 @@ public class MockingOomAdjusterTests { client1.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); client2.mState.setForcingToImportant(new Object()); setProcessesToLru(app1, app2, app3, client1, client2); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); final ComponentName cn1 = ComponentName.unflattenFromString( MOCKAPP_PACKAGENAME + "/.TestService"); @@ -2164,10 +2213,10 @@ public class MockingOomAdjusterTests { c2s.startRequested = true; try { - sService.mOomAdjuster.mActiveUids.put(MOCKAPP_UID, app1UidRecord); - sService.mOomAdjuster.mActiveUids.put(MOCKAPP2_UID, app2UidRecord); - sService.mOomAdjuster.mActiveUids.put(MOCKAPP5_UID, app3UidRecord); - sService.mOomAdjuster.mActiveUids.put(MOCKAPP3_UID, clientUidRecord); + mService.mOomAdjuster.mActiveUids.put(MOCKAPP_UID, app1UidRecord); + mService.mOomAdjuster.mActiveUids.put(MOCKAPP2_UID, app2UidRecord); + mService.mOomAdjuster.mActiveUids.put(MOCKAPP5_UID, app3UidRecord); + mService.mOomAdjuster.mActiveUids.put(MOCKAPP3_UID, clientUidRecord); setServiceMap(s1, MOCKAPP_UID, cn1); setServiceMap(s2, MOCKAPP2_UID, cn2); @@ -2195,10 +2244,10 @@ public class MockingOomAdjusterTests { app2UidRecord.setIdle(true); app3UidRecord.setIdle(true); clientUidRecord.setIdle(true); - doReturn(ActivityManager.APP_START_MODE_DELAYED).when(sService) + doReturn(ActivityManager.APP_START_MODE_DELAYED).when(mService) .getAppStartModeLOSP(anyInt(), any(String.class), anyInt(), anyInt(), anyBoolean(), anyBoolean(), anyBoolean()); - doNothing().when(sService.mServices) + doNothing().when(mService.mServices) .scheduleServiceTimeoutLocked(any(ProcessRecord.class)); updateOomAdj(client1, client2, app1, app2, app3); @@ -2206,11 +2255,11 @@ public class MockingOomAdjusterTests { assertEquals(PROCESS_STATE_SERVICE, app1.mState.getSetProcState()); assertEquals(PROCESS_STATE_SERVICE, client2.mState.getSetProcState()); } finally { - doCallRealMethod().when(sService) + doCallRealMethod().when(mService) .getAppStartModeLOSP(anyInt(), any(String.class), anyInt(), anyInt(), anyBoolean(), anyBoolean(), anyBoolean()); - sService.mServices.mServiceMap.clear(); - sService.mOomAdjuster.mActiveUids.clear(); + mService.mServices.mServiceMap.clear(); + mService.mOomAdjuster.mActiveUids.clear(); } } @@ -2224,7 +2273,7 @@ public class MockingOomAdjusterTests { MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); app2.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app, app2); assertProcStates(app, PROCESS_STATE_TRANSIENT_BACKGROUND, PERCEPTIBLE_APP_ADJ, @@ -2244,7 +2293,7 @@ public class MockingOomAdjusterTests { MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); app2.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); bindService(app, app2, null, null, 0, mock(IBinder.class)); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app, app2); assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, @@ -2268,7 +2317,7 @@ public class MockingOomAdjusterTests { bindService(app2, app3, null, null, 0, mock(IBinder.class)); app3.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); bindService(app3, app, null, null, 0, mock(IBinder.class)); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app, app2, app3); assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, @@ -2313,7 +2362,7 @@ public class MockingOomAdjusterTests { MOCKAPP5_PROCESSNAME, MOCKAPP5_PACKAGENAME, false)); app5.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); bindService(app, app5, null, s, 0, mock(IBinder.class)); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app, app2, app3, app4, app5); assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, @@ -2355,7 +2404,7 @@ public class MockingOomAdjusterTests { MOCKAPP5_PROCESSNAME, MOCKAPP5_PACKAGENAME, false)); app5.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); bindService(app, app5, null, s, 0, mock(IBinder.class)); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app5, app4, app3, app2, app); assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, @@ -2397,7 +2446,7 @@ public class MockingOomAdjusterTests { MOCKAPP5_PROCESSNAME, MOCKAPP5_PACKAGENAME, false)); app5.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); bindService(app, app5, null, s, 0, mock(IBinder.class)); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app3, app4, app2, app, app5); assertProcStates(app, PROCESS_STATE_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, @@ -2437,7 +2486,7 @@ public class MockingOomAdjusterTests { client3.mState.setMaxAdj(PERSISTENT_PROC_ADJ); bindService(app, client3, null, null, Context.BIND_INCLUDE_CAPABILITIES, mock(IBinder.class)); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app, client, client2, client3); final int expected = PROCESS_CAPABILITY_ALL & ~PROCESS_CAPABILITY_BFSL; @@ -2468,7 +2517,7 @@ public class MockingOomAdjusterTests { MOCKAPP5_PROCESSNAME, MOCKAPP5_PACKAGENAME, false)); app5.mServices.setHasForegroundServices(true, 0, /* hasNoneType=*/true); bindProvider(app, app5, cr, null, false); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app, app2, app3, app4, app5); assertProcStates(app, PROCESS_STATE_BOUND_FOREGROUND_SERVICE, PERCEPTIBLE_APP_ADJ, @@ -2512,8 +2561,8 @@ public class MockingOomAdjusterTests { doCallRealMethod().when(s).getConnections(); s.startRequested = true; s.lastActivity = now; - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); - sService.mOomAdjuster.mNumServiceProcs = 3; + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mOomAdjuster.mNumServiceProcs = 3; updateOomAdj(app3, app2, app); assertEquals(SERVICE_B_ADJ, app3.mState.getSetAdj()); @@ -2530,15 +2579,15 @@ public class MockingOomAdjusterTests { MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); final int userOwner = 0; final int userOther = 1; - final int cachedAdj1 = sService.mConstants.USE_TIERED_CACHED_ADJ + final int cachedAdj1 = mService.mConstants.USE_TIERED_CACHED_ADJ ? CACHED_APP_MIN_ADJ + 10 : CACHED_APP_MIN_ADJ + ProcessList.CACHED_APP_IMPORTANCE_LEVELS; - final int cachedAdj2 = sService.mConstants.USE_TIERED_CACHED_ADJ + final int cachedAdj2 = mService.mConstants.USE_TIERED_CACHED_ADJ ? CACHED_APP_MIN_ADJ + 10 : cachedAdj1 + ProcessList.CACHED_APP_IMPORTANCE_LEVELS * 2; - doReturn(userOwner).when(sService.mUserController).getCurrentUserId(); + doReturn(userOwner).when(mService.mUserController).getCurrentUserId(); - final ArrayList<ProcessRecord> lru = sService.mProcessList.getLruProcessesLOSP(); + final ArrayList<ProcessRecord> lru = mService.mProcessList.getLruProcessesLOSP(); lru.clear(); lru.add(app2); lru.add(app); @@ -2549,10 +2598,10 @@ public class MockingOomAdjusterTests { MOCKAPP2_PACKAGENAME + "/.TestService"); final long now = SystemClock.uptimeMillis(); - sService.mConstants.KEEP_WARMING_SERVICES.clear(); + mService.mConstants.KEEP_WARMING_SERVICES.clear(); final ServiceInfo si = mock(ServiceInfo.class); si.applicationInfo = mock(ApplicationInfo.class); - ServiceRecord s = spy(new ServiceRecord(sService, cn, cn, null, 0, null, + ServiceRecord s = spy(new ServiceRecord(mService, cn, cn, null, 0, null, si, false, null)); doReturn(new ArrayMap<IBinder, ArrayList<ConnectionRecord>>()).when(s).getConnections(); s.startRequested = true; @@ -2564,16 +2613,16 @@ public class MockingOomAdjusterTests { final ServiceInfo si2 = mock(ServiceInfo.class); si2.applicationInfo = mock(ApplicationInfo.class); si2.applicationInfo.uid = MOCKAPP2_UID_OTHER; - ServiceRecord s2 = spy(new ServiceRecord(sService, cn2, cn2, null, 0, null, + ServiceRecord s2 = spy(new ServiceRecord(mService, cn2, cn2, null, 0, null, si2, false, null)); doReturn(new ArrayMap<IBinder, ArrayList<ConnectionRecord>>()).when(s2).getConnections(); s2.startRequested = true; - s2.lastActivity = now - sService.mConstants.MAX_SERVICE_INACTIVITY - 1; + s2.lastActivity = now - mService.mConstants.MAX_SERVICE_INACTIVITY - 1; app2.mServices.startService(s2); app2.mState.setHasShownUi(false); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(); assertProcStates(app, true, PROCESS_STATE_SERVICE, cachedAdj1, "cch-started-ui-services"); @@ -2590,7 +2639,7 @@ public class MockingOomAdjusterTests { app.mState.setSetProcState(PROCESS_STATE_NONEXISTENT); app.mState.setAdjType(null); app.mState.setSetAdj(UNKNOWN_ADJ); - s.lastActivity = now - sService.mConstants.MAX_SERVICE_INACTIVITY - 1; + s.lastActivity = now - mService.mConstants.MAX_SERVICE_INACTIVITY - 1; updateOomAdj(); assertProcStates(app, true, PROCESS_STATE_SERVICE, cachedAdj1, "cch-started-services"); @@ -2600,9 +2649,9 @@ public class MockingOomAdjusterTests { app.mState.setAdjType(null); app.mState.setSetAdj(UNKNOWN_ADJ); app.mState.setHasShownUi(true); - sService.mConstants.KEEP_WARMING_SERVICES.add(cn); - sService.mConstants.KEEP_WARMING_SERVICES.add(cn2); - s = spy(new ServiceRecord(sService, cn, cn, null, 0, null, + mService.mConstants.KEEP_WARMING_SERVICES.add(cn); + mService.mConstants.KEEP_WARMING_SERVICES.add(cn2); + s = spy(new ServiceRecord(mService, cn, cn, null, 0, null, si, false, null)); doReturn(new ArrayMap<IBinder, ArrayList<ConnectionRecord>>()).when(s).getConnections(); s.startRequested = true; @@ -2618,14 +2667,14 @@ public class MockingOomAdjusterTests { app.mState.setAdjType(null); app.mState.setSetAdj(UNKNOWN_ADJ); app.mState.setHasShownUi(false); - s.lastActivity = now - sService.mConstants.MAX_SERVICE_INACTIVITY - 1; + s.lastActivity = now - mService.mConstants.MAX_SERVICE_INACTIVITY - 1; updateOomAdj(); assertProcStates(app, false, PROCESS_STATE_SERVICE, SERVICE_ADJ, "started-services"); assertProcStates(app2, true, PROCESS_STATE_SERVICE, cachedAdj1, "cch-started-services"); - doReturn(userOther).when(sService.mUserController).getCurrentUserId(); - sService.mOomAdjuster.handleUserSwitchedLocked(); + doReturn(userOther).when(mService.mUserController).getCurrentUserId(); + mService.mOomAdjuster.handleUserSwitchedLocked(); updateOomAdj(); assertProcStates(app, true, PROCESS_STATE_SERVICE, cachedAdj1, "cch-started-services"); @@ -2637,9 +2686,9 @@ public class MockingOomAdjusterTests { public void testUpdateOomAdj_DoOne_AboveClient_SameProcess() { ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); - doReturn(PROCESS_STATE_TOP).when(sService.mAtmInternal).getTopProcessState(); - doReturn(app).when(sService).getTopApp(); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + doReturn(PROCESS_STATE_TOP).when(mService.mAtmInternal).getTopProcessState(); + doReturn(app).when(mService).getTopApp(); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); assertEquals(FOREGROUND_APP_ADJ, app.mState.getSetAdj()); @@ -2672,8 +2721,8 @@ public class MockingOomAdjusterTests { s = bindService(app3, app2, null, null, 0, mock(IBinder.class)); s.lastActivity = now; - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); - sService.mOomAdjuster.mNumServiceProcs = 3; + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mOomAdjuster.mNumServiceProcs = 3; updateOomAdj(app, app2, app3); assertEquals(SERVICE_ADJ, app.mState.getSetAdj()); @@ -2688,9 +2737,9 @@ public class MockingOomAdjusterTests { public void testUpdateOomAdj_DoOne_AboveClient_NotStarted() { ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, true)); - doReturn(PROCESS_STATE_TOP).when(sService.mAtmInternal).getTopProcessState(); - doReturn(app).when(sService).getTopApp(); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + doReturn(PROCESS_STATE_TOP).when(mService.mAtmInternal).getTopProcessState(); + doReturn(app).when(mService).getTopApp(); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(app); assertEquals(FOREGROUND_APP_ADJ, app.mState.getSetAdj()); @@ -2718,7 +2767,7 @@ public class MockingOomAdjusterTests { ServiceRecord s = makeServiceRecord(app); s.startRequested = true; s.lastActivity = SystemClock.uptimeMillis(); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(); assertProcStates(app, PROCESS_STATE_SERVICE, SERVICE_ADJ, SCHED_GROUP_BACKGROUND); @@ -2738,7 +2787,7 @@ public class MockingOomAdjusterTests { ServiceRecord s = makeServiceRecord(app); s.startRequested = true; s.lastActivity = SystemClock.uptimeMillis(); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdjPending(app); assertProcStates(app, PROCESS_STATE_SERVICE, SERVICE_ADJ, SCHED_GROUP_BACKGROUND); @@ -2760,7 +2809,7 @@ public class MockingOomAdjusterTests { ServiceRecord s = makeServiceRecord(app); s.startRequested = true; s.lastActivity = SystemClock.uptimeMillis(); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(); assertProcStates(app, PROCESS_STATE_SERVICE, SERVICE_ADJ, SCHED_GROUP_BACKGROUND); @@ -2781,7 +2830,7 @@ public class MockingOomAdjusterTests { ServiceRecord s = makeServiceRecord(app); s.startRequested = true; s.lastActivity = SystemClock.uptimeMillis(); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdjPending(app); assertProcStates(app, PROCESS_STATE_SERVICE, SERVICE_ADJ, SCHED_GROUP_BACKGROUND); @@ -2809,7 +2858,7 @@ public class MockingOomAdjusterTests { client.mState.setMaxAdj(PERSISTENT_PROC_ADJ); attributedClient.mServices.setHasForegroundServices(true, 0, true); bindService(sandboxService, client, attributedClient, null, 0, mock(IBinder.class)); - sService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); updateOomAdj(); assertProcStates(client, PROCESS_STATE_PERSISTENT, PERSISTENT_PROC_ADJ, SCHED_GROUP_DEFAULT); @@ -2830,13 +2879,13 @@ public class MockingOomAdjusterTests { // App1 binds to app2 and gets temp allowlisted. bindService(app2, app, null, null, 0, mock(IBinder.class)); - sService.mOomAdjuster.setUidTempAllowlistStateLSP(MOCKAPP_UID, true); + mService.mOomAdjuster.setUidTempAllowlistStateLSP(MOCKAPP_UID, true); assertEquals(true, app.getUidRecord().isSetAllowListed()); assertEquals(true, app.mOptRecord.shouldNotFreeze()); assertEquals(true, app2.mOptRecord.shouldNotFreeze()); - sService.mOomAdjuster.setUidTempAllowlistStateLSP(MOCKAPP_UID, false); + mService.mOomAdjuster.setUidTempAllowlistStateLSP(MOCKAPP_UID, false); assertEquals(false, app.getUidRecord().isSetAllowListed()); assertEquals(false, app.mOptRecord.shouldNotFreeze()); assertEquals(false, app2.mOptRecord.shouldNotFreeze()); @@ -2856,8 +2905,8 @@ public class MockingOomAdjusterTests { // App1 and app2 both bind to app3 and get temp allowlisted. bindService(app3, app, null, null, 0, mock(IBinder.class)); bindService(app3, app2, null, null, 0, mock(IBinder.class)); - sService.mOomAdjuster.setUidTempAllowlistStateLSP(MOCKAPP_UID, true); - sService.mOomAdjuster.setUidTempAllowlistStateLSP(MOCKAPP2_UID, true); + mService.mOomAdjuster.setUidTempAllowlistStateLSP(MOCKAPP_UID, true); + mService.mOomAdjuster.setUidTempAllowlistStateLSP(MOCKAPP2_UID, true); assertEquals(true, app.getUidRecord().isSetAllowListed()); assertEquals(true, app2.getUidRecord().isSetAllowListed()); @@ -2866,7 +2915,7 @@ public class MockingOomAdjusterTests { assertEquals(true, app3.mOptRecord.shouldNotFreeze()); // Remove app1 from allowlist. - sService.mOomAdjuster.setUidTempAllowlistStateLSP(MOCKAPP_UID, false); + mService.mOomAdjuster.setUidTempAllowlistStateLSP(MOCKAPP_UID, false); assertEquals(false, app.getUidRecord().isSetAllowListed()); assertEquals(true, app2.getUidRecord().isSetAllowListed()); assertEquals(false, app.mOptRecord.shouldNotFreeze()); @@ -2874,7 +2923,7 @@ public class MockingOomAdjusterTests { assertEquals(true, app3.mOptRecord.shouldNotFreeze()); // Now remove app2 from allowlist. - sService.mOomAdjuster.setUidTempAllowlistStateLSP(MOCKAPP2_UID, false); + mService.mOomAdjuster.setUidTempAllowlistStateLSP(MOCKAPP2_UID, false); assertEquals(false, app.getUidRecord().isSetAllowListed()); assertEquals(false, app2.getUidRecord().isSetAllowListed()); assertEquals(false, app.mOptRecord.shouldNotFreeze()); @@ -2882,6 +2931,73 @@ public class MockingOomAdjusterTests { assertEquals(false, app3.mOptRecord.shouldNotFreeze()); } + @SuppressWarnings("GuardedBy") + @Test + public void testUpdateOomAdj_DoAll_ClientlessService() { + ProcessRecord app = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, + MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false)); + + setProcessesToLru(app); + ServiceRecord s = makeServiceRecord(app); + s.startRequested = true; + s.lastActivity = SystemClock.uptimeMillis(); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + updateOomAdj(); + assertProcStates(app, PROCESS_STATE_SERVICE, SERVICE_ADJ, SCHED_GROUP_BACKGROUND, + "started-services"); + + if (!Flags.followUpOomadjUpdates()) return; + + final ArgumentCaptor<Long> followUpTimeCaptor = ArgumentCaptor.forClass(Long.class); + verify(mService.mHandler).sendEmptyMessageAtTime( + eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG), followUpTimeCaptor.capture()); + mInjector.jumpUptimeAheadTo(followUpTimeCaptor.getValue()); + mService.mOomAdjuster.updateOomAdjFollowUpTargetsLocked(); + + assertProcStates(app, PROCESS_STATE_SERVICE, sFirstCachedAdj, SCHED_GROUP_BACKGROUND, + "cch-started-services"); + // Follow up should not have been called again. + verify(mService.mHandler).sendEmptyMessageAtTime(eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG), + followUpTimeCaptor.capture()); + } + + @SuppressWarnings("GuardedBy") + @Test + public void testUpdateOomAdj_DoAll_Multiple_Provider_Retention() { + ProcessRecord app1 = spy(makeDefaultProcessRecord(MOCKAPP_PID, MOCKAPP_UID, + MOCKAPP_PROCESSNAME, MOCKAPP_PACKAGENAME, false)); + ProcessRecord app2 = spy(makeDefaultProcessRecord(MOCKAPP2_PID, MOCKAPP2_UID, + MOCKAPP2_PROCESSNAME, MOCKAPP2_PACKAGENAME, false)); + app1.mProviders.setLastProviderTime(SystemClock.uptimeMillis()); + app2.mProviders.setLastProviderTime(SystemClock.uptimeMillis() + 2000); + mService.mWakefulness.set(PowerManagerInternal.WAKEFULNESS_AWAKE); + setProcessesToLru(app1, app2); + mService.mOomAdjuster.updateOomAdjLocked(OOM_ADJ_REASON_NONE); + + assertProcStates(app1, PROCESS_STATE_LAST_ACTIVITY, PREVIOUS_APP_ADJ, + SCHED_GROUP_BACKGROUND, "recent-provider"); + assertProcStates(app2, PROCESS_STATE_LAST_ACTIVITY, PREVIOUS_APP_ADJ, + SCHED_GROUP_BACKGROUND, "recent-provider"); + + if (!Flags.followUpOomadjUpdates()) return; + + final ArgumentCaptor<Long> followUpTimeCaptor = ArgumentCaptor.forClass(Long.class); + verify(mService.mHandler, atLeastOnce()).sendEmptyMessageAtTime( + eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG), followUpTimeCaptor.capture()); + mInjector.jumpUptimeAheadTo(followUpTimeCaptor.getValue()); + mService.mOomAdjuster.updateOomAdjFollowUpTargetsLocked(); + + assertProcStates(app1, PROCESS_STATE_CACHED_EMPTY, sFirstCachedAdj, SCHED_GROUP_BACKGROUND, + "cch-empty"); + + verify(mService.mHandler, atLeastOnce()).sendEmptyMessageAtTime( + eq(FOLLOW_UP_OOMADJUSTER_UPDATE_MSG), followUpTimeCaptor.capture()); + mInjector.jumpUptimeAheadTo(followUpTimeCaptor.getValue()); + mService.mOomAdjuster.updateOomAdjFollowUpTargetsLocked(); + assertProcStates(app2, PROCESS_STATE_CACHED_EMPTY, sFirstCachedAdj, SCHED_GROUP_BACKGROUND, + "cch-empty"); + } + private ProcessRecord makeDefaultProcessRecord(int pid, int uid, String processName, String packageName, boolean hasShownUi) { return new ProcessRecordBuilder(pid, uid, processName, packageName).setHasShownUi( @@ -2904,7 +3020,7 @@ public class MockingOomAdjusterTests { } private void setServiceMap(ServiceRecord s, int uid, ComponentName cn) { - ActiveServices.ServiceMap serviceMap = sService.mServices.mServiceMap.get( + ActiveServices.ServiceMap serviceMap = mService.mServices.mServiceMap.get( UserHandle.getUserId(uid)); if (serviceMap == null) { serviceMap = mock(ActiveServices.ServiceMap.class); @@ -2916,7 +3032,7 @@ public class MockingOomAdjusterTests { new ArrayMap<>()); setFieldValue(ActiveServices.ServiceMap.class, serviceMap, "mDelayedStartList", new ArrayList<>()); - sService.mServices.mServiceMap.put(UserHandle.getUserId(uid), serviceMap); + mService.mServices.mServiceMap.put(UserHandle.getUserId(uid), serviceMap); } serviceMap.mServicesByInstanceName.put(cn, s); } @@ -2957,6 +3073,7 @@ public class MockingOomAdjusterTests { return record; } + @SuppressWarnings("GuardedBy") private void assertProcStates(ProcessRecord app, int expectedProcState, int expectedAdj, int expectedSchedGroup) { final ProcessStateRecord state = app.mState; @@ -2974,6 +3091,7 @@ public class MockingOomAdjusterTests { } } + @SuppressWarnings("GuardedBy") private void assertProcStates(ProcessRecord app, boolean expectedCached, int expectedProcState, int expectedAdj, String expectedAdjType) { final ProcessStateRecord state = app.mState; @@ -2992,7 +3110,26 @@ public class MockingOomAdjusterTests { } } - private static class ProcessRecordBuilder { + @SuppressWarnings("GuardedBy") + private void assertProcStates(ProcessRecord app, int expectedProcState, int expectedAdj, + int expectedSchedGroup, String expectedAdjType) { + final ProcessStateRecord state = app.mState; + assertEquals(expectedAdjType, state.getAdjType()); + assertEquals(expectedProcState, state.getSetProcState()); + assertEquals(expectedAdj, state.getSetAdj()); + assertEquals(expectedSchedGroup, state.getSetSchedGroup()); + + // Below BFGS should never have BFSL. + if (expectedProcState > PROCESS_STATE_BOUND_FOREGROUND_SERVICE) { + assertNoBfsl(app); + } + // Above FGS should always have BFSL. + if (expectedProcState < PROCESS_STATE_FOREGROUND_SERVICE) { + assertBfsl(app); + } + } + + private class ProcessRecordBuilder { @SuppressWarnings("UnusedVariable") int mPid; int mUid; @@ -3069,17 +3206,17 @@ public class MockingOomAdjusterTests { ai.packageName = mPackageName; ai.longVersionCode = mVersionCode; ai.targetSdkVersion = mTargetSdkVersion; - doCallRealMethod().when(sService).getPackageManagerInternal(); - doReturn(null).when(sPackageManagerInternal).getApplicationInfo( + doCallRealMethod().when(mService).getPackageManagerInternal(); + doReturn(null).when(mPackageManagerInternal).getApplicationInfo( eq(mSdkSandboxClientAppPackage), anyLong(), anyInt(), anyInt()); - ProcessRecord app = new ProcessRecord(sService, ai, mProcessName, mUid, + ProcessRecord app = new ProcessRecord(mService, ai, mProcessName, mUid, mSdkSandboxClientAppPackage, -1, null); final ProcessStateRecord state = app.mState; final ProcessServiceRecord services = app.mServices; final ProcessReceiverRecord receivers = app.mReceivers; final ProcessProfileRecord profile = app.mProfile; final ProcessProviderRecord providers = app.mProviders; - app.makeActive(mock(IApplicationThread.class), sService.mProcessStats); + app.makeActive(mock(IApplicationThread.class), mService.mProcessStats); app.setLastActivityTime(mLastActivityTime); app.setKilledByAm(mKilledByAm); app.setIsolatedEntryPoint(mIsolatedEntryPoint); @@ -3124,14 +3261,35 @@ public class MockingOomAdjusterTests { } providers.setLastProviderTime(mLastProviderTime); - UidRecord uidRec = sService.mOomAdjuster.mActiveUids.get(mUid); + UidRecord uidRec = mService.mOomAdjuster.mActiveUids.get(mUid); if (uidRec == null) { - uidRec = new UidRecord(mUid, sService); - sService.mOomAdjuster.mActiveUids.put(mUid, uidRec); + uidRec = new UidRecord(mUid, mService); + mService.mOomAdjuster.mActiveUids.put(mUid, uidRec); } uidRec.addProcess(app); app.setUidRecord(uidRec); return app; } } + + static class OomAdjusterInjector extends OomAdjuster.Injector { + // Jump ahead in time by this offset amount. + long mTimeOffsetMillis = 0; + + void jumpUptimeAheadTo(long uptimeMillis) { + final long jumpMs = uptimeMillis - getUptimeMillis(); + if (jumpMs <= 0) return; + mTimeOffsetMillis += jumpMs; + } + + @Override + long getUptimeMillis() { + return SystemClock.uptimeMillis() + mTimeOffsetMillis; + } + + @Override + long getElapsedRealtimeMillis() { + return SystemClock.elapsedRealtime() + mTimeOffsetMillis; + } + } } diff --git a/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java b/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java index 4460c6af0691..ce2bb95b790a 100644 --- a/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java +++ b/services/tests/powerservicetests/src/com/android/server/power/NotifierTest.java @@ -31,6 +31,7 @@ import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.verifyZeroInteractions; import static org.mockito.Mockito.when; +import android.app.AppOpsManager; import android.content.Context; import android.content.res.Resources; import android.hardware.SensorManager; @@ -41,7 +42,6 @@ import android.os.IWakeLockCallback; import android.os.Looper; import android.os.PowerManager; import android.os.RemoteException; -import android.os.ServiceManager; import android.os.VibrationAttributes; import android.os.Vibrator; import android.os.test.TestLooper; @@ -82,8 +82,12 @@ public class NotifierTest { @Mock private StatusBarManagerInternal mStatusBarManagerInternal; @Mock private WakeLockLog mWakeLockLog; + @Mock private IBatteryStats mBatteryStats; + @Mock private PowerManagerFlags mPowerManagerFlags; + @Mock private AppOpsManager mAppOpsManager; + private PowerManagerService mService; private Context mContextSpy; private Resources mResourcesSpy; @@ -230,7 +234,7 @@ public class NotifierTest { public void testOnWakeLockListener_RemoteException_NoRethrow() { when(mPowerManagerFlags.improveWakelockLatency()).thenReturn(true); createNotifier(); - + clearInvocations(mWakeLockLog, mBatteryStats, mAppOpsManager); IWakeLockCallback exceptingCallback = new IWakeLockCallback.Stub() { @Override public void onStateChanged(boolean enabled) throws RemoteException { throw new RemoteException("Just testing"); @@ -245,6 +249,7 @@ public class NotifierTest { verifyZeroInteractions(mWakeLockLog); mTestLooper.dispatchAll(); verify(mWakeLockLog).onWakeLockReleased("wakelockTag", uid, 1); + mNotifier.onWakeLockAcquired(PowerManager.PARTIAL_WAKE_LOCK, "wakelockTag", "my.package.name", uid, pid, /* workSource= */ null, /* historyTag= */ null, exceptingCallback); @@ -277,6 +282,115 @@ public class NotifierTest { verify(mWakeLockLog).onWakeLockReleased("wakelockTag", uid, -1); } + + @Test + public void testOnWakeLockListener_FullWakeLock_ProcessesOnHandler() throws RemoteException { + when(mPowerManagerFlags.improveWakelockLatency()).thenReturn(true); + createNotifier(); + + IWakeLockCallback exceptingCallback = new IWakeLockCallback.Stub() { + @Override public void onStateChanged(boolean enabled) throws RemoteException { + throw new RemoteException("Just testing"); + } + }; + clearInvocations(mWakeLockLog, mBatteryStats, mAppOpsManager); + + final int uid = 1234; + final int pid = 5678; + + // Release the wakelock + mNotifier.onWakeLockReleased(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "wakelockTag", + "my.package.name", uid, pid, /* workSource= */ null, /* historyTag= */ null, + exceptingCallback); + + // No interaction because we expect that to happen in async + verifyZeroInteractions(mWakeLockLog, mBatteryStats, mAppOpsManager); + + // Progressing the looper, and validating all the interactions + mTestLooper.dispatchAll(); + verify(mWakeLockLog).onWakeLockReleased("wakelockTag", uid, 1); + verify(mBatteryStats).noteStopWakelock(uid, pid, "wakelockTag", /* historyTag= */ null, + BatteryStats.WAKE_TYPE_FULL); + verify(mAppOpsManager).finishOp(AppOpsManager.OP_WAKE_LOCK, uid, + "my.package.name", null); + + clearInvocations(mWakeLockLog, mBatteryStats, mAppOpsManager); + + // Acquire the wakelock + mNotifier.onWakeLockAcquired(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "wakelockTag", + "my.package.name", uid, pid, /* workSource= */ null, /* historyTag= */ null, + exceptingCallback); + + // No interaction because we expect that to happen in async + verifyNoMoreInteractions(mWakeLockLog, mBatteryStats, mAppOpsManager); + + // Progressing the looper, and validating all the interactions + mTestLooper.dispatchAll(); + verify(mWakeLockLog).onWakeLockAcquired("wakelockTag", uid, + PowerManager.SCREEN_BRIGHT_WAKE_LOCK, 1); + verify(mBatteryStats).noteStartWakelock(uid, pid, "wakelockTag", /* historyTag= */ null, + BatteryStats.WAKE_TYPE_FULL, false); + verify(mAppOpsManager).startOpNoThrow(AppOpsManager.OP_WAKE_LOCK, uid, + "my.package.name", false, null, null); + + // Test with improveWakelockLatency flag false, hence the wakelock log will run on the same + // thread + clearInvocations(mWakeLockLog, mBatteryStats, mAppOpsManager); + when(mPowerManagerFlags.improveWakelockLatency()).thenReturn(false); + + mNotifier.onWakeLockAcquired(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "wakelockTag", + "my.package.name", uid, pid, /* workSource= */ null, /* historyTag= */ null, + exceptingCallback); + verify(mWakeLockLog).onWakeLockAcquired("wakelockTag", uid, + PowerManager.SCREEN_BRIGHT_WAKE_LOCK, -1); + + mNotifier.onWakeLockReleased(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "wakelockTag", + "my.package.name", uid, pid, /* workSource= */ null, /* historyTag= */ null, + exceptingCallback); + verify(mWakeLockLog).onWakeLockReleased("wakelockTag", uid, -1); + } + + @Test + public void testOnWakeLockListener_FullWakeLock_ProcessesInSync() throws RemoteException { + createNotifier(); + + IWakeLockCallback exceptingCallback = new IWakeLockCallback.Stub() { + @Override public void onStateChanged(boolean enabled) throws RemoteException { + throw new RemoteException("Just testing"); + } + }; + clearInvocations(mWakeLockLog, mBatteryStats, mAppOpsManager); + + final int uid = 1234; + final int pid = 5678; + + // Release the wakelock + mNotifier.onWakeLockReleased(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "wakelockTag", + "my.package.name", uid, pid, /* workSource= */ null, /* historyTag= */ null, + exceptingCallback); + + verify(mWakeLockLog).onWakeLockReleased("wakelockTag", uid, -1); + verify(mBatteryStats).noteStopWakelock(uid, pid, "wakelockTag", /* historyTag= */ null, + BatteryStats.WAKE_TYPE_FULL); + verify(mAppOpsManager).finishOp(AppOpsManager.OP_WAKE_LOCK, uid, + "my.package.name", null); + + clearInvocations(mWakeLockLog, mBatteryStats, mAppOpsManager); + + // Acquire the wakelock + mNotifier.onWakeLockAcquired(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "wakelockTag", + "my.package.name", uid, pid, /* workSource= */ null, /* historyTag= */ null, + exceptingCallback); + + mTestLooper.dispatchAll(); + verify(mWakeLockLog).onWakeLockAcquired("wakelockTag", uid, + PowerManager.SCREEN_BRIGHT_WAKE_LOCK, -1); + verify(mBatteryStats).noteStartWakelock(uid, pid, "wakelockTag", /* historyTag= */ null, + BatteryStats.WAKE_TYPE_FULL, false); + verify(mAppOpsManager).startOpNoThrow(AppOpsManager.OP_WAKE_LOCK, uid, + "my.package.name", false, null, null); + } + private final PowerManagerService.Injector mInjector = new PowerManagerService.Injector() { @Override Notifier createNotifier(Looper looper, Context context, IBatteryStats batteryStats, @@ -365,13 +479,17 @@ public class NotifierTest { public WakeLockLog getWakeLockLog(Context context) { return mWakeLockLog; } + + @Override + public AppOpsManager getAppOpsManager(Context context) { + return mAppOpsManager; + } }; mNotifier = new Notifier( mTestLooper.getLooper(), mContextSpy, - IBatteryStats.Stub.asInterface(ServiceManager.getService( - BatteryStats.SERVICE_NAME)), + mBatteryStats, mInjector.createSuspendBlocker(mService, "testBlocker"), null, null, diff --git a/services/tests/servicestests/src/com/android/server/am/OomAdjusterTests.java b/services/tests/servicestests/src/com/android/server/am/OomAdjusterTests.java index 8cbed2c7ffb8..31bf5f046cff 100644 --- a/services/tests/servicestests/src/com/android/server/am/OomAdjusterTests.java +++ b/services/tests/servicestests/src/com/android/server/am/OomAdjusterTests.java @@ -61,20 +61,6 @@ public class OomAdjusterTests { private static final long USAGE_STATS_INTERACTION = 10 * 60 * 1000L; private static final long SERVICE_USAGE_INTERACTION = 60 * 1000; - static class MyOomAdjuster extends OomAdjuster { - - MyOomAdjuster(ActivityManagerService service, ProcessList processList, - ActiveUids activeUids) { - super(service, processList, activeUids); - } - - @Override - protected boolean isChangeEnabled(int changeId, ApplicationInfo app, - boolean defaultValue) { - return true; - } - } - @BeforeClass public static void setUpOnce() { sContext = getInstrumentation().getTargetContext(); @@ -99,7 +85,15 @@ public class OomAdjusterTests { final AppProfiler profiler = mock(AppProfiler.class); setFieldValue(AppProfiler.class, profiler, "mProfilerLock", new Object()); setFieldValue(ActivityManagerService.class, sService, "mAppProfiler", profiler); - sService.mOomAdjuster = new MyOomAdjuster(sService, sService.mProcessList, null); + final OomAdjuster.Injector injector = new OomAdjuster.Injector(){ + @Override + boolean isChangeEnabled(int changeId, ApplicationInfo app, + boolean defaultValue) { + return true; + } + }; + sService.mOomAdjuster = new OomAdjuster(sService, sService.mProcessList, null, + injector); LocalServices.addService(UsageStatsManagerInternal.class, mock(UsageStatsManagerInternal.class)); sService.mUsageStatsService = LocalServices.getService(UsageStatsManagerInternal.class); diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java index 3cab75b5d320..3d6884925098 100644 --- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java @@ -18,11 +18,13 @@ package com.android.server.net; import static android.Manifest.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS; import static android.Manifest.permission.NETWORK_STACK; +import static android.app.ActivityManager.MAX_PROCESS_STATE; import static android.app.ActivityManager.PROCESS_CAPABILITY_ALL; import static android.app.ActivityManager.PROCESS_CAPABILITY_NONE; import static android.app.ActivityManager.PROCESS_CAPABILITY_POWER_RESTRICTED_NETWORK; import static android.app.ActivityManager.PROCESS_CAPABILITY_USER_RESTRICTED_NETWORK; import static android.app.ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND; +import static android.app.ActivityManager.PROCESS_STATE_LAST_ACTIVITY; import static android.app.ActivityManager.PROCESS_STATE_SERVICE; import static android.app.ActivityManager.PROCESS_STATE_TOP; import static android.net.ConnectivityManager.BLOCKED_METERED_REASON_DATA_SAVER; @@ -165,9 +167,11 @@ import android.os.PowerManagerInternal; import android.os.PowerSaveState; import android.os.RemoteException; import android.os.SimpleClock; +import android.os.SystemClock; import android.os.UserHandle; import android.os.UserManager; import android.platform.test.annotations.Presubmit; +import android.platform.test.annotations.RequiresFlagsDisabled; import android.platform.test.annotations.RequiresFlagsEnabled; import android.platform.test.flag.junit.CheckFlagsRule; import android.platform.test.flag.junit.DeviceFlagsValueProvider; @@ -2158,7 +2162,8 @@ public class NetworkPolicyManagerServiceTest { @Test @RequiresFlagsEnabled(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE) - public void testBackgroundChainOnProcStateChange() throws Exception { + @RequiresFlagsDisabled(Flags.FLAG_USE_DIFFERENT_DELAYS_FOR_BACKGROUND_CHAIN) + public void testBackgroundChainOnProcStateChangeSameDelay() throws Exception { // initialization calls setFirewallChainEnabled, so we want to reset the invocations. clearInvocations(mNetworkManager); @@ -2186,6 +2191,59 @@ public class NetworkPolicyManagerServiceTest { } @Test + @RequiresFlagsEnabled({ + Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE, + Flags.FLAG_USE_DIFFERENT_DELAYS_FOR_BACKGROUND_CHAIN + }) + public void testBackgroundChainOnProcStateChangeDifferentDelays() throws Exception { + // The app will be blocked when there is no prior proc-state. + assertTrue(mService.isUidNetworkingBlocked(UID_A, false)); + + // Tweak delays to avoid waiting too long in tests. + mService.mBackgroundRestrictionShortDelayMs = 50; + mService.mBackgroundRestrictionLongDelayMs = 1000; + + int procStateSeq = 231; // Any arbitrary starting sequence. + for (int ps = BACKGROUND_THRESHOLD_STATE; ps <= MAX_PROCESS_STATE; ps++) { + clearInvocations(mNetworkManager); + + // Make sure app is in correct process-state to access network. + callAndWaitOnUidStateChanged(UID_A, BACKGROUND_THRESHOLD_STATE - 1, procStateSeq++); + verify(mNetworkManager).setFirewallUidRule(FIREWALL_CHAIN_BACKGROUND, UID_A, + FIREWALL_RULE_ALLOW); + assertFalse(mService.isUidNetworkingBlocked(UID_A, false)); + + // Now put the app into the background and test that it eventually loses network. + callAndWaitOnUidStateChanged(UID_A, ps, procStateSeq++); + + final long uidStateChangeTime = SystemClock.uptimeMillis(); + if (ps <= PROCESS_STATE_LAST_ACTIVITY) { + // Verify that the app is blocked after long delay but not after short delay. + waitForDelayedMessageOnHandler(mService.mBackgroundRestrictionShortDelayMs + 1); + verify(mNetworkManager, never()).setFirewallUidRule(FIREWALL_CHAIN_BACKGROUND, + UID_A, FIREWALL_RULE_DEFAULT); + assertFalse(mService.isUidNetworkingBlocked(UID_A, false)); + + final long timeUntilLongDelay = uidStateChangeTime + + mService.mBackgroundRestrictionLongDelayMs - SystemClock.uptimeMillis(); + assertTrue("No time left to verify long delay in background transition", + timeUntilLongDelay >= 0); + + waitForDelayedMessageOnHandler(timeUntilLongDelay + 1); + verify(mNetworkManager).setFirewallUidRule(FIREWALL_CHAIN_BACKGROUND, UID_A, + FIREWALL_RULE_DEFAULT); + assertTrue(mService.isUidNetworkingBlocked(UID_A, false)); + } else { + // Verify that the app is blocked after short delay. + waitForDelayedMessageOnHandler(mService.mBackgroundRestrictionShortDelayMs + 1); + verify(mNetworkManager).setFirewallUidRule(FIREWALL_CHAIN_BACKGROUND, UID_A, + FIREWALL_RULE_DEFAULT); + assertTrue(mService.isUidNetworkingBlocked(UID_A, false)); + } + } + } + + @Test @RequiresFlagsEnabled(Flags.FLAG_NETWORK_BLOCKED_FOR_TOP_SLEEPING_AND_ABOVE) public void testBackgroundChainOnAllowlistChange() throws Exception { // initialization calls setFirewallChainEnabled, so we want to reset the invocations. @@ -2881,6 +2939,11 @@ public class NetworkPolicyManagerServiceTest { } } + /** + * This posts a blocking message to the service handler with the given delayMs and waits for it + * to complete. This ensures that all messages posted before the given delayMs will also + * have been executed before this method returns and can be verified in subsequent code. + */ private void waitForDelayedMessageOnHandler(long delayMs) throws InterruptedException { final CountDownLatch latch = new CountDownLatch(1); mService.getHandlerForTesting().postDelayed(latch::countDown, delayMs); diff --git a/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java b/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java index 0f28528c9327..e019a416c069 100644 --- a/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java @@ -1235,12 +1235,18 @@ public class RootTaskTests extends WindowTestsBase { assertEquals(STOPPING, activity2.getState()); assertThat(mSupervisor.mStoppingActivities).contains(activity2); + registerTestTransitionPlayer(); + final Transition transition = display.mTransitionController + .requestCloseTransitionIfNeeded(rootTask1); + transition.collectClose(rootTask1); // The display becomes empty. Since there is no next activity to be idle, the activity // should be destroyed immediately with updating configuration to restore original state. final ActivityRecord activity1 = finishTopActivity(rootTask1); assertEquals(DESTROYING, activity1.getState()); verify(mRootWindowContainer).ensureVisibilityAndConfig(eq(null) /* starting */, eq(display), anyBoolean()); + assertTrue("Transition must be ready if there is no next running activity", + transition.allReady()); } private ActivityRecord finishTopActivity(Task task) { diff --git a/tests/Input/assets/testPointerScale.png b/tests/Input/assets/testPointerScale.png Binary files differnew file mode 100644 index 000000000000..54d37c24afc6 --- /dev/null +++ b/tests/Input/assets/testPointerScale.png diff --git a/tests/Input/src/com/android/test/input/PointerIconLoadingTest.kt b/tests/Input/src/com/android/test/input/PointerIconLoadingTest.kt index dac425329f48..d196b85a7466 100644 --- a/tests/Input/src/com/android/test/input/PointerIconLoadingTest.kt +++ b/tests/Input/src/com/android/test/input/PointerIconLoadingTest.kt @@ -93,7 +93,29 @@ class PointerIconLoadingTest { PointerIcon.getLoadedSystemIcon( ContextThemeWrapper(context, theme), PointerIcon.TYPE_ARROW, - /* useLargeIcons= */ false) + /* useLargeIcons= */ false, + /* pointerScale= */ 1f) + + pointerIcon.getBitmap().assertAgainstGolden( + screenshotRule, + testName.methodName, + exactScreenshotMatcher + ) + } + + @Test + fun testPointerScale() { + assumeTrue(enableVectorCursors()) + assumeTrue(enableVectorCursorA11ySettings()) + + val pointerScale = 2f + + val pointerIcon = + PointerIcon.getLoadedSystemIcon( + context, + PointerIcon.TYPE_ARROW, + /* useLargeIcons= */ false, + pointerScale) pointerIcon.getBitmap().assertAgainstGolden( screenshotRule, |