diff options
403 files changed, 8476 insertions, 2964 deletions
diff --git a/apct-tests/perftests/packagemanager/AndroidTest.xml b/apct-tests/perftests/packagemanager/AndroidTest.xml index c9d45a6bda74..db938e4373a3 100644 --- a/apct-tests/perftests/packagemanager/AndroidTest.xml +++ b/apct-tests/perftests/packagemanager/AndroidTest.xml @@ -76,11 +76,6 @@ <option name="test-file-name" value="QueriesAll49.apk"/> </target_preparer> - <test class="com.android.tradefed.testtype.AndroidJUnitTest"> - <option name="package" value="com.android.perftests.packagemanager"/> - <option name="hidden-api-checks" value="false"/> - </test> - <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector"> <option name="directory-keys" value="/data/local/PackageManagerPerfTests"/> <option name="collect-on-run-ended-only" value="true"/> diff --git a/core/api/current.txt b/core/api/current.txt index c189a24c84ae..c86e4cd40cfa 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -282,7 +282,7 @@ package android { field public static final String REQUEST_IGNORE_BATTERY_OPTIMIZATIONS = "android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"; field public static final String REQUEST_INSTALL_PACKAGES = "android.permission.REQUEST_INSTALL_PACKAGES"; field public static final String REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE = "android.permission.REQUEST_OBSERVE_COMPANION_DEVICE_PRESENCE"; - field @FlaggedApi("android.companion.flags.device_presence") public static final String REQUEST_OBSERVE_DEVICE_UUID_PRESENCE = "android.permission.REQUEST_OBSERVE_DEVICE_UUID_PRESENCE"; + field @FlaggedApi("android.companion.device_presence") public static final String REQUEST_OBSERVE_DEVICE_UUID_PRESENCE = "android.permission.REQUEST_OBSERVE_DEVICE_UUID_PRESENCE"; field public static final String REQUEST_PASSWORD_COMPLEXITY = "android.permission.REQUEST_PASSWORD_COMPLEXITY"; field @Deprecated public static final String RESTART_PACKAGES = "android.permission.RESTART_PACKAGES"; field public static final String RUN_USER_INITIATED_JOBS = "android.permission.RUN_USER_INITIATED_JOBS"; @@ -39388,10 +39388,8 @@ package android.security { } public final class FileIntegrityManager { - method @FlaggedApi("android.security.fsverity_api") @Nullable public byte[] getFsVerityDigest(@NonNull java.io.File) throws java.io.IOException; method public boolean isApkVeritySupported(); method @Deprecated @RequiresPermission(anyOf={android.Manifest.permission.INSTALL_PACKAGES, android.Manifest.permission.REQUEST_INSTALL_PACKAGES}) public boolean isAppSourceCertificateTrusted(@NonNull java.security.cert.X509Certificate) throws java.security.cert.CertificateEncodingException; - method @FlaggedApi("android.security.fsverity_api") public void setupFsVerity(@NonNull java.io.File) throws java.io.IOException; } public final class KeyChain { diff --git a/core/api/system-current.txt b/core/api/system-current.txt index b767c52ea9ba..bed8c4169a63 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -355,6 +355,7 @@ package android { field public static final String SEND_SHOW_SUSPENDED_APP_DETAILS = "android.permission.SEND_SHOW_SUSPENDED_APP_DETAILS"; field public static final String SEND_SMS_NO_CONFIRMATION = "android.permission.SEND_SMS_NO_CONFIRMATION"; field public static final String SERIAL_PORT = "android.permission.SERIAL_PORT"; + field @FlaggedApi("android.security.fsverity_api") public static final String SETUP_FSVERITY = "android.permission.SETUP_FSVERITY"; field public static final String SET_ACTIVITY_WATCHER = "android.permission.SET_ACTIVITY_WATCHER"; field public static final String SET_CLIP_SOURCE = "android.permission.SET_CLIP_SOURCE"; field public static final String SET_DEFAULT_ACCOUNT_FOR_CONTACTS = "android.permission.SET_DEFAULT_ACCOUNT_FOR_CONTACTS"; @@ -1144,6 +1145,7 @@ package android.app { public static final class StatusBarManager.DisableInfo implements android.os.Parcelable { method public boolean areAllComponentsEnabled(); method public int describeContents(); + method public boolean isBackDisabled(); method public boolean isNavigateToHomeDisabled(); method public boolean isNotificationPeekingDisabled(); method public boolean isRecentsDisabled(); @@ -12106,6 +12108,11 @@ package android.se.omapi { package android.security { + public final class FileIntegrityManager { + method @FlaggedApi("android.security.fsverity_api") @Nullable public byte[] getFsVerityDigest(@NonNull java.io.File) throws java.io.IOException; + method @FlaggedApi("android.security.fsverity_api") public void setupFsVerity(@NonNull java.io.File) throws java.io.IOException; + } + public final class KeyChain { method @Nullable @WorkerThread public static String getWifiKeyGrantAsUser(@NonNull android.content.Context, @NonNull android.os.UserHandle, @NonNull String); method @WorkerThread public static boolean hasWifiKeyGrantAsUser(@NonNull android.content.Context, @NonNull android.os.UserHandle, @NonNull String); diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java index 301fef877d6c..e6e46ddaa420 100644 --- a/core/java/android/app/StatusBarManager.java +++ b/core/java/android/app/StatusBarManager.java @@ -1448,6 +1448,7 @@ public class StatusBarManager { * * @hide */ + @SystemApi public boolean isBackDisabled() { return mBack; } @@ -1861,38 +1862,38 @@ public class StatusBarManager { }; @DataClass.Generated( - time = 1708625947132L, + time = 1707345957771L, codegenVersion = "1.0.23", sourceFile = "frameworks/base/core/java/android/app/StatusBarManager.java", - inputSignatures = "private boolean mStatusBarExpansion\nprivate boolean " - + "mNavigateHome\nprivate boolean mNotificationPeeking\nprivate " - + "boolean mRecents\nprivate boolean mBack\nprivate boolean mSearch\n" - + "private boolean mSystemIcons\nprivate boolean mClock\nprivate " - + "boolean mNotificationIcons\nprivate boolean mRotationSuggestion\n" + inputSignatures = "private boolean mStatusBarExpansion\nprivate " + + "boolean mNavigateHome\nprivate boolean mNotificationPeeking\nprivate " + + "boolean mRecents\nprivate boolean mBack\nprivate boolean " + + "mSearch\nprivate boolean mSystemIcons\nprivate boolean mClock\nprivate" + + " boolean mNotificationIcons\nprivate boolean mRotationSuggestion\n" + "private boolean mNotificationTicker\npublic " + "@android.annotation.SystemApi boolean isStatusBarExpansionDisabled()\n" + "public void setStatusBarExpansionDisabled(boolean)\npublic " - + "@android.annotation.SystemApi boolean isNavigateToHomeDisabled()\npublic" - + " void setNavigationHomeDisabled(boolean)\npublic " - + "@android.annotation.SystemApi boolean isNotificationPeekingDisabled()" - + "\npublic void setNotificationPeekingDisabled(boolean)\npublic " + + "@android.annotation.SystemApi boolean isNavigateToHomeDisabled()\n" + + "public void setNavigationHomeDisabled(boolean)\npublic " + + "@android.annotation.SystemApi boolean isNotificationPeekingDisabled()\n" + + "public void setNotificationPeekingDisabled(boolean)\npublic " + "@android.annotation.SystemApi boolean isRecentsDisabled()\npublic " - + "void setRecentsDisabled(boolean)\npublic boolean isBackDisabled()" - + "\npublic void setBackDisabled(boolean)\npublic " + + "void setRecentsDisabled(boolean)\npublic @android.annotation.SystemApi " + + "boolean isBackDisabled()\npublic void setBackDisabled(boolean)\npublic " + "@android.annotation.SystemApi boolean isSearchDisabled()\npublic " + "void setSearchDisabled(boolean)\npublic boolean " - + "areSystemIconsDisabled()\npublic void setSystemIconsDisabled(boolean)\n" - + "public boolean isClockDisabled()\npublic " - + "void setClockDisabled(boolean)\npublic boolean " - + "areNotificationIconsDisabled()\npublic void " - + "setNotificationIconsDisabled(boolean)\npublic boolean " + + "areSystemIconsDisabled()\npublic void setSystemIconsDisabled(boolean)" + + "\npublic boolean isClockDisabled()\npublic " + + "void setClockDisabled(boolean)\npublic " + + "boolean areNotificationIconsDisabled()\npublic " + + "void setNotificationIconsDisabled(boolean)\npublic boolean " + "isNotificationTickerDisabled()\npublic void " + "setNotificationTickerDisabled(boolean)\npublic " + "@android.annotation.TestApi boolean isRotationSuggestionDisabled()\n" + "public void setRotationSuggestionDisabled(boolean)\npublic " - + "@android.annotation.SystemApi boolean areAllComponentsEnabled()\npublic" - + " void setEnableAll()\npublic boolean areAllComponentsDisabled()\n" - + "public void setDisableAll()\npublic @android.annotation.NonNull " + + "@android.annotation.SystemApi boolean areAllComponentsEnabled()\n" + + "public void setEnableAll()\npublic boolean areAllComponentsDisabled()" + + "\npublic void setDisableAll()\npublic @android.annotation.NonNull " + "@java.lang.Override java.lang.String toString()\npublic " + "android.util.Pair<java.lang.Integer,java.lang.Integer> toFlags()\n" + "class DisableInfo extends java.lang.Object implements " diff --git a/core/java/android/net/vcn/VcnManager.java b/core/java/android/net/vcn/VcnManager.java index 6246dd77fd6d..91cdf8d8fcae 100644 --- a/core/java/android/net/vcn/VcnManager.java +++ b/core/java/android/net/vcn/VcnManager.java @@ -124,6 +124,22 @@ public class VcnManager { "vcn_network_selection_ipsec_packet_loss_percent_threshold"; /** + * Key for detecting unusually large increases in IPsec packet sequence numbers. + * + * <p>If the sequence number increases by more than this value within a second, it may indicate + * an intentional leap on the server's downlink. To avoid false positives, the packet loss + * detector will suppress loss reporting. + * + * <p>By default, there's no maximum limit enforced, prioritizing detection of lossy networks. + * To reduce false positives, consider setting an appropriate maximum threshold. + * + * @hide + */ + @NonNull + public static final String VCN_NETWORK_SELECTION_MAX_SEQ_NUM_INCREASE_PER_SECOND_KEY = + "vcn_network_selection_max_seq_num_increase_per_second"; + + /** * Key for the list of timeouts in minute to stop penalizing an underlying network candidate * * @hide @@ -180,6 +196,7 @@ public class VcnManager { VCN_NETWORK_SELECTION_WIFI_EXIT_RSSI_THRESHOLD_KEY, VCN_NETWORK_SELECTION_POLL_IPSEC_STATE_INTERVAL_SECONDS_KEY, VCN_NETWORK_SELECTION_IPSEC_PACKET_LOSS_PERCENT_THRESHOLD_KEY, + VCN_NETWORK_SELECTION_MAX_SEQ_NUM_INCREASE_PER_SECOND_KEY, VCN_NETWORK_SELECTION_PENALTY_TIMEOUT_MINUTES_LIST_KEY, VCN_RESTRICTED_TRANSPORTS_INT_ARRAY_KEY, VCN_SAFE_MODE_TIMEOUT_SECONDS_KEY, diff --git a/core/java/android/net/vcn/flags.aconfig b/core/java/android/net/vcn/flags.aconfig index e64823af84cb..6fde39852844 100644 --- a/core/java/android/net/vcn/flags.aconfig +++ b/core/java/android/net/vcn/flags.aconfig @@ -34,4 +34,14 @@ flag{ namespace: "vcn" description: "Re-evaluate IPsec packet loss on LinkProperties or NetworkCapabilities change" bug: "323238888" +} + +flag{ + name: "handle_seq_num_leap" + namespace: "vcn" + description: "Do not report bad network when there is a suspected sequence number leap" + bug: "332598276" + metadata { + purpose: PURPOSE_BUGFIX + } }
\ No newline at end of file diff --git a/core/java/android/security/FileIntegrityManager.java b/core/java/android/security/FileIntegrityManager.java index 025aac962fb9..478435b1ac5e 100644 --- a/core/java/android/security/FileIntegrityManager.java +++ b/core/java/android/security/FileIntegrityManager.java @@ -20,6 +20,8 @@ import android.annotation.FlaggedApi; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; +import android.annotation.SuppressLint; +import android.annotation.SystemApi; import android.annotation.SystemService; import android.content.Context; import android.os.IInstalld.IFsveritySetupAuthToken; @@ -99,8 +101,11 @@ public final class FileIntegrityManager { * @throws IOException If the operation failed. * * @see <a href="https://www.kernel.org/doc/html/next/filesystems/fsverity.html">Kernel doc</a> + * @hide */ @FlaggedApi(Flags.FLAG_FSVERITY_API) + @SuppressLint("StreamFiles") + @SystemApi public void setupFsVerity(@NonNull File file) throws IOException { if (!file.isAbsolute()) { // fs-verity is to be enabled by installd, which enforces the validation to the @@ -138,8 +143,11 @@ public final class FileIntegrityManager { * @param file The file to measure the fs-verity digest. * @return The fs-verity digest in byte[], null if none. * @see <a href="https://www.kernel.org/doc/html/next/filesystems/fsverity.html">Kernel doc</a> + * @hide */ @FlaggedApi(Flags.FLAG_FSVERITY_API) + @SuppressLint("StreamFiles") + @SystemApi public @Nullable byte[] getFsVerityDigest(@NonNull File file) throws IOException { return VerityUtils.getFsverityDigest(file.getPath()); } diff --git a/core/java/android/tracing/perfetto/DataSource.java b/core/java/android/tracing/perfetto/DataSource.java index c33fa7dffad6..b9ab82cb63a9 100644 --- a/core/java/android/tracing/perfetto/DataSource.java +++ b/core/java/android/tracing/perfetto/DataSource.java @@ -18,10 +18,6 @@ package android.tracing.perfetto; import android.util.proto.ProtoInputStream; -import com.android.internal.annotations.VisibleForTesting; - -import dalvik.annotation.optimization.CriticalNative; - /** * Templated base class meant to be derived by embedders to create a custom data * source. @@ -73,7 +69,8 @@ public abstract class DataSource<DataSourceInstanceType extends DataSourceInstan * @param fun The tracing lambda that will be called with the tracing contexts of each active * tracing instance. */ - public final void trace(TraceFunction<TlsStateType, IncrementalStateType> fun) { + public final void trace( + TraceFunction<DataSourceInstanceType, TlsStateType, IncrementalStateType> fun) { boolean startedIterator = nativePerfettoDsTraceIterateBegin(mNativeObj); if (!startedIterator) { @@ -82,8 +79,10 @@ public abstract class DataSource<DataSourceInstanceType extends DataSourceInstan try { do { - TracingContext<TlsStateType, IncrementalStateType> ctx = - new TracingContext<>(mNativeObj); + int instanceIndex = nativeGetPerfettoDsInstanceIndex(mNativeObj); + + TracingContext<DataSourceInstanceType, TlsStateType, IncrementalStateType> ctx = + new TracingContext<>(this, instanceIndex); fun.trace(ctx); ctx.flush(); @@ -104,9 +103,7 @@ public abstract class DataSource<DataSourceInstanceType extends DataSourceInstan * Override this method to create a custom TlsState object for your DataSource. A new instance * will be created per trace instance per thread. * - * NOTE: Should only be called from native side. */ - @VisibleForTesting public TlsStateType createTlsState(CreateTlsStateArgs<DataSourceInstanceType> args) { return null; } @@ -114,9 +111,8 @@ public abstract class DataSource<DataSourceInstanceType extends DataSourceInstan /** * Override this method to create and use a custom IncrementalState object for your DataSource. * - * NOTE: Should only be called from native side. */ - protected IncrementalStateType createIncrementalState( + public IncrementalStateType createIncrementalState( CreateIncrementalStateArgs<DataSourceInstanceType> args) { return null; } @@ -179,10 +175,8 @@ public abstract class DataSource<DataSourceInstanceType extends DataSourceInstan private static native void nativeReleasePerfettoInstanceLocked( long dataSourcePtr, int dsInstanceIdx); - @CriticalNative private static native boolean nativePerfettoDsTraceIterateBegin(long dataSourcePtr); - @CriticalNative private static native boolean nativePerfettoDsTraceIterateNext(long dataSourcePtr); - @CriticalNative private static native void nativePerfettoDsTraceIterateBreak(long dataSourcePtr); + private static native int nativeGetPerfettoDsInstanceIndex(long dataSourcePtr); } diff --git a/core/java/android/tracing/perfetto/TraceFunction.java b/core/java/android/tracing/perfetto/TraceFunction.java index 13e663d607e9..d8854f9e4d5d 100644 --- a/core/java/android/tracing/perfetto/TraceFunction.java +++ b/core/java/android/tracing/perfetto/TraceFunction.java @@ -19,12 +19,14 @@ package android.tracing.perfetto; /** * The interface for the trace function called from native on a trace call with a context. * + * @param <DataSourceInstanceType> The type of DataSource this tracing context is for. * @param <TlsStateType> The type of the custom TLS state, if any is used. * @param <IncrementalStateType> The type of the custom incremental state, if any is used. * * @hide */ -public interface TraceFunction<TlsStateType, IncrementalStateType> { +public interface TraceFunction<DataSourceInstanceType extends DataSourceInstance, + TlsStateType, IncrementalStateType> { /** * This function will be called synchronously (i.e., always before trace() returns) only if @@ -34,5 +36,5 @@ public interface TraceFunction<TlsStateType, IncrementalStateType> { * * @param ctx the tracing context to trace for in the trace function. */ - void trace(TracingContext<TlsStateType, IncrementalStateType> ctx); + void trace(TracingContext<DataSourceInstanceType, TlsStateType, IncrementalStateType> ctx); } diff --git a/core/java/android/tracing/perfetto/TracingContext.java b/core/java/android/tracing/perfetto/TracingContext.java index 060f9649bb06..6b7df5441427 100644 --- a/core/java/android/tracing/perfetto/TracingContext.java +++ b/core/java/android/tracing/perfetto/TracingContext.java @@ -24,18 +24,25 @@ import java.util.List; /** * Argument passed to the lambda function passed to Trace(). * + * @param <DataSourceInstanceType> The type of the datasource this tracing context is for. * @param <TlsStateType> The type of the custom TLS state, if any is used. * @param <IncrementalStateType> The type of the custom incremental state, if any is used. * * @hide */ -public class TracingContext<TlsStateType, IncrementalStateType> { +public class TracingContext<DataSourceInstanceType extends DataSourceInstance, TlsStateType, + IncrementalStateType> { - private final long mNativeDsPtr; + private final DataSource<DataSourceInstanceType, TlsStateType, IncrementalStateType> + mDataSource; + private final int mInstanceIndex; private final List<ProtoOutputStream> mTracePackets = new ArrayList<>(); - TracingContext(long nativeDsPtr) { - this.mNativeDsPtr = nativeDsPtr; + TracingContext(DataSource<DataSourceInstanceType, TlsStateType, IncrementalStateType> + dataSource, + int instanceIndex) { + this.mDataSource = dataSource; + this.mInstanceIndex = instanceIndex; } /** @@ -61,18 +68,26 @@ public class TracingContext<TlsStateType, IncrementalStateType> { * Stop timeout expires. */ public void flush() { - nativeFlush(mNativeDsPtr, getAndClearAllPendingTracePackets()); + nativeFlush(mDataSource.mNativeObj, getAndClearAllPendingTracePackets()); } /** * Can optionally be used to store custom per-sequence * session data, which is not reset when incremental state is cleared * (e.g. configuration options). - * + *h * @return The TlsState instance for the tracing thread and instance. */ public TlsStateType getCustomTlsState() { - return (TlsStateType) nativeGetCustomTls(mNativeDsPtr); + TlsStateType tlsState = (TlsStateType) nativeGetCustomTls(mDataSource.mNativeObj); + if (tlsState == null) { + final CreateTlsStateArgs<DataSourceInstanceType> args = + new CreateTlsStateArgs<>(mDataSource, mInstanceIndex); + tlsState = mDataSource.createTlsState(args); + nativeSetCustomTls(mDataSource.mNativeObj, tlsState); + } + + return tlsState; } /** @@ -82,7 +97,16 @@ public class TracingContext<TlsStateType, IncrementalStateType> { * @return The current IncrementalState object instance. */ public IncrementalStateType getIncrementalState() { - return (IncrementalStateType) nativeGetIncrementalState(mNativeDsPtr); + IncrementalStateType incrementalState = + (IncrementalStateType) nativeGetIncrementalState(mDataSource.mNativeObj); + if (incrementalState == null) { + final CreateIncrementalStateArgs<DataSourceInstanceType> args = + new CreateIncrementalStateArgs<>(mDataSource, mInstanceIndex); + incrementalState = mDataSource.createIncrementalState(args); + nativeSetIncrementalState(mDataSource.mNativeObj, incrementalState); + } + + return incrementalState; } private byte[][] getAndClearAllPendingTracePackets() { @@ -97,6 +121,10 @@ public class TracingContext<TlsStateType, IncrementalStateType> { } private static native void nativeFlush(long dataSourcePtr, byte[][] packetData); + private static native Object nativeGetCustomTls(long nativeDsPtr); + private static native void nativeSetCustomTls(long nativeDsPtr, Object tlsState); + private static native Object nativeGetIncrementalState(long nativeDsPtr); + private static native void nativeSetIncrementalState(long nativeDsPtr, Object incrementalState); } diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index c95d6ffe6268..a23df799da59 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -951,7 +951,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall private boolean performSurfaceTransaction(ViewRootImpl viewRoot, Translator translator, boolean creating, boolean sizeChanged, boolean hintChanged, boolean relativeZChanged, - Transaction surfaceUpdateTransaction) { + boolean hdrHeadroomChanged, Transaction surfaceUpdateTransaction) { boolean realSizeChanged = false; mSurfaceLock.lock(); @@ -986,7 +986,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall updateBackgroundVisibility(surfaceUpdateTransaction); updateBackgroundColor(surfaceUpdateTransaction); - if (mLimitedHdrEnabled) { + if (mLimitedHdrEnabled && hdrHeadroomChanged) { surfaceUpdateTransaction.setDesiredHdrHeadroom( mBlastSurfaceControl, mHdrHeadroom); } @@ -1203,7 +1203,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall } final boolean realSizeChanged = performSurfaceTransaction(viewRoot, translator, - creating, sizeChanged, hintChanged, relativeZChanged, + creating, sizeChanged, hintChanged, relativeZChanged, hdrHeadroomChanged, surfaceUpdateTransaction); try { diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index cacf0d201b06..ac1f646d9f3f 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -2428,6 +2428,26 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ public static final int FRAME_RATE_CATEGORY_REASON_IDLE = 0x0700_0000; + /** + * This indicates that the frame rate category was chosen because it is currently boosting. + * @hide + */ + public static final int FRAME_RATE_CATEGORY_REASON_BOOST = 0x0800_0000; + + /** + * This indicates that the frame rate category was chosen because it is currently having + * touch boost. + * @hide + */ + public static final int FRAME_RATE_CATEGORY_REASON_TOUCH = 0x0900_0000; + + /** + * This indicates that the frame rate category was chosen because it is currently having + * touch boost. + * @hide + */ + public static final int FRAME_RATE_CATEGORY_REASON_CONFLICTED = 0x0A00_0000; + private static final int FRAME_RATE_CATEGORY_REASON_MASK = 0xFFFF_0000; /** @@ -5742,7 +5762,7 @@ public class View implements Drawable.Callback, KeyEvent.Callback, */ private static final float FRAME_RATE_SIZE_PERCENTAGE_THRESHOLD = 0.07f; - private static final float MAX_FRAME_RATE = 140; + static final float MAX_FRAME_RATE = 140; private static final int INFREQUENT_UPDATE_INTERVAL_MILLIS = 100; private static final int INFREQUENT_UPDATE_COUNTS = 2; @@ -33897,36 +33917,41 @@ public class View implements Drawable.Callback, KeyEvent.Callback, int height = mBottom - mTop; if (viewRootImpl != null && (width != 0 && height != 0)) { - if (mAttachInfo.mViewVelocityApi) { - float velocity = mFrameContentVelocity; - int mask = PFLAG4_HAS_MOVED | PFLAG4_HAS_DRAWN; - float frameRate = 0; - - if (velocity < 0f - && (mPrivateFlags4 & mask) == mask - && mParent instanceof View - && ((View) mParent).mFrameContentVelocity <= 0 - ) { - // This current calculation is very simple. If something on the screen moved, - // then it votes for the highest velocity. If it doesn't move, then return 0. - velocity = Float.POSITIVE_INFINITY; - frameRate = MAX_FRAME_RATE; + if (viewRootImpl.shouldCheckFrameRate(mPreferredFrameRate > 0f)) { + float velocityFrameRate = 0f; + if (mAttachInfo.mViewVelocityApi) { + float velocity = mFrameContentVelocity; + + if (velocity < 0f + && (mPrivateFlags4 & (PFLAG4_HAS_MOVED | PFLAG4_HAS_DRAWN)) == ( + PFLAG4_HAS_MOVED | PFLAG4_HAS_DRAWN) + && mParent instanceof View + && ((View) mParent).mFrameContentVelocity <= 0 + ) { + // This current calculation is very simple. If something on the screen + // moved, then it votes for the highest velocity. + velocityFrameRate = MAX_FRAME_RATE; + } else if (velocity > 0f) { + velocityFrameRate = convertVelocityToFrameRate(velocity); + } } - if (velocity > 0f) { - if (sToolkitFrameRateVelocityMappingReadOnlyFlagValue) { - frameRate = convertVelocityToFrameRate(velocity); + if (velocityFrameRate > 0f || mPreferredFrameRate > 0f) { + int compatibility = FRAME_RATE_COMPATIBILITY_GTE; + float frameRate = velocityFrameRate; + if (mPreferredFrameRate > velocityFrameRate) { + compatibility = FRAME_RATE_COMPATIBILITY_FIXED_SOURCE; + frameRate = mPreferredFrameRate; } - viewRootImpl.votePreferredFrameRate(frameRate, FRAME_RATE_COMPATIBILITY_GTE); - return; + viewRootImpl.votePreferredFrameRate(frameRate, compatibility); } } - if (!willNotDraw() && isDirty()) { + if (!willNotDraw() && isDirty() && viewRootImpl.shouldCheckFrameRateCategory()) { if (sToolkitMetricsForFrameRateDecisionFlagValue) { float sizePercentage = width * height / mAttachInfo.mDisplayPixelCount; viewRootImpl.recordViewPercentage(sizePercentage); } - int frameRateCategory; + int frameRateCategory = FRAME_RATE_CATEGORY_NO_PREFERENCE; if (Float.isNaN(mPreferredFrameRate)) { frameRateCategory = calculateFrameRateCategory(); } else if (mPreferredFrameRate < 0) { @@ -33951,10 +33976,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback, | FRAME_RATE_CATEGORY_REASON_INVALID; } } - } else { - viewRootImpl.votePreferredFrameRate(mPreferredFrameRate, - mFrameRateCompatibility); - return; } int category = frameRateCategory & ~FRAME_RATE_CATEGORY_REASON_MASK; diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 4024e3cd34a2..09f98d7e1d82 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -35,14 +35,18 @@ import static android.view.Surface.FRAME_RATE_CATEGORY_NORMAL; import static android.view.Surface.FRAME_RATE_CATEGORY_NO_PREFERENCE; import static android.view.Surface.FRAME_RATE_COMPATIBILITY_FIXED_SOURCE; import static android.view.Surface.FRAME_RATE_COMPATIBILITY_GTE; +import static android.view.View.FRAME_RATE_CATEGORY_REASON_BOOST; +import static android.view.View.FRAME_RATE_CATEGORY_REASON_CONFLICTED; import static android.view.View.FRAME_RATE_CATEGORY_REASON_IDLE; import static android.view.View.FRAME_RATE_CATEGORY_REASON_INTERMITTENT; import static android.view.View.FRAME_RATE_CATEGORY_REASON_INVALID; import static android.view.View.FRAME_RATE_CATEGORY_REASON_LARGE; import static android.view.View.FRAME_RATE_CATEGORY_REASON_REQUESTED; import static android.view.View.FRAME_RATE_CATEGORY_REASON_SMALL; +import static android.view.View.FRAME_RATE_CATEGORY_REASON_TOUCH; import static android.view.View.FRAME_RATE_CATEGORY_REASON_UNKNOWN; import static android.view.View.FRAME_RATE_CATEGORY_REASON_VELOCITY; +import static android.view.View.MAX_FRAME_RATE; import static android.view.View.PFLAG_DRAW_ANIMATION; import static android.view.View.SYSTEM_UI_FLAG_FULLSCREEN; import static android.view.View.SYSTEM_UI_FLAG_HIDE_NAVIGATION; @@ -235,6 +239,7 @@ import android.view.animation.Interpolator; import android.view.autofill.AutofillManager; import android.view.contentcapture.ContentCaptureManager; import android.view.contentcapture.ContentCaptureSession; +import android.view.flags.Flags; import android.view.inputmethod.ImeTracker; import android.view.inputmethod.InputMethodManager; import android.widget.Scroller; @@ -1149,7 +1154,9 @@ public final class ViewRootImpl implements ViewParent, private static boolean sToolkitFrameRateTypingReadOnlyFlagValue; private static final boolean sToolkitFrameRateViewEnablingReadOnlyFlagValue; private static boolean sToolkitFrameRateVelocityMappingReadOnlyFlagValue = - toolkitFrameRateVelocityMappingReadOnly();; + toolkitFrameRateVelocityMappingReadOnly(); + private static boolean sToolkitEnableInvalidateCheckThreadFlagValue = + Flags.enableInvalidateCheckThread(); static { sToolkitSetFrameRateReadOnlyFlagValue = toolkitSetFrameRateReadOnly(); @@ -2371,8 +2378,9 @@ public final class ViewRootImpl implements ViewParent, @Override public void onDescendantInvalidated(@NonNull View child, @NonNull View descendant) { - // TODO: Re-enable after camera is fixed or consider targetSdk checking this - // checkThread(); + if (sToolkitEnableInvalidateCheckThreadFlagValue) { + checkThread(); + } if ((descendant.mPrivateFlags & PFLAG_DRAW_ANIMATION) != 0) { mIsAnimating = true; } @@ -4191,8 +4199,15 @@ public final class ViewRootImpl implements ViewParent, // For the variable refresh rate project. // We set the preferred frame rate and frame rate category at the end of performTraversals // when the values are applicable. + setCategoryFromCategoryCounts(); setPreferredFrameRate(mPreferredFrameRate); setPreferredFrameRateCategory(mPreferredFrameRateCategory); + if (!mIsFrameRateConflicted) { + mHandler.removeMessages(MSG_FRAME_RATE_SETTING); + mHandler.sendEmptyMessageDelayed(MSG_FRAME_RATE_SETTING, + FRAME_RATE_SETTING_REEVALUATE_TIME); + } + checkIdleness(); mFrameRateCategoryHighCount = mFrameRateCategoryHighCount > 0 ? mFrameRateCategoryHighCount - 1 : mFrameRateCategoryHighCount; mFrameRateCategoryNormalCount = mFrameRateCategoryNormalCount > 0 @@ -4201,7 +4216,6 @@ public final class ViewRootImpl implements ViewParent, ? mFrameRateCategoryLowCount - 1 : mFrameRateCategoryLowCount; mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_DEFAULT; mPreferredFrameRate = -1; - mFrameRateCompatibility = FRAME_RATE_COMPATIBILITY_FIXED_SOURCE; mIsFrameRateConflicted = false; mFrameRateCategoryChangeReason = FRAME_RATE_CATEGORY_REASON_UNKNOWN; } @@ -6630,8 +6644,6 @@ public final class ViewRootImpl implements ViewParent, */ mIsFrameRateBoosting = false; mIsTouchBoosting = false; - setPreferredFrameRateCategory(Math.max(mPreferredFrameRateCategory, - mLastPreferredFrameRateCategory)); break; case MSG_CHECK_INVALIDATION_IDLE: if (!mHasInvalidation && !mIsFrameRateBoosting && !mIsTouchBoosting) { @@ -7677,7 +7689,6 @@ public final class ViewRootImpl implements ViewParent, mWindowAttributes.type)) { // set the frame rate to the maximum value. mIsTouchBoosting = true; - setPreferredFrameRateCategory(mPreferredFrameRateCategory); } /** * We want to lower the refresh rate when MotionEvent.ACTION_UP, @@ -12469,59 +12480,50 @@ public final class ViewRootImpl implements ViewParent, EventLog.writeEvent(LOGTAG_VIEWROOT_DRAW_EVENT, mTag, msg); } + /** + * Sets the mPreferredFrameRateCategory from the high, high_hint, normal, and low counts. + */ + private void setCategoryFromCategoryCounts() { + if (mFrameRateCategoryHighCount > 0) { + mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_HIGH; + } else if (mFrameRateCategoryHighHintCount > 0) { + mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_HIGH_HINT; + } else if (mFrameRateCategoryNormalCount > 0) { + mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_NORMAL; + } else if (mFrameRateCategoryLowCount > 0) { + mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_LOW; + } + } + private void setPreferredFrameRateCategory(int preferredFrameRateCategory) { - if (!shouldSetFrameRateCategory() - || (mFrameRateCompatibility == FRAME_RATE_COMPATIBILITY_GTE - && mPreferredFrameRate > 0 - && sToolkitFrameRateVelocityMappingReadOnlyFlagValue)) { + if (!shouldSetFrameRateCategory()) { return; } - int categoryFromConflictedFrameRates = FRAME_RATE_CATEGORY_DEFAULT; - if (mIsFrameRateConflicted) { - categoryFromConflictedFrameRates = mPreferredFrameRate > 60 - ? FRAME_RATE_CATEGORY_HIGH : FRAME_RATE_CATEGORY_NORMAL; - } - int frameRateCategory = mIsTouchBoosting - ? FRAME_RATE_CATEGORY_HIGH_HINT - : Math.max(preferredFrameRateCategory, categoryFromConflictedFrameRates); + int frameRateCategory; + int frameRateReason; + String view; - // FRAME_RATE_CATEGORY_HIGH has a higher precedence than FRAME_RATE_CATEGORY_HIGH_HINT - // For now, FRAME_RATE_CATEGORY_HIGH_HINT is used for boosting with user interaction. - // FRAME_RATE_CATEGORY_HIGH is for boosting without user interaction - // (e.g., Window Initialization). - if (mIsFrameRateBoosting || mInsetsAnimationRunning - || (mFrameRateCompatibility == FRAME_RATE_COMPATIBILITY_GTE - && mPreferredFrameRate > 0)) { + if (mIsFrameRateBoosting || mInsetsAnimationRunning) { frameRateCategory = FRAME_RATE_CATEGORY_HIGH; - if (mFrameRateCompatibility == FRAME_RATE_COMPATIBILITY_GTE) { - // We've received a velocity, so we'll let the velocity control the - // frame rate unless we receive additional motion events. - mIsTouchBoosting = false; - mFrameRateCategoryChangeReason = FRAME_RATE_CATEGORY_REASON_VELOCITY; - mFrameRateCategoryView = null; - } else { - mFrameRateCategoryChangeReason = FRAME_RATE_CATEGORY_REASON_UNKNOWN; - } + frameRateReason = FRAME_RATE_CATEGORY_REASON_BOOST; + view = null; + } else if (mIsTouchBoosting && preferredFrameRateCategory < FRAME_RATE_CATEGORY_HIGH_HINT) { + frameRateCategory = FRAME_RATE_CATEGORY_HIGH_HINT; + frameRateReason = FRAME_RATE_CATEGORY_REASON_TOUCH; + view = null; + } else { + frameRateCategory = preferredFrameRateCategory; + frameRateReason = mFrameRateCategoryChangeReason; + view = mFrameRateCategoryView; } try { if (frameRateCategory != FRAME_RATE_CATEGORY_DEFAULT && mLastPreferredFrameRateCategory != frameRateCategory) { if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) { - String reason = reasonToString(mFrameRateCategoryChangeReason); - String sourceView = mFrameRateCategoryView == null ? "-" - : mFrameRateCategoryView; - if (preferredFrameRateCategory == FRAME_RATE_CATEGORY_HIGH_HINT) { - reason = "touch boost"; - sourceView = "-"; - } else if (categoryFromConflictedFrameRates == frameRateCategory - && frameRateCategory != preferredFrameRateCategory - && mIsFrameRateConflicted - ) { - reason = "conflict"; - sourceView = "-"; - } + String reason = reasonToString(frameRateReason); + String sourceView = view == null ? "-" : view; String category = categoryToString(frameRateCategory); Trace.traceBegin( Trace.TRACE_TAG_VIEW, "ViewRootImpl#setFrameRateCategory " @@ -12565,24 +12567,21 @@ public final class ViewRootImpl implements ViewParent, case FRAME_RATE_CATEGORY_REASON_VELOCITY -> str = "velocity"; case FRAME_RATE_CATEGORY_REASON_IDLE -> str = "idle"; case FRAME_RATE_CATEGORY_REASON_UNKNOWN -> str = "unknown"; + case FRAME_RATE_CATEGORY_REASON_BOOST -> str = "boost"; + case FRAME_RATE_CATEGORY_REASON_TOUCH -> str = "touch"; + case FRAME_RATE_CATEGORY_REASON_CONFLICTED -> str = "conflicted"; default -> str = String.valueOf(reason); } return str; } private void setPreferredFrameRate(float preferredFrameRate) { - if (!shouldSetFrameRate()) { - return; - } - if (mFrameRateCompatibility == FRAME_RATE_COMPATIBILITY_GTE - && preferredFrameRate > 0 && !sToolkitFrameRateVelocityMappingReadOnlyFlagValue) { - mIsTouchBoosting = false; + if (!shouldSetFrameRate() || preferredFrameRate < 0) { return; } try { - if (mLastPreferredFrameRate != preferredFrameRate - && preferredFrameRate >= 0) { + if (mLastPreferredFrameRate != preferredFrameRate) { if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) { Trace.traceBegin( Trace.TRACE_TAG_VIEW, "ViewRootImpl#setFrameRate " @@ -12591,7 +12590,7 @@ public final class ViewRootImpl implements ViewParent, } if (sToolkitFrameRateFunctionEnablingReadOnlyFlagValue) { mFrameRateTransaction.setFrameRate(mSurfaceControl, preferredFrameRate, - mFrameRateCompatibility).applyAsyncUnsafe(); + mFrameRateCompatibility).applyAsyncUnsafe(); } mLastPreferredFrameRate = preferredFrameRate; } @@ -12602,12 +12601,6 @@ public final class ViewRootImpl implements ViewParent, } } - private void sendDelayedEmptyMessage(int message, int delayedTime) { - mHandler.removeMessages(message); - - mHandler.sendEmptyMessageDelayed(message, delayedTime); - } - private boolean shouldSetFrameRateCategory() { // use toolkitSetFrameRate flag to gate the change return mSurface.isValid() && shouldEnableDvrr(); @@ -12645,28 +12638,34 @@ public final class ViewRootImpl implements ViewParent, case FRAME_RATE_CATEGORY_HIGH -> mFrameRateCategoryHighCount = FRAME_RATE_CATEGORY_COUNT; } - - int oldCategory = mPreferredFrameRateCategory; - // For View that votes NO_PREFERENCE - mPreferredFrameRateCategory = frameRateCategory; - - if (mFrameRateCategoryHighCount > 0) { - mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_HIGH; - } else if (mFrameRateCategoryHighHintCount > 0) { - mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_HIGH_HINT; - } else if (mFrameRateCategoryNormalCount > 0) { - mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_NORMAL; - } else if (mFrameRateCategoryLowCount > 0) { - mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_LOW; - } - mHasInvalidation = true; - checkIdleness(); - if (mPreferredFrameRateCategory != oldCategory - && mPreferredFrameRateCategory == frameRateCategory - ) { + if (frameRateCategory > mPreferredFrameRateCategory) { + mPreferredFrameRateCategory = frameRateCategory; mFrameRateCategoryChangeReason = reason; - mFrameRateCategoryView = view == null ? "null" : view.getClass().getSimpleName(); + mFrameRateCategoryView = view == null ? "-" : view.getClass().getSimpleName(); } + mHasInvalidation = true; + } + + /** + * Returns whether a View should vote for frame rate category. When the category is HIGH + * already, there's no need to calculate the category on the View and vote. + */ + public boolean shouldCheckFrameRateCategory() { + return mPreferredFrameRateCategory < FRAME_RATE_CATEGORY_HIGH; + } + + /** + * Returns whether a View should vote for frame rate. When the maximum frame rate has already + * been voted for, there's no point in calculating and voting for the frame rate. When + * isDirect is false, then it will return false when the velocity-calculated frame rate + * can be avoided. + * @param isDirect true when the frame rate has been set directly on the View or false if + * the calculation is based only on velocity. + */ + public boolean shouldCheckFrameRate(boolean isDirect) { + return mPreferredFrameRate < MAX_FRAME_RATE + || (!isDirect && !sToolkitFrameRateVelocityMappingReadOnlyFlagValue + && mPreferredFrameRateCategory < FRAME_RATE_CATEGORY_HIGH); } /** @@ -12692,24 +12691,44 @@ public final class ViewRootImpl implements ViewParent, if (frameRate <= 0) { return; } + if (frameRateCompatibility == FRAME_RATE_COMPATIBILITY_GTE) { + mIsTouchBoosting = false; + if (!sToolkitFrameRateVelocityMappingReadOnlyFlagValue) { + mPreferredFrameRateCategory = FRAME_RATE_CATEGORY_HIGH; + mFrameRateCategoryHighCount = FRAME_RATE_CATEGORY_COUNT; + mFrameRateCategoryChangeReason = FRAME_RATE_CATEGORY_REASON_VELOCITY; + mFrameRateCategoryView = null; + return; + } + } + float nextFrameRate; + int nextFrameRateCompatibility; + if (frameRate > mPreferredFrameRate) { + nextFrameRate = frameRate; + nextFrameRateCompatibility = frameRateCompatibility; + } else { + nextFrameRate = mPreferredFrameRate; + nextFrameRateCompatibility = mFrameRateCompatibility; + } + if (mPreferredFrameRate > 0 && mPreferredFrameRate % frameRate != 0 && frameRate % mPreferredFrameRate != 0) { mIsFrameRateConflicted = true; - mFrameRateCompatibility = FRAME_RATE_COMPATIBILITY_FIXED_SOURCE; - } - if (frameRate > mPreferredFrameRate) { - mFrameRateCompatibility = frameRateCompatibility; + if (nextFrameRate > 60 && mFrameRateCategoryHighCount != FRAME_RATE_CATEGORY_COUNT) { + mFrameRateCategoryHighCount = FRAME_RATE_CATEGORY_COUNT; + mFrameRateCategoryChangeReason = FRAME_RATE_CATEGORY_REASON_CONFLICTED; + mFrameRateCategoryView = null; + } else if (mFrameRateCategoryHighCount == 0 && mFrameRateCategoryHighHintCount == 0 + && mFrameRateCategoryNormalCount < FRAME_RATE_CATEGORY_COUNT) { + mFrameRateCategoryNormalCount = FRAME_RATE_CATEGORY_COUNT; + mFrameRateCategoryChangeReason = FRAME_RATE_CATEGORY_REASON_CONFLICTED; + mFrameRateCategoryView = null; + } } - mPreferredFrameRate = Math.max(mPreferredFrameRate, frameRate); + mPreferredFrameRate = nextFrameRate; + mFrameRateCompatibility = nextFrameRateCompatibility; mHasInvalidation = true; - - if (!mIsFrameRateConflicted) { - mHandler.removeMessages(MSG_FRAME_RATE_SETTING); - mHandler.sendEmptyMessageDelayed(MSG_FRAME_RATE_SETTING, - FRAME_RATE_SETTING_REEVALUATE_TIME); - } - checkIdleness(); } /** @@ -12779,7 +12798,6 @@ public final class ViewRootImpl implements ViewParent, private void boostFrameRate(int boostTimeOut) { mIsFrameRateBoosting = true; - setPreferredFrameRateCategory(mPreferredFrameRateCategory); mHandler.removeMessages(MSG_TOUCH_BOOST_TIMEOUT); mHandler.sendEmptyMessageDelayed(MSG_TOUCH_BOOST_TIMEOUT, boostTimeOut); diff --git a/core/java/android/view/flags/view_flags.aconfig b/core/java/android/view/flags/view_flags.aconfig index c482f8be7315..486c2ab6b6ab 100644 --- a/core/java/android/view/flags/view_flags.aconfig +++ b/core/java/android/view/flags/view_flags.aconfig @@ -50,3 +50,11 @@ flag { description: "Enable default arrow icon when hovering on buttons or clickable widgets." bug: "299269803" } + +flag { + name: "enable_invalidate_check_thread" + namespace: "toolkit" + description: "Enable checkThread call in ViewRootImpl#onDescendentInvalidated" + bug: "333752000" + is_fixed_read_only: true +} diff --git a/core/java/android/window/flags/lse_desktop_experience.aconfig b/core/java/android/window/flags/lse_desktop_experience.aconfig index 9524a6ec8f7b..65e5f1a32a53 100644 --- a/core/java/android/window/flags/lse_desktop_experience.aconfig +++ b/core/java/android/window/flags/lse_desktop_experience.aconfig @@ -49,3 +49,10 @@ flag { description: "Shows running apps in Desktop Mode Taskbar" bug: "332504528" } + +flag { + name: "enable_desktop_windowing_wallpaper_activity" + namespace: "lse_desktop_experience" + description: "Enables desktop wallpaper activity to show wallpaper in the desktop mode" + bug: "309014605" +} diff --git a/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java b/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java index fca4e91a8ac3..b6558cb44f6a 100644 --- a/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java +++ b/core/java/com/android/internal/protolog/PerfettoProtoLogImpl.java @@ -440,6 +440,7 @@ public class PerfettoProtoLogImpl implements IProtoLog { } private int internStacktraceString(TracingContext< + ProtoLogDataSource.Instance, ProtoLogDataSource.TlsState, ProtoLogDataSource.IncrementalState> ctx, String stacktrace) { @@ -449,7 +450,8 @@ public class PerfettoProtoLogImpl implements IProtoLog { } private int internStringArg( - TracingContext<ProtoLogDataSource.TlsState, ProtoLogDataSource.IncrementalState> ctx, + TracingContext<ProtoLogDataSource.Instance, ProtoLogDataSource.TlsState, + ProtoLogDataSource.IncrementalState> ctx, String string ) { final ProtoLogDataSource.IncrementalState incrementalState = ctx.getIncrementalState(); @@ -458,7 +460,8 @@ public class PerfettoProtoLogImpl implements IProtoLog { } private int internString( - TracingContext<ProtoLogDataSource.TlsState, ProtoLogDataSource.IncrementalState> ctx, + TracingContext<ProtoLogDataSource.Instance, ProtoLogDataSource.TlsState, + ProtoLogDataSource.IncrementalState> ctx, Map<String, Integer> internMap, long fieldId, String string diff --git a/core/jni/android_tracing_PerfettoDataSource.cpp b/core/jni/android_tracing_PerfettoDataSource.cpp index 5c7b470b6520..f82ebfe8c947 100644 --- a/core/jni/android_tracing_PerfettoDataSource.cpp +++ b/core/jni/android_tracing_PerfettoDataSource.cpp @@ -93,49 +93,6 @@ jobject PerfettoDataSource::newInstance(JNIEnv* env, void* ds_config, size_t ds_ return instance; } -jobject PerfettoDataSource::createTlsStateGlobalRef(JNIEnv* env, PerfettoDsInstanceIndex inst_id) { - ScopedLocalRef<jobject> args(env, - env->NewObject(gCreateTlsStateArgsClassInfo.clazz, - gCreateTlsStateArgsClassInfo.init, mJavaDataSource, - inst_id)); - - ScopedLocalRef<jobject> tslState(env, - env->CallObjectMethod(mJavaDataSource, - gPerfettoDataSourceClassInfo - .createTlsState, - args.get())); - - if (env->ExceptionCheck()) { - LOGE_EX(env); - env->ExceptionClear(); - LOG_ALWAYS_FATAL("Failed to create new Java Perfetto incremental state"); - } - - return env->NewGlobalRef(tslState.get()); -} - -jobject PerfettoDataSource::createIncrementalStateGlobalRef(JNIEnv* env, - PerfettoDsInstanceIndex inst_id) { - ScopedLocalRef<jobject> args(env, - env->NewObject(gCreateIncrementalStateArgsClassInfo.clazz, - gCreateIncrementalStateArgsClassInfo.init, - mJavaDataSource, inst_id)); - - ScopedLocalRef<jobject> incrementalState(env, - env->CallObjectMethod(mJavaDataSource, - gPerfettoDataSourceClassInfo - .createIncrementalState, - args.get())); - - if (env->ExceptionCheck()) { - LOGE_EX(env); - env->ExceptionClear(); - LOG_ALWAYS_FATAL("Failed to create Java Perfetto incremental state"); - } - - return env->NewGlobalRef(incrementalState.get()); -} - bool PerfettoDataSource::TraceIterateBegin() { if (gInIteration) { return false; @@ -177,6 +134,15 @@ void PerfettoDataSource::TraceIterateBreak() { gInIteration = false; } +PerfettoDsInstanceIndex PerfettoDataSource::GetInstanceIndex() { + if (!gInIteration) { + LOG_ALWAYS_FATAL("Tried calling GetInstanceIndex outside of a tracer iteration."); + return -1; + } + + return gIterator.impl.inst_id; +} + jobject PerfettoDataSource::GetCustomTls() { if (!gInIteration) { LOG_ALWAYS_FATAL("Tried getting CustomTls outside of a tracer iteration."); @@ -189,6 +155,18 @@ jobject PerfettoDataSource::GetCustomTls() { return tls_state->jobj; } +void PerfettoDataSource::SetCustomTls(jobject tlsState) { + if (!gInIteration) { + LOG_ALWAYS_FATAL("Tried getting CustomTls outside of a tracer iteration."); + return; + } + + TlsState* tls_state = + reinterpret_cast<TlsState*>(PerfettoDsGetCustomTls(&dataSource, &gIterator)); + + tls_state->jobj = tlsState; +} + jobject PerfettoDataSource::GetIncrementalState() { if (!gInIteration) { LOG_ALWAYS_FATAL("Tried getting IncrementalState outside of a tracer iteration."); @@ -201,6 +179,18 @@ jobject PerfettoDataSource::GetIncrementalState() { return incr_state->jobj; } +void PerfettoDataSource::SetIncrementalState(jobject incrementalState) { + if (!gInIteration) { + LOG_ALWAYS_FATAL("Tried getting IncrementalState outside of a tracer iteration."); + return; + } + + IncrementalState* incr_state = reinterpret_cast<IncrementalState*>( + PerfettoDsGetIncrementalState(&dataSource, &gIterator)); + + incr_state->jobj = incrementalState; +} + void PerfettoDataSource::WritePackets(JNIEnv* env, jobjectArray packets) { if (!gInIteration) { LOG_ALWAYS_FATAL("Tried writing packets outside of a tracer iteration."); @@ -211,7 +201,7 @@ void PerfettoDataSource::WritePackets(JNIEnv* env, jobjectArray packets) { for (int i = 0; i < packets_count; i++) { jbyteArray packet_proto_buffer = (jbyteArray)env->GetObjectArrayElement(packets, i); - jbyte* raw_proto_buffer = env->GetByteArrayElements(packet_proto_buffer, 0); + jbyte* raw_proto_buffer = env->GetByteArrayElements(packet_proto_buffer, nullptr); int buffer_size = env->GetArrayLength(packet_proto_buffer); struct PerfettoDsRootTracePacket trace_packet; @@ -219,6 +209,8 @@ void PerfettoDataSource::WritePackets(JNIEnv* env, jobjectArray packets) { PerfettoPbMsgAppendBytes(&trace_packet.msg.msg, (const uint8_t*)raw_proto_buffer, buffer_size); PerfettoDsTracerPacketEnd(&gIterator, &trace_packet); + + env->ReleaseByteArrayElements(packet_proto_buffer, raw_proto_buffer, 0 /* default mode */); } } @@ -264,7 +256,7 @@ void nativeFlushAll(JNIEnv* env, jclass clazz, jlong ptr) { } void nativeRegisterDataSource(JNIEnv* env, jclass clazz, jlong datasource_ptr, - int buffer_exhausted_policy) { + jint buffer_exhausted_policy) { sp<PerfettoDataSource> datasource = reinterpret_cast<PerfettoDataSource*>(datasource_ptr); struct PerfettoDsParams params = PerfettoDsParamsDefault(); @@ -291,13 +283,8 @@ void nativeRegisterDataSource(JNIEnv* env, jclass clazz, jlong datasource_ptr, params.on_create_tls_cb = [](struct PerfettoDsImpl* ds_impl, PerfettoDsInstanceIndex inst_id, struct PerfettoDsTracerImpl* tracer, void* user_arg) -> void* { - JNIEnv* env = GetOrAttachJNIEnvironment(gVm, JNI_VERSION_1_6); - - auto* datasource = reinterpret_cast<PerfettoDataSource*>(user_arg); - - jobject java_tls_state = datasource->createTlsStateGlobalRef(env, inst_id); - - auto* tls_state = new TlsState(java_tls_state); + // Populated later and only if required by the java side + auto* tls_state = new TlsState(NULL); return static_cast<void*>(tls_state); }; @@ -306,18 +293,16 @@ void nativeRegisterDataSource(JNIEnv* env, jclass clazz, jlong datasource_ptr, TlsState* tls_state = reinterpret_cast<TlsState*>(ptr); - env->DeleteGlobalRef(tls_state->jobj); + if (tls_state->jobj != NULL) { + env->DeleteGlobalRef(tls_state->jobj); + } delete tls_state; }; params.on_create_incr_cb = [](struct PerfettoDsImpl* ds_impl, PerfettoDsInstanceIndex inst_id, struct PerfettoDsTracerImpl* tracer, void* user_arg) -> void* { - JNIEnv* env = GetOrAttachJNIEnvironment(gVm, JNI_VERSION_1_6); - - auto* datasource = reinterpret_cast<PerfettoDataSource*>(user_arg); - jobject java_incr_state = datasource->createIncrementalStateGlobalRef(env, inst_id); - - auto* incr_state = new IncrementalState(java_incr_state); + // Populated later and only if required by the java side + auto* incr_state = new IncrementalState(NULL); return static_cast<void*>(incr_state); }; @@ -326,7 +311,9 @@ void nativeRegisterDataSource(JNIEnv* env, jclass clazz, jlong datasource_ptr, IncrementalState* incr_state = reinterpret_cast<IncrementalState*>(ptr); - env->DeleteGlobalRef(incr_state->jobj); + if (incr_state->jobj != NULL) { + env->DeleteGlobalRef(incr_state->jobj); + } delete incr_state; }; @@ -386,31 +373,49 @@ void nativeReleasePerfettoInstanceLocked(JNIEnv* /* env */, jclass /* clazz */, PerfettoDsImplReleaseInstanceLocked(datasource->dataSource.impl, instance_idx); } -bool nativePerfettoDsTraceIterateBegin(jlong dataSourcePtr) { +bool nativePerfettoDsTraceIterateBegin(JNIEnv* /* env */, jclass /* clazz */, jlong dataSourcePtr) { sp<PerfettoDataSource> datasource = reinterpret_cast<PerfettoDataSource*>(dataSourcePtr); return datasource->TraceIterateBegin(); } -bool nativePerfettoDsTraceIterateNext(jlong dataSourcePtr) { +bool nativePerfettoDsTraceIterateNext(JNIEnv* /* env */, jclass /* clazz */, jlong dataSourcePtr) { sp<PerfettoDataSource> datasource = reinterpret_cast<PerfettoDataSource*>(dataSourcePtr); return datasource->TraceIterateNext(); } -void nativePerfettoDsTraceIterateBreak(jlong dataSourcePtr) { +void nativePerfettoDsTraceIterateBreak(JNIEnv* /* env */, jclass /* clazz */, jlong dataSourcePtr) { sp<PerfettoDataSource> datasource = reinterpret_cast<PerfettoDataSource*>(dataSourcePtr); return datasource->TraceIterateBreak(); } +jint nativeGetPerfettoDsInstanceIndex(JNIEnv* /* env */, jclass /* clazz */, jlong dataSourcePtr) { + sp<PerfettoDataSource> datasource = reinterpret_cast<PerfettoDataSource*>(dataSourcePtr); + return (jint)datasource->GetInstanceIndex(); +} + jobject nativeGetCustomTls(JNIEnv* /* env */, jclass /* clazz */, jlong dataSourcePtr) { sp<PerfettoDataSource> datasource = reinterpret_cast<PerfettoDataSource*>(dataSourcePtr); return datasource->GetCustomTls(); } +void nativeSetCustomTls(JNIEnv* env, jclass /* clazz */, jlong dataSourcePtr, jobject tlsState) { + sp<PerfettoDataSource> datasource = reinterpret_cast<PerfettoDataSource*>(dataSourcePtr); + tlsState = env->NewGlobalRef(tlsState); + return datasource->SetCustomTls(tlsState); +} + jobject nativeGetIncrementalState(JNIEnv* /* env */, jclass /* clazz */, jlong dataSourcePtr) { sp<PerfettoDataSource> datasource = reinterpret_cast<PerfettoDataSource*>(dataSourcePtr); return datasource->GetIncrementalState(); } +void nativeSetIncrementalState(JNIEnv* env, jclass /* clazz */, jlong dataSourcePtr, + jobject incrementalState) { + sp<PerfettoDataSource> datasource = reinterpret_cast<PerfettoDataSource*>(dataSourcePtr); + incrementalState = env->NewGlobalRef(incrementalState); + return datasource->SetIncrementalState(incrementalState); +} + const JNINativeMethod gMethods[] = { /* name, signature, funcPtr */ {"nativeCreate", "(Landroid/tracing/perfetto/DataSource;Ljava/lang/String;)J", @@ -425,13 +430,16 @@ const JNINativeMethod gMethods[] = { {"nativePerfettoDsTraceIterateBegin", "(J)Z", (void*)nativePerfettoDsTraceIterateBegin}, {"nativePerfettoDsTraceIterateNext", "(J)Z", (void*)nativePerfettoDsTraceIterateNext}, - {"nativePerfettoDsTraceIterateBreak", "(J)V", (void*)nativePerfettoDsTraceIterateBreak}}; + {"nativePerfettoDsTraceIterateBreak", "(J)V", (void*)nativePerfettoDsTraceIterateBreak}, + {"nativeGetPerfettoDsInstanceIndex", "(J)I", (void*)nativeGetPerfettoDsInstanceIndex}}; const JNINativeMethod gMethodsTracingContext[] = { /* name, signature, funcPtr */ {"nativeFlush", "(J[[B)V", (void*)nativeFlush}, {"nativeGetCustomTls", "(J)Ljava/lang/Object;", (void*)nativeGetCustomTls}, {"nativeGetIncrementalState", "(J)Ljava/lang/Object;", (void*)nativeGetIncrementalState}, + {"nativeSetCustomTls", "(JLjava/lang/Object;)V", (void*)nativeSetCustomTls}, + {"nativeSetIncrementalState", "(JLjava/lang/Object;)V", (void*)nativeSetIncrementalState}, }; int register_android_tracing_PerfettoDataSource(JNIEnv* env) { diff --git a/core/jni/android_tracing_PerfettoDataSource.h b/core/jni/android_tracing_PerfettoDataSource.h index 209de29f17d6..fe151844da1b 100644 --- a/core/jni/android_tracing_PerfettoDataSource.h +++ b/core/jni/android_tracing_PerfettoDataSource.h @@ -44,16 +44,16 @@ public: jobject newInstance(JNIEnv* env, void* ds_config, size_t ds_config_size, PerfettoDsInstanceIndex inst_id); - jobject createTlsStateGlobalRef(JNIEnv* env, PerfettoDsInstanceIndex inst_id); - jobject createIncrementalStateGlobalRef(JNIEnv* env, PerfettoDsInstanceIndex inst_id); - bool TraceIterateBegin(); bool TraceIterateNext(); void TraceIterateBreak(); + PerfettoDsInstanceIndex GetInstanceIndex(); void WritePackets(JNIEnv* env, jobjectArray packets); jobject GetCustomTls(); + void SetCustomTls(jobject); jobject GetIncrementalState(); + void SetIncrementalState(jobject); void flushAll(); diff --git a/core/jni/com_android_internal_content_FileSystemUtils.cpp b/core/jni/com_android_internal_content_FileSystemUtils.cpp index 4bd2d72a1eb4..01920de88496 100644 --- a/core/jni/com_android_internal_content_FileSystemUtils.cpp +++ b/core/jni/com_android_internal_content_FileSystemUtils.cpp @@ -42,7 +42,11 @@ namespace android { bool punchHoles(const char *filePath, const uint64_t offset, const std::vector<Elf64_Phdr> &programHeaders) { struct stat64 beforePunch; - lstat64(filePath, &beforePunch); + if (int result = lstat64(filePath, &beforePunch); result != 0) { + ALOGE("lstat64 failed for filePath %s, error:%d", filePath, errno); + return false; + } + uint64_t blockSize = beforePunch.st_blksize; IF_ALOGD() { ALOGD("Total number of LOAD segments %zu", programHeaders.size()); @@ -152,7 +156,10 @@ bool punchHoles(const char *filePath, const uint64_t offset, IF_ALOGD() { struct stat64 afterPunch; - lstat64(filePath, &afterPunch); + if (int result = lstat64(filePath, &afterPunch); result != 0) { + ALOGD("lstat64 failed for filePath %s, error:%d", filePath, errno); + return false; + } ALOGD("Size after punching holes st_blocks: %" PRIu64 ", st_blksize: %ld, st_size: %" PRIu64 "", afterPunch.st_blocks, afterPunch.st_blksize, @@ -177,7 +184,7 @@ bool punchHolesInElf64(const char *filePath, const uint64_t offset) { // only consider elf64 for punching holes if (ehdr.e_ident[EI_CLASS] != ELFCLASS64) { - ALOGE("Provided file is not ELF64"); + ALOGW("Provided file is not ELF64"); return false; } @@ -215,4 +222,108 @@ bool punchHolesInElf64(const char *filePath, const uint64_t offset) { return punchHoles(filePath, offset, programHeaders); } +bool punchHolesInZip(const char *filePath, uint64_t offset, uint16_t extraFieldLen) { + android::base::unique_fd fd(open(filePath, O_RDWR | O_CLOEXEC)); + if (!fd.ok()) { + ALOGE("Can't open file to punch %s", filePath); + return false; + } + + struct stat64 beforePunch; + if (int result = lstat64(filePath, &beforePunch); result != 0) { + ALOGE("lstat64 failed for filePath %s, error:%d", filePath, errno); + return false; + } + + uint64_t blockSize = beforePunch.st_blksize; + IF_ALOGD() { + ALOGD("Extra field length: %hu, Size before punching holes st_blocks: %" PRIu64 + ", st_blksize: %ld, st_size: %" PRIu64 "", + extraFieldLen, beforePunch.st_blocks, beforePunch.st_blksize, + static_cast<uint64_t>(beforePunch.st_size)); + } + + if (extraFieldLen < blockSize) { + ALOGD("Skipping punching apk as extra field length is less than block size"); + return false; + } + + // content is preceded by extra field. Zip offset is offset of exact content. + // move back by extraFieldLen so that scan can be started at start of extra field. + uint64_t extraFieldStart; + if (__builtin_sub_overflow(offset, extraFieldLen, &extraFieldStart)) { + ALOGE("Overflow occurred when calculating start of extra field"); + return false; + } + + constexpr uint64_t kMaxSize = 64 * 1024; + // Use malloc to gracefully handle any oom conditions + std::unique_ptr<uint8_t, decltype(&free)> buffer(static_cast<uint8_t *>(malloc(kMaxSize)), + &free); + if (buffer == nullptr) { + ALOGE("Failed to allocate read buffer"); + return false; + } + + // Read the entire extra fields at once and punch file according to zero stretches. + if (!ReadFullyAtOffset(fd, buffer.get(), extraFieldLen, extraFieldStart)) { + ALOGE("Failed to read extra field content"); + return false; + } + + IF_ALOGD() { + ALOGD("Extra field length: %hu content near offset: %s", extraFieldLen, + HexString(buffer.get(), extraFieldLen).c_str()); + } + + uint64_t currentSize = 0; + while (currentSize < extraFieldLen) { + uint64_t end = currentSize; + // find zero ranges + while (end < extraFieldLen && *(buffer.get() + end) == 0) { + ++end; + } + + uint64_t punchLen; + if (__builtin_sub_overflow(end, currentSize, &punchLen)) { + ALOGW("Overflow occurred when calculating punching length"); + return false; + } + + // Don't punch for every stretch of zero which is found + if (punchLen > blockSize) { + uint64_t punchOffset; + if (__builtin_add_overflow(extraFieldStart, currentSize, &punchOffset)) { + ALOGW("Overflow occurred when calculating punch start offset"); + return false; + } + + ALOGD("Punching hole in apk start: %" PRIu64 " len:%" PRIu64 "", punchOffset, punchLen); + + // Punch hole for this entire stretch. + int result = fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, punchOffset, + punchLen); + if (result < 0) { + ALOGE("fallocate failed to punch hole inside apk, error:%d", errno); + return false; + } + } + currentSize = end; + ++currentSize; + } + + IF_ALOGD() { + struct stat64 afterPunch; + if (int result = lstat64(filePath, &afterPunch); result != 0) { + ALOGD("lstat64 failed for filePath %s, error:%d", filePath, errno); + return false; + } + ALOGD("punchHolesInApk:: Size after punching holes st_blocks: %" PRIu64 + ", st_blksize: %ld, st_size: %" PRIu64 "", + afterPunch.st_blocks, afterPunch.st_blksize, + static_cast<uint64_t>(afterPunch.st_size)); + } + return true; +} + }; // namespace android diff --git a/core/jni/com_android_internal_content_FileSystemUtils.h b/core/jni/com_android_internal_content_FileSystemUtils.h index a6b145c690d1..52445e2b4229 100644 --- a/core/jni/com_android_internal_content_FileSystemUtils.h +++ b/core/jni/com_android_internal_content_FileSystemUtils.h @@ -28,4 +28,11 @@ namespace android { */ bool punchHolesInElf64(const char* filePath, uint64_t offset); +/* + * This function punches holes in zero segments of Apk file which are introduced during the + * alignment. Alignment tools add padding inside of extra field in local file header. punch holes in + * extra field for zero stretches till the actual file content. + */ +bool punchHolesInZip(const char* filePath, uint64_t offset, uint16_t extraFieldLen); + } // namespace android
\ No newline at end of file diff --git a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp index faa83f8017f7..9b8dab78b342 100644 --- a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp +++ b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp @@ -28,6 +28,7 @@ #include <stdlib.h> #include <string.h> #include <sys/stat.h> +#include <sys/statfs.h> #include <sys/types.h> #include <time.h> #include <unistd.h> @@ -145,8 +146,9 @@ copyFileIfChanged(JNIEnv *env, void* arg, ZipFileRO* zipFile, ZipEntryRO zipEntr uint16_t method; off64_t offset; - - if (!zipFile->getEntryInfo(zipEntry, &method, &uncompLen, nullptr, &offset, &when, &crc)) { + uint16_t extraFieldLength; + if (!zipFile->getEntryInfo(zipEntry, &method, &uncompLen, nullptr, &offset, &when, &crc, + &extraFieldLength)) { ALOGE("Couldn't read zip entry info\n"); return INSTALL_FAILED_INVALID_APK; } @@ -177,6 +179,12 @@ copyFileIfChanged(JNIEnv *env, void* arg, ZipFileRO* zipFile, ZipEntryRO zipEntr "%" PRIu64 "", fileName, zipFile->getZipFileName(), offset); } + + // if extra field for this zip file is present with some length, possibility is that it is + // padding added for zip alignment. Punch holes there too. + if (!punchHolesInZip(zipFile->getZipFileName(), offset, extraFieldLength)) { + ALOGW("Failed to punch apk : %s at extra field", zipFile->getZipFileName()); + } #endif // ENABLE_PUNCH_HOLES return INSTALL_SUCCEEDED; @@ -279,6 +287,25 @@ copyFileIfChanged(JNIEnv *env, void* arg, ZipFileRO* zipFile, ZipEntryRO zipEntr return INSTALL_FAILED_CONTAINER_ERROR; } +#ifdef ENABLE_PUNCH_HOLES + // punch extracted elf files as well. This will fail where compression is on (like f2fs) but it + // will be useful for ext4 based systems + struct statfs64 fsInfo; + int result = statfs64(localFileName, &fsInfo); + if (result < 0) { + ALOGW("Failed to stat file :%s", localFileName); + } + + if (result == 0 && fsInfo.f_type == EXT4_SUPER_MAGIC) { + ALOGD("Punching extracted elf file %s on fs:%" PRIu64 "", fileName, + static_cast<uint64_t>(fsInfo.f_type)); + if (!punchHolesInElf64(localFileName, 0)) { + ALOGW("Failed to punch extracted elf file :%s from apk : %s", fileName, + zipFile->getZipFileName()); + } + } +#endif // ENABLE_PUNCH_HOLES + ALOGV("Successfully moved %s to %s\n", localTmpFileName, localFileName); return INSTALL_SUCCEEDED; diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index ab714ad7d807..f55f3c7c5820 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -5890,7 +5890,7 @@ <!-- Allows an application to subscribe to notifications about the nearby devices' presence status change base on the UUIDs. <p>Not for use by third-party applications.</p> - @FlaggedApi("android.companion.flags.device_presence") + @FlaggedApi("android.companion.device_presence") --> <permission android:name="android.permission.REQUEST_OBSERVE_DEVICE_UUID_PRESENCE" android:protectionLevel="signature|privileged" /> @@ -8182,6 +8182,15 @@ <permission android:name="android.permission.SCREEN_TIMEOUT_OVERRIDE" android:protectionLevel="signature" /> + <!-- @SystemApi + @FlaggedApi("android.security.fsverity_api") + Allows app to setup fs-verity through FileIntegrityManager. + <p>Protection level: signature|privileged + @hide + --> + <permission android:name="android.permission.SETUP_FSVERITY" + android:protectionLevel="signature|privileged"/> + <!-- Attribution for Geofencing service. --> <attribution android:tag="GeofencingService" android:label="@string/geofencing_service"/> <!-- Attribution for Country Detector. --> diff --git a/core/res/res/layout/notification_template_header.xml b/core/res/res/layout/notification_template_header.xml index be1c939f0ff8..6f06d803fe6f 100644 --- a/core/res/res/layout/notification_template_header.xml +++ b/core/res/res/layout/notification_template_header.xml @@ -80,6 +80,7 @@ android:layout_height="match_parent" android:layout_alignParentStart="true" android:importantForAccessibility="no" + android:focusable="false" /> <include layout="@layout/notification_expand_button" diff --git a/core/res/res/layout/notification_template_material_base.xml b/core/res/res/layout/notification_template_material_base.xml index 710a70a32955..64227d86c616 100644 --- a/core/res/res/layout/notification_template_material_base.xml +++ b/core/res/res/layout/notification_template_material_base.xml @@ -55,6 +55,7 @@ android:layout_height="match_parent" android:layout_gravity="start" android:importantForAccessibility="no" + android:focusable="false" /> <LinearLayout diff --git a/core/res/res/layout/notification_template_material_media.xml b/core/res/res/layout/notification_template_material_media.xml index df32d30918c8..8a94c48e8f58 100644 --- a/core/res/res/layout/notification_template_material_media.xml +++ b/core/res/res/layout/notification_template_material_media.xml @@ -54,6 +54,7 @@ android:layout_height="match_parent" android:layout_gravity="start" android:importantForAccessibility="no" + android:focusable="false" /> <LinearLayout diff --git a/core/res/res/layout/notification_template_material_messaging.xml b/core/res/res/layout/notification_template_material_messaging.xml index 3e82bd1814c6..a83d923933f3 100644 --- a/core/res/res/layout/notification_template_material_messaging.xml +++ b/core/res/res/layout/notification_template_material_messaging.xml @@ -67,6 +67,7 @@ android:layout_height="match_parent" android:layout_gravity="start" android:importantForAccessibility="no" + android:focusable="false" /> <!-- diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml index 58ec95ab6c25..185c3c655f04 100644 --- a/core/res/res/values-ar/strings.xml +++ b/core/res/res/values-ar/strings.xml @@ -195,10 +195,10 @@ <string name="low_memory" product="default" msgid="2539532364144025569">"مساحة تخزين الهاتف ممتلئة. احذف بعض الملفات لإخلاء مساحة."</string> <string name="ssl_ca_cert_warning" msgid="7233573909730048571">"{count,plural, =1{تم تثبيت مرجع التصديق.}zero{تم تثبيت مراجع التصديق.}two{تم تثبيت مرجعَي التصديق.}few{تم تثبيت مراجع التصديق.}many{تم تثبيت مراجع التصديق.}other{تم تثبيت مراجع التصديق.}}"</string> <string name="ssl_ca_cert_noti_by_unknown" msgid="4961102218216815242">"بواسطة جهة خارجية غير معلومة"</string> - <string name="ssl_ca_cert_noti_by_administrator" msgid="4564941950768783879">"بواسطة مشرف الملف الشخصي للعمل"</string> + <string name="ssl_ca_cert_noti_by_administrator" msgid="4564941950768783879">"بواسطة مشرف ملف العمل"</string> <string name="ssl_ca_cert_noti_managed" msgid="217337232273211674">"بواسطة <xliff:g id="MANAGING_DOMAIN">%s</xliff:g>"</string> - <string name="work_profile_deleted" msgid="5891181538182009328">"تم حذف الملف الشخصي للعمل."</string> - <string name="work_profile_deleted_details" msgid="3773706828364418016">"تطبيق المشرف للملف الشخصي للعمل مفقود أو تالف لذا تم حذف الملف الشخصي للعمل والبيانات ذات الصلة. اتصل بالمشرف للحصول على المساعدة."</string> + <string name="work_profile_deleted" msgid="5891181538182009328">"تم حذف ملف العمل."</string> + <string name="work_profile_deleted_details" msgid="3773706828364418016">"تطبيق المشرف لملف العمل مفقود أو تالف لذا تم حذف ملف العمل والبيانات ذات الصلة. اتصل بالمشرف للحصول على المساعدة."</string> <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"لم يعد ملفك الشخصي للعمل متاحًا على هذا الجهاز"</string> <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"تم إجراء محاولات كثيرة جدًا لإدخال كلمة المرور"</string> <string name="device_ownership_relinquished" msgid="4080886992183195724">"تنازل المشرف عن الجهاز للاستخدام الشخصي"</string> @@ -218,9 +218,9 @@ <string name="factory_reset_warning" msgid="6858705527798047809">"سيتم محو بيانات جهازك."</string> <string name="factory_reset_message" msgid="2657049595153992213">"تعذّر استخدام تطبيق المشرف. سيتم محو بيانات جهازك الآن.\n\nإذا كانت لديك أسئلة، اتصل بمشرف مؤسستك."</string> <string name="printing_disabled_by" msgid="3517499806528864633">"تم إيقاف الطباعة بواسطة <xliff:g id="OWNER_APP">%s</xliff:g>."</string> - <string name="personal_apps_suspension_title" msgid="7561416677884286600">"تفعيل الملف الشخصي للعمل"</string> + <string name="personal_apps_suspension_title" msgid="7561416677884286600">"تفعيل ملف العمل"</string> <string name="personal_apps_suspension_text" msgid="6115455688932935597">"تم حظر تطبيقاتك الشخصية إلى أن تفعِّل ملفك الشخصي للعمل."</string> - <string name="personal_apps_suspension_soon_text" msgid="8123898693479590">"سيتم حظر التطبيقات الشخصية في <xliff:g id="DATE">%1$s</xliff:g> في <xliff:g id="TIME">%2$s</xliff:g>. لا يسمح مشرف تكنولوجيا المعلومات في مؤسستك بإيقاف الملف الشخصي للعمل أكثر من <xliff:g id="NUMBER">%3$d</xliff:g> يوم."</string> + <string name="personal_apps_suspension_soon_text" msgid="8123898693479590">"سيتم حظر التطبيقات الشخصية في <xliff:g id="DATE">%1$s</xliff:g> في <xliff:g id="TIME">%2$s</xliff:g>. لا يسمح مشرف تكنولوجيا المعلومات في مؤسستك بإيقاف ملف العمل أكثر من <xliff:g id="NUMBER">%3$d</xliff:g> يوم."</string> <string name="personal_apps_suspended_turn_profile_on" msgid="2758012869627513689">"تفعيل"</string> <string name="work_profile_telephony_paused_title" msgid="7690804479291839519">"المكالمات والرسائل غير مفعّلة"</string> <string name="work_profile_telephony_paused_text" msgid="8065762301100978221">"لقد أوقفت تطبيقات العمل مؤقتًا. لن تتلقّى مكالمات هاتفية أو رسائل نصية."</string> @@ -312,9 +312,9 @@ <string name="safeMode" msgid="8974401416068943888">"الوضع الآمن"</string> <string name="android_system_label" msgid="5974767339591067210">"نظام Android"</string> <string name="user_owner_label" msgid="8628726904184471211">"التبديل إلى الملف الشخصي"</string> - <string name="managed_profile_label" msgid="7316778766973512382">"التبديل إلى الملف الشخصي للعمل"</string> + <string name="managed_profile_label" msgid="7316778766973512382">"التبديل إلى ملف العمل"</string> <string name="user_owner_app_label" msgid="1553595155465750298">"التبديل إلى تطبيق \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" في الملف الشخصي"</string> - <string name="managed_profile_app_label" msgid="367401088383965725">"التبديل إلى تطبيق \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" في الملف الشخصي للعمل"</string> + <string name="managed_profile_app_label" msgid="367401088383965725">"التبديل إلى تطبيق \"<xliff:g id="APP_NAME">%1$s</xliff:g>\" في ملف العمل"</string> <string name="permgrouplab_contacts" msgid="4254143639307316920">"جهات الاتصال"</string> <string name="permgroupdesc_contacts" msgid="9163927941244182567">"الوصول إلى جهات اتصالك"</string> <string name="permgrouplab_location" msgid="1858277002233964394">"الموقع الجغرافي"</string> @@ -708,10 +708,10 @@ <string name="face_acquired_too_dark" msgid="8539853432479385326">"الإضاءة غير كافية"</string> <string name="face_acquired_too_close" msgid="4453646176196302462">"يُرجى إبعاد الهاتف عنك."</string> <string name="face_acquired_too_far" msgid="2922278214231064859">"يُرجى تقريب الهاتف منك"</string> - <string name="face_acquired_too_high" msgid="8278815780046368576">"يُرجى رفع الهاتف للأعلى"</string> - <string name="face_acquired_too_low" msgid="4075391872960840081">"يُرجى خفض الهاتف للأسفل"</string> - <string name="face_acquired_too_right" msgid="6245286514593540859">"يُرجى تحريك الهاتف لجهة اليسار"</string> - <string name="face_acquired_too_left" msgid="9201762240918405486">"يُرجى تحريك الهاتف لجهة اليمين"</string> + <string name="face_acquired_too_high" msgid="8278815780046368576">"ارفع الهاتف للأعلى"</string> + <string name="face_acquired_too_low" msgid="4075391872960840081">"خفّض الهاتف للأسفل"</string> + <string name="face_acquired_too_right" msgid="6245286514593540859">"حرِّك الهاتف لجهة اليسار"</string> + <string name="face_acquired_too_left" msgid="9201762240918405486">"حرِّك الهاتف لجهة اليمين"</string> <string name="face_acquired_poor_gaze" msgid="4427153558773628020">"يُرجى النظر إلى جهازك مباشرة أكثر."</string> <string name="face_acquired_not_detected" msgid="1057966913397548150">"ارفع هاتفك إلى مستوى العينَين لأنّه تتعذّر رؤية وجهك"</string> <string name="face_acquired_too_much_motion" msgid="8199691445085189528">"حركة أكثر من اللازم. يُرجى حمل الهاتف بثبات."</string> @@ -1898,7 +1898,7 @@ <string name="clone_profile_label_badge" msgid="1871997694718793964">"نسخة طبق الأصل عن \"<xliff:g id="LABEL">%1$s</xliff:g>\""</string> <string name="private_profile_label_badge" msgid="1712086003787839183">"ملف شخصي خاص على <xliff:g id="LABEL">%1$s</xliff:g>"</string> <string name="lock_to_app_unlock_pin" msgid="3890940811866290782">"طلب إدخال رقم التعريف الشخصي قبل إزالة التثبيت"</string> - <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"طلب إدخال النقش الخاص بإلغاء القفل قبل إزالة التثبيت"</string> + <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"طلب إدخال نقش فتح القفل قبل إزالة التثبيت"</string> <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"طلب إدخال كلمة المرور قبل إزالة التثبيت"</string> <string name="package_installed_device_owner" msgid="7035926868974878525">"تم التثبيت بواسطة المشرف"</string> <string name="package_updated_device_owner" msgid="7560272363805506941">"تم التحديث بواسطة المشرف"</string> @@ -1948,7 +1948,7 @@ <string name="stk_cc_ss_to_ussd" msgid="8417905193112944760">"تم تغيير طلب SS إلى طلب USSD."</string> <string name="stk_cc_ss_to_ss" msgid="132040645206514450">"تم التغيير إلى طلب SS الجديد."</string> <string name="notification_phishing_alert_content_description" msgid="494227305355958790">"تنبيه بشأن تصيّد احتيالي"</string> - <string name="notification_work_profile_content_description" msgid="5296477955677725799">"الملف الشخصي للعمل"</string> + <string name="notification_work_profile_content_description" msgid="5296477955677725799">"ملف العمل"</string> <string name="notification_alerted_content_description" msgid="6139691253611265992">"تمّ تفعيل التنبيه"</string> <string name="notification_verified_content_description" msgid="6401483602782359391">"تم التحقّق من المتّصل"</string> <string name="expand_button_content_description_collapsed" msgid="3873368935659010279">"توسيع"</string> @@ -2027,8 +2027,8 @@ <string name="new_sms_notification_title" msgid="6528758221319927107">"لديك رسائل جديدة"</string> <string name="new_sms_notification_content" msgid="3197949934153460639">"فتح تطبيق الرسائل القصيرة SMS للعرض"</string> <string name="profile_encrypted_title" msgid="9001208667521266472">"قد تكون بعض الوظائف مُقيّدة."</string> - <string name="profile_encrypted_detail" msgid="5279730442756849055">"تم قفل الملف الشخصي للعمل."</string> - <string name="profile_encrypted_message" msgid="1128512616293157802">"انقر لإلغاء قفل الملف الشخصي للعمل"</string> + <string name="profile_encrypted_detail" msgid="5279730442756849055">"تم قفل ملف العمل."</string> + <string name="profile_encrypted_message" msgid="1128512616293157802">"انقر لإلغاء قفل ملف العمل"</string> <string name="usb_mtp_launch_notification_title" msgid="774319638256707227">"تم الاتصال بـ <xliff:g id="PRODUCT_NAME">%1$s</xliff:g>"</string> <string name="usb_mtp_launch_notification_description" msgid="6942535713629852684">"انقر لعرض الملفات"</string> <string name="pin_target" msgid="8036028973110156895">"تثبيت"</string> @@ -2213,7 +2213,7 @@ <string name="resolver_no_personal_apps_available" msgid="6284837227019594881">"ما مِن تطبيقات شخصية."</string> <string name="miniresolver_open_work" msgid="6286176185835401931">"هل تريد فتح تطبيق \"<xliff:g id="APP">%s</xliff:g>\" في ملفك الشخصي للعمل؟"</string> <string name="miniresolver_open_in_personal" msgid="807427577794490375">"هل تريد فتح المحتوى في تطبيق \"<xliff:g id="APP">%s</xliff:g>\" في الملف الشخصي؟"</string> - <string name="miniresolver_open_in_work" msgid="941341494673509916">"هل تريد فتح المحتوى في تطبيق \"<xliff:g id="APP">%s</xliff:g>\" في الملف الشخصي للعمل؟"</string> + <string name="miniresolver_open_in_work" msgid="941341494673509916">"هل تريد فتح المحتوى في تطبيق \"<xliff:g id="APP">%s</xliff:g>\" في ملف العمل؟"</string> <string name="miniresolver_call_in_work" msgid="528779988307529039">"هل تريد الاتصال من تطبيق العمل؟"</string> <string name="miniresolver_switch_to_work" msgid="1042640606122638596">"هل تريد الانتقال إلى تطبيق العمل؟"</string> <string name="miniresolver_call_information" msgid="6739417525304184083">"تسمح لك مؤسستك بإجراء المكالمات من تطبيقات العمل فقط."</string> diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml index 1c01cc76cc77..59940a3bc4c9 100644 --- a/core/res/res/values-de/strings.xml +++ b/core/res/res/values-de/strings.xml @@ -706,7 +706,7 @@ <string name="face_acquired_too_far" msgid="2922278214231064859">"Bewege das Smartphone näher heran"</string> <string name="face_acquired_too_high" msgid="8278815780046368576">"Bewege das Smartphone nach oben"</string> <string name="face_acquired_too_low" msgid="4075391872960840081">"Bewege das Smartphone nach unten"</string> - <string name="face_acquired_too_right" msgid="6245286514593540859">"Bewege das Smartphone nach links"</string> + <string name="face_acquired_too_right" msgid="6245286514593540859">"Bewege das Smartphone nach links"</string> <string name="face_acquired_too_left" msgid="9201762240918405486">"Bewege das Smartphone nach rechts"</string> <string name="face_acquired_poor_gaze" msgid="4427153558773628020">"Bitte sieh direkt auf dein Gerät."</string> <string name="face_acquired_not_detected" msgid="1057966913397548150">"Gesicht nicht erkannt. Smartphone auf Augenhöhe halten."</string> diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml index 796e5eabdf7d..d2aa32c4f9e5 100644 --- a/core/res/res/values-es/strings.xml +++ b/core/res/res/values-es/strings.xml @@ -710,7 +710,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 detecta tu cara. Sujeta el teléfono a la altura de los ojos."</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_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> diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml index ac070c785905..c2b36b7342b2 100644 --- a/core/res/res/values-hi/strings.xml +++ b/core/res/res/values-hi/strings.xml @@ -452,7 +452,7 @@ <string name="permdesc_foregroundServiceSpecialUse" msgid="646713654541885919">"इससे ऐप्लिकेशन, \"specialUse\" टाइप वाली फ़ोरग्राउंड सेवाओं का इस्तेमाल कर पाता है"</string> <string name="permlab_getPackageSize" msgid="375391550792886641">"पता करें कि ऐप मेमोरी में कितनी जगह है"</string> <string name="permdesc_getPackageSize" msgid="742743530909966782">"ऐप को उसका कोड, डेटा, और कैश मेमोरी के आकारों को फिर से पाने देता है"</string> - <string name="permlab_writeSettings" msgid="8057285063719277394">"सिस्टम सेटिंग बदलें"</string> + <string name="permlab_writeSettings" msgid="8057285063719277394">"सिस्टम की सेटिंग में बदलाव करे"</string> <string name="permdesc_writeSettings" msgid="8293047411196067188">"ऐप्लिकेशन को सिस्टम सेटिंग डेटा में बदलाव करने देता है. नुकसान पहुंचाने वाले ऐप्लिकेशन आपके सिस्टम के कॉन्फ़िगरेशन को खराब सकते हैं."</string> <string name="permlab_receiveBootCompleted" msgid="6643339400247325379">"प्रारंभ होने पर चलाएं"</string> <string name="permdesc_receiveBootCompleted" product="tablet" msgid="5565659082718177484">"ऐप्लिकेशन को सिस्टम से बूटिंग पूरी करते ही अपने आप शुरू करने देता है. इससे टैबलेट को शुरू होने में ज़्यादा समय लग सकता है और ऐप्लिकेशन निरंतर चलाकर संपूर्ण टैबलेट को धीमा करने देता है."</string> @@ -831,7 +831,7 @@ <string name="policydesc_watchLogin" product="tablet" msgid="2388436408621909298">"स्क्रीन को अनलॉक करते समय गलत लिखे गए पासवर्ड की संख्या पर निगरानी करें, और बहुत ज़्यादा बार गलत पासवर्ड लिखे जाने पर टैबलेट लॉक करें या टैबलेट का संपूर्ण डेटा मिटाएं."</string> <string name="policydesc_watchLogin" product="tv" msgid="2140588224468517507">"स्क्रीन को अनलॉक करते समय ध्यान रखें कि कितनी बार गलत पासवर्ड डाला गया है. अगर बहुत ज़्यादा बार गलत पासवर्ड डाला गया है, तो अपने Android TV डिवाइस को तुरंत लॉक करें या इसका सभी डेटा मिटाएं."</string> <string name="policydesc_watchLogin" product="automotive" msgid="7011438994051251521">"स्क्रीन को अनलॉक करते समय ध्यान रखें कि कितनी बार गलत पासवर्ड डाला गया है. अगर बहुत ज़्यादा बार गलत पासवर्ड डाला गया है, तो सूचना और मनोरंजन की सुविधा देने वाले डिवाइस को लॉक करें या इस डिवाइस का सारा डेटा मिटाएं."</string> - <string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"स्क्रीन को अनलॉक करते समय ध्यान रखें कि कितनी बार गलत पासवर्ड डाला गया है. अगर बहुत ज़्यादा बार गलत पासवर्ड डाला गया है, तो अपने फ़ोन को तुरंत लॉक करें या फ़ोन का सारा डेटा मिटा दें."</string> + <string name="policydesc_watchLogin" product="default" msgid="4885030206253600299">"स्क्रीन को अनलॉक करते समय ध्यान रखेगा कि कितनी बार गलत पासवर्ड डाला गया है. अगर बहुत ज़्यादा बार गलत पासवर्ड डाला जाएगा, तो फ़ोन को तुरंत लॉक करेगा या फ़ोन का सारा डेटा मिटा देगा."</string> <string name="policydesc_watchLogin_secondaryUser" product="tablet" msgid="2049038943004297474">"स्क्रीन का लॉक खोलते समय गलत तरीके से लिखे गए पासवर्ड पर नज़र रखें, और अगर बार-बार ज़्यादा पासवर्ड लिखे जाते हैं तो टैबलेट को लॉक करें या इस उपयोगकर्ता का सभी डेटा मिटा दें."</string> <string name="policydesc_watchLogin_secondaryUser" product="tv" msgid="8965224107449407052">"स्क्रीन को अनलॉक करते समय ध्यान रखें कि कितनी बार गलत पासवर्ड डाला गया है. अगर बहुत ज़्यादा बार गलत पासवर्ड डाला गया है, तो अपने Android TV डिवाइस को तुरंत लॉक करें या इस उपयोगकर्ता का सभी डेटा मिटाएं."</string> <string name="policydesc_watchLogin_secondaryUser" product="automotive" msgid="7180857406058327941">"स्क्रीन को अनलॉक करते समय ध्यान रखें कि कितनी बार गलत पासवर्ड डाला गया है. अगर बहुत ज़्यादा बार गलत पासवर्ड डाला गया है, तो सूचना और मनोरंजन की सुविधा देने वाले डिवाइस को लॉक करें या इस प्रोफ़ाइल का सारा डेटा मिटाएं."</string> @@ -844,7 +844,7 @@ <string name="policydesc_wipeData" product="tablet" msgid="7245372676261947507">"फ़ैक्टरी डेटा रीसेट करके चेतावनी दिए बिना फ़ोन का डेटा मिटाना."</string> <string name="policydesc_wipeData" product="tv" msgid="513862488950801261">"फ़ैक्ट्री डेटा रीसेट करके अपने Android TV डिवाइस का डेटा बिना चेतावनी दिए मिटाएं."</string> <string name="policydesc_wipeData" product="automotive" msgid="660804547737323300">"फ़ैक्ट्री डेटा रीसेट करके, बिना किसी चेतावनी के सूचना और मनोरंजन की सुविधा देने वाले डिवाइस में सेव डेटा को हमेशा के लिए मिटाएं."</string> - <string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"इससे फ़ैक्टरी डेटा रीसेट करके, चेतावनी दिए बिना फ़ोन का डेटा मिट जाता है."</string> + <string name="policydesc_wipeData" product="default" msgid="8036084184768379022">"फ़ैक्टरी डेटा रीसेट करके, चेतावनी दिए बिना फ़ोन का डेटा मिटा देगा."</string> <string name="policylab_wipeData_secondaryUser" product="automotive" msgid="115034358520328373">"प्रोफ़ाइल का डेटा मिटाना"</string> <string name="policylab_wipeData_secondaryUser" product="default" msgid="413813645323433166">"उपयोगकर्ता डेटा मिटाएं"</string> <string name="policydesc_wipeData_secondaryUser" product="tablet" msgid="2336676480090926470">"इस टैबलेट पर मौजूद इस उपयोगकर्ता का डेटा बिना चेतावनी के मिटा दें."</string> @@ -1655,7 +1655,7 @@ <string name="wireless_display_route_description" msgid="8297563323032966831">"वायरलेस डिसप्ले"</string> <string name="media_route_button_content_description" msgid="2299223698196869956">"कास्ट करें"</string> <string name="media_route_chooser_title" msgid="6646594924991269208">"डिवाइस से कनेक्ट करें"</string> - <string name="media_route_chooser_title_for_remote_display" msgid="3105906508794326446">"स्क्रीन को डिवाइस में कास्ट करें"</string> + <string name="media_route_chooser_title_for_remote_display" msgid="3105906508794326446">"स्क्रीन को डिवाइस पर कास्ट करें"</string> <string name="media_route_chooser_searching" msgid="6119673534251329535">"डिवाइस खोजे जा रहे हैं…"</string> <string name="media_route_chooser_extended_settings" msgid="2506352159381327741">"सेटिंग"</string> <string name="media_route_controller_disconnect" msgid="7362617572732576959">"डिसकनेक्ट करें"</string> @@ -1722,14 +1722,14 @@ <string name="accessibility_shortcut_off" msgid="3651336255403648739">"चालू न करें"</string> <string name="accessibility_shortcut_menu_item_status_on" msgid="6608392117189732543">"चालू है"</string> <string name="accessibility_shortcut_menu_item_status_off" msgid="5531598275559472393">"बंद है"</string> - <string name="accessibility_enable_service_title" msgid="3931558336268541484">"<xliff:g id="SERVICE">%1$s</xliff:g> को अपना डिवाइस पूरी तरह कंट्रोल करने की मंज़ूरी दें?"</string> + <string name="accessibility_enable_service_title" msgid="3931558336268541484">"<xliff:g id="SERVICE">%1$s</xliff:g> को अपना डिवाइस पूरी तरह कंट्रोल करने की अनुमति देनी है?"</string> <string name="accessibility_service_warning_description" msgid="291674995220940133">"पूरी तरह कंट्रोल करने की अनुमति उन ऐप्लिकेशन के लिए ठीक है जो सुलभता से जुड़ी ज़रूरतों के लिए बने हैं, लेकिन ज़्यादातर ऐप्लिकेशन के लिए यह ठीक नहीं है."</string> <string name="accessibility_service_screen_control_title" msgid="190017412626919776">"स्क्रीन को देखें और कंट्रोल करें"</string> <string name="accessibility_service_screen_control_description" msgid="6946315917771791525">"यह स्क्रीन पर दिखने वाले कॉन्टेंट को पढ़ सकता है और उसे दूसरे ऐप्लिकेशन के ऊपर दिखा सकता है."</string> <string name="accessibility_service_action_perform_title" msgid="779670378951658160">"देखें और कार्रवाई करें"</string> <string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"यह आपके और किसी ऐप्लिकेशन या हार्डवेयर सेंसर के बीच होने वाले इंटरैक्शन को ट्रैक कर सकता है और आपकी तरफ़ से ऐप्लिकेशन के साथ इंटरैक्ट कर सकता है."</string> <string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"अनुमति दें"</string> - <string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"इंकार करें"</string> + <string name="accessibility_dialog_button_deny" msgid="4129575637812472671">"न दें"</string> <string name="accessibility_dialog_button_uninstall" msgid="2952465517671708108">"अनइंस्टॉल करें"</string> <string name="accessibility_dialog_touch_filtered_warning" msgid="3741940116597822451">"ऐप्लिकेशन की वजह से, अनुमति का अनुरोध समझने में परेशानी हो रही है. इसलिए, आपके जवाब की पुष्टि नहीं की जा सकी."</string> <string name="accessibility_select_shortcut_menu_title" msgid="6002726538854613272">"किसी सुविधा का इस्तेमाल करने के लिए, उस पर टैप करें:"</string> @@ -1761,7 +1761,7 @@ <string name="owner_name" msgid="8713560351570795743">"मालिक"</string> <string name="guest_name" msgid="8502103277839834324">"मेहमान"</string> <string name="error_message_title" msgid="4082495589294631966">"गड़बड़ी"</string> - <string name="error_message_change_not_allowed" msgid="843159705042381454">"आपका व्यवस्थापक इस बदलाव की अनुमति नहीं देता"</string> + <string name="error_message_change_not_allowed" msgid="843159705042381454">"आपका एडमिन इस बदलाव की अनुमति नहीं देता"</string> <string name="app_not_found" msgid="3429506115332341800">"इस कार्यवाही को प्रबंधित करने के लिए कोई ऐप्स नहीं मिला"</string> <string name="revoke" msgid="5526857743819590458">"रद्द करें"</string> <string name="mediasize_iso_a0" msgid="7039061159929977973">"ISO A0"</string> @@ -1902,7 +1902,7 @@ <string name="confirm_battery_saver" msgid="5247976246208245754">"ठीक है"</string> <string name="battery_saver_description_with_learn_more" msgid="5444908404021316250">"बैटरी सेवर, गहरे रंग वाली थीम को चालू करता है. साथ ही, इस मोड में बैकग्राउंड की गतिविधि, कुछ विज़ुअल इफ़ेक्ट, और कुछ खास सुविधाएं कम या बंद हो जाती हैं. कुछ इंटरनेट कनेक्शन भी पूरी तरह काम नहीं करते."</string> <string name="battery_saver_description" msgid="8518809702138617167">"बैटरी सेवर, गहरे रंग वाली थीम को चालू करता है. साथ ही, इस मोड में बैकग्राउंड की गतिविधि, कुछ विज़ुअल इफ़ेक्ट, और कुछ सुविधाएं सीमित या बंद हो जाती हैं. कुछ इंटरनेट कनेक्शन भी पूरी तरह काम नहीं करते."</string> - <string name="data_saver_description" msgid="4995164271550590517">"डेटा खर्च को कम करने के लिए, डेटा बचाने की सेटिंग कुछ ऐप्लिकेशन को बैकग्राउंड में डेटा भेजने या डेटा पाने से रोकती है. फ़िलहाल, जिस ऐप्लिकेशन का इस्तेमाल किया जा रहा है वह डेटा ऐक्सेस कर सकता है, लेकिन ऐसा कभी-कभी ही हो पाएगा. उदाहरण के लिए, इमेज तब तक नहीं दिखेंगी, जब तक उन पर टैप नहीं किया जाएगा."</string> + <string name="data_saver_description" msgid="4995164271550590517">"डेटा खर्च को कम करने के लिए, डेटा बचाने की सेटिंग बैकग्राउंड में चलने वाले कुछ ऐप्लिकेशन को डेटा भेजने या पाने से रोकती है. हालांकि, फ़िलहाल इस्तेमाल किया जा रहा ऐप्लिकेशन, डेटा को ऐक्सेस कर सकता है, लेकिन वह अक्सर ऐसा नहीं कर पाएगा. उदाहरण के लिए, ऐसा हो सकता है कि इमेज तब तक न दिखें, जब तक आप उन पर टैप न करें."</string> <string name="data_saver_enable_title" msgid="7080620065745260137">"डेटा बचाने की सेटिंग चालू करनी है?"</string> <string name="data_saver_enable_button" msgid="4399405762586419726">"चालू करें"</string> <string name="zen_mode_duration_minutes_summary" msgid="4555514757230849789">"{count,plural, =1{एक मिनट के लिए ({formattedTime} तक)}one{# मिनट के लिए ({formattedTime} तक)}other{# मिनट के लिए ({formattedTime} तक)}}"</string> diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml index 9c8eb5634a33..518e851e67cf 100644 --- a/core/res/res/values-hu/strings.xml +++ b/core/res/res/values-hu/strings.xml @@ -1894,7 +1894,7 @@ <string name="clone_profile_label_badge" msgid="1871997694718793964">"<xliff:g id="LABEL">%1$s</xliff:g> klónozása"</string> <string name="private_profile_label_badge" msgid="1712086003787839183">"Privát <xliff:g id="LABEL">%1$s</xliff:g>"</string> <string name="lock_to_app_unlock_pin" msgid="3890940811866290782">"PIN-kód kérése a kitűzés feloldásához"</string> - <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"Feloldási minta kérése a rögzítés feloldásához"</string> + <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"Feloldási minta kérése a kitűzés feloldásához"</string> <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"Jelszó kérése a rögzítés feloldásához"</string> <string name="package_installed_device_owner" msgid="7035926868974878525">"A rendszergazda által telepítve"</string> <string name="package_updated_device_owner" msgid="7560272363805506941">"A rendszergazda által frissítve"</string> diff --git a/core/res/res/values-hy/strings.xml b/core/res/res/values-hy/strings.xml index 63edfcedc523..74d3751532b8 100644 --- a/core/res/res/values-hy/strings.xml +++ b/core/res/res/values-hy/strings.xml @@ -647,17 +647,17 @@ <string name="biometric_error_generic" msgid="6784371929985434439">"Չհաջողվեց նույնականացնել"</string> <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Էկրանի կողպում"</string> <string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Շարունակելու համար ապակողպեք էկրանը"</string> - <string name="fingerprint_acquired_partial" msgid="4323789264604479684">"Մատը ուժեղ սեղմեք սկաների վրա"</string> + <string name="fingerprint_acquired_partial" msgid="4323789264604479684">"Մատը ուժեղ սեղմեք սկաներին"</string> <string name="fingerprint_acquired_insufficient" msgid="2410176550915730974">"Մատնահետքը չի ճանաչվել։ Նորից փորձեք։"</string> <string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Մաքրեք մատնահետքերի սկաները և նորից փորձեք"</string> <string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Մաքրեք սկաները և նորից փորձեք"</string> - <string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Մատը ուժեղ սեղմեք սկաների վրա"</string> + <string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Մատը ուժեղ սեղմեք սկաներին"</string> <string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Շատ դանդաղ անցկացրիք մատը: Փորձեք նորից:"</string> <string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Փորձեք մեկ այլ մատնահետք"</string> <string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Շատ լուսավոր է"</string> <string name="fingerprint_acquired_power_press" msgid="3107864151278434961">"Հայտնաբերվել է սնուցման կոճակի սեղմում"</string> <string name="fingerprint_acquired_try_adjusting" msgid="3667006071003809364">"Փորձեք փոխել մատի դիրքը"</string> - <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Թեթևակի փոխեք մատի դիրքն ամեն անգամ"</string> + <string name="fingerprint_acquired_immobile" msgid="1621891895241888048">"Ամեն անգամ թեթևակի փոխեք մատի դիրքը"</string> <string-array name="fingerprint_acquired_vendor"> </string-array> <string name="fingerprint_error_not_match" msgid="4599441812893438961">"Մատնահետքը չի ճանաչվել"</string> @@ -704,8 +704,8 @@ <string name="face_acquired_too_dark" msgid="8539853432479385326">"Թույլ լուսավորություն"</string> <string name="face_acquired_too_close" msgid="4453646176196302462">"Փոքր-ինչ հեռու պահեք հեռախոսը"</string> <string name="face_acquired_too_far" msgid="2922278214231064859">"Մոտեցրեք հեռախոսը"</string> - <string name="face_acquired_too_high" msgid="8278815780046368576">"Պահեք հեռախոսն ավելի վերև"</string> - <string name="face_acquired_too_low" msgid="4075391872960840081">"Պահեք հեռախոսն ավելի ներքև"</string> + <string name="face_acquired_too_high" msgid="8278815780046368576">"Հեռախոսը շարժեք ավելի վերև"</string> + <string name="face_acquired_too_low" msgid="4075391872960840081">"Հեռախոսը շարժեք ավելի ներքև"</string> <string name="face_acquired_too_right" msgid="6245286514593540859">"Հեռախոսը շարժեք դեպի ձախ"</string> <string name="face_acquired_too_left" msgid="9201762240918405486">"Հեռախոսը շարժեք դեպի աջ"</string> <string name="face_acquired_poor_gaze" msgid="4427153558773628020">"Նայեք ուղիղ էկրանին։"</string> diff --git a/core/res/res/values-is/strings.xml b/core/res/res/values-is/strings.xml index 996c17779580..caa801de4ae6 100644 --- a/core/res/res/values-is/strings.xml +++ b/core/res/res/values-is/strings.xml @@ -306,7 +306,7 @@ <string name="foreground_service_tap_for_details" msgid="9078123626015586751">"Ýttu til að fá upplýsingar um rafhlöðu- og gagnanotkun"</string> <string name="foreground_service_multiple_separator" msgid="5002287361849863168">"<xliff:g id="LEFT_SIDE">%1$s</xliff:g>, <xliff:g id="RIGHT_SIDE">%2$s</xliff:g>"</string> <string name="safeMode" msgid="8974401416068943888">"Örugg stilling"</string> - <string name="android_system_label" msgid="5974767339591067210">"Android kerfið"</string> + <string name="android_system_label" msgid="5974767339591067210">"Android-kerfið"</string> <string name="user_owner_label" msgid="8628726904184471211">"Skipta yfir í einkasnið"</string> <string name="managed_profile_label" msgid="7316778766973512382">"Skipta yfir í vinnusnið"</string> <string name="user_owner_app_label" msgid="1553595155465750298">"Skipta yfir í einkasnið <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> @@ -1723,9 +1723,9 @@ <string name="accessibility_shortcut_menu_item_status_on" msgid="6608392117189732543">"KVEIKT"</string> <string name="accessibility_shortcut_menu_item_status_off" msgid="5531598275559472393">"SLÖKKT"</string> <string name="accessibility_enable_service_title" msgid="3931558336268541484">"Viltu leyfa „<xliff:g id="SERVICE">%1$s</xliff:g>“ að hafa fulla stjórn yfir tækinu þínu?"</string> - <string name="accessibility_service_warning_description" msgid="291674995220940133">"Full stjórnun er viðeigandi fyrir forrit sem hjálpa þér ef þú hefur ekki aðgang, en ekki fyrir flest forrit."</string> + <string name="accessibility_service_warning_description" msgid="291674995220940133">"Full stjórn er viðeigandi fyrir forrit sem hjálpa þér ef þú hefur ekki aðgang, en ekki fyrir flest forrit."</string> <string name="accessibility_service_screen_control_title" msgid="190017412626919776">"Skoða og stjórna skjá"</string> - <string name="accessibility_service_screen_control_description" msgid="6946315917771791525">"Það getur lesið allt efni á skjánum og birt efni yfir öðrum forritum."</string> + <string name="accessibility_service_screen_control_description" msgid="6946315917771791525">"Getur lesið allt efni á skjánum og birt efni yfir öðrum forritum."</string> <string name="accessibility_service_action_perform_title" msgid="779670378951658160">"Skoða og framkvæma aðgerðir"</string> <string name="accessibility_service_action_perform_description" msgid="2718852014003170558">"Það getur fylgst með samskiptum þínum við forrit eða skynjara vélbúnaðar og haft samskipti við forrit fyrir þína hönd."</string> <string name="accessibility_dialog_button_allow" msgid="2092558122987144530">"Leyfa"</string> diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml index e8dee3c625dc..4eb0ced6e840 100644 --- a/core/res/res/values-ja/strings.xml +++ b/core/res/res/values-ja/strings.xml @@ -196,7 +196,7 @@ <string name="work_profile_deleted" msgid="5891181538182009328">"仕事用プロファイルが削除されました"</string> <string name="work_profile_deleted_details" msgid="3773706828364418016">"仕事用プロファイルの管理アプリがないか、破損しています。そのため仕事用プロファイルと関連データが削除されました。管理者にサポートをご依頼ください。"</string> <string name="work_profile_deleted_description_dpm_wipe" msgid="2477244968924647232">"お使いの仕事用プロファイルはこのデバイスで使用できなくなりました"</string> - <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"パスワード入力回数が上限を超えました"</string> + <string name="work_profile_deleted_reason_maximum_password_failure" msgid="1080323158315663167">"パスワード入力回数が上限に達しました"</string> <string name="device_ownership_relinquished" msgid="4080886992183195724">"管理者により、デバイスの個人使用が許可されました"</string> <string name="network_logging_notification_title" msgid="554983187553845004">"管理対象のデバイス"</string> <string name="network_logging_notification_text" msgid="1327373071132562512">"このデバイスは組織によって管理され、ネットワーク トラフィックが監視される場合があります。詳しくはタップしてください。"</string> @@ -671,8 +671,8 @@ <string name="fingerprint_error_timeout" msgid="7361192266621252164">"指紋の設定がタイムアウトしました。もう一度お試しください。"</string> <string name="fingerprint_error_canceled" msgid="5541771463159727513">"指紋認証操作がキャンセルされました"</string> <string name="fingerprint_error_user_canceled" msgid="2017941773466506863">"指紋認証操作がユーザーによりキャンセルされました"</string> - <string name="fingerprint_error_lockout" msgid="6626753679019351368">"試行回数が上限を超えました。代わりに画面ロックを使用してください。"</string> - <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"試行回数が上限を超えました。代わりに画面ロックを使用してください。"</string> + <string name="fingerprint_error_lockout" msgid="6626753679019351368">"試行回数が上限に達しました。代わりに画面ロックを使用してください。"</string> + <string name="fingerprint_error_lockout_permanent" msgid="9060651300306264843">"試行回数が上限に達しました。代わりに画面ロックを使用してください。"</string> <string name="fingerprint_error_unable_to_process" msgid="2446280592818621224">"指紋を処理できません。もう一度お試しください。"</string> <string name="fingerprint_error_no_fingerprints" msgid="3144806556204061862">"指紋が登録されていません"</string> <string name="fingerprint_error_hw_not_present" msgid="5898827259419366359">"このデバイスには指紋認証センサーがありません"</string> @@ -734,8 +734,8 @@ <string name="face_error_canceled" msgid="2164434737103802131">"顔の操作をキャンセルしました。"</string> <string name="face_error_user_canceled" msgid="5766472033202928373">"顔認証はユーザーによりキャンセルされました"</string> <string name="face_error_lockout" msgid="7864408714994529437">"試行回数の上限です。後でもう一度お試しください。"</string> - <string name="face_error_lockout_permanent" msgid="8533257333130473422">"試行回数が上限を超えました。顔認証を利用できません。"</string> - <string name="face_error_lockout_screen_lock" msgid="5062609811636860928">"試行回数が上限を超えました。代わりに画面ロック解除を入力してください。"</string> + <string name="face_error_lockout_permanent" msgid="8533257333130473422">"試行回数が上限に達しました。顔認証を利用できません。"</string> + <string name="face_error_lockout_screen_lock" msgid="5062609811636860928">"試行回数が上限に達しました。代わりに画面ロック解除を入力してください。"</string> <string name="face_error_unable_to_process" msgid="5723292697366130070">"顔を確認できません。もう一度お試しください。"</string> <string name="face_error_not_enrolled" msgid="1134739108536328412">"顔認証を設定していません"</string> <string name="face_error_hw_not_present" msgid="7940978724978763011">"このデバイスは顔認証に対応していません"</string> diff --git a/core/res/res/values-kk/strings.xml b/core/res/res/values-kk/strings.xml index 7a2528dc03df..d1da74e1c680 100644 --- a/core/res/res/values-kk/strings.xml +++ b/core/res/res/values-kk/strings.xml @@ -995,7 +995,7 @@ <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Дұрыс!"</string> <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Қайталап көріңіз"</string> <string name="lockscreen_password_wrong" msgid="8605355913868947490">"Қайталап көріңіз"</string> - <string name="lockscreen_storage_locked" msgid="634993789186443380">"Мүмкіндіктер мен деректер үшін құлыпты ашыңыз"</string> + <string name="lockscreen_storage_locked" msgid="634993789186443380">"Барлық функция мен дерек үшін құлыпты ашыңыз"</string> <string name="faceunlock_multiple_failures" msgid="681991538434031708">"Бет тану арқылы ашу әрекеттері анықталған шегінен асып кетті"</string> <string name="lockscreen_missing_sim_message_short" msgid="1229301273156907613">"SIM картасы жоқ."</string> <string name="lockscreen_missing_sim_message" product="tablet" msgid="3986843848305639161">"Планшетте SIM картасы жоқ."</string> diff --git a/core/res/res/values-mk/strings.xml b/core/res/res/values-mk/strings.xml index a0afccfac08d..9b2b6ab52df7 100644 --- a/core/res/res/values-mk/strings.xml +++ b/core/res/res/values-mk/strings.xml @@ -995,7 +995,7 @@ <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"Точно!"</string> <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"Обидете се повторно"</string> <string name="lockscreen_password_wrong" msgid="8605355913868947490">"Обидете се повторно"</string> - <string name="lockscreen_storage_locked" msgid="634993789186443380">"Отклучи за сите функции и податоци"</string> + <string name="lockscreen_storage_locked" msgid="634993789186443380">"Отклучете за пристап до сите функции и податоци"</string> <string name="faceunlock_multiple_failures" msgid="681991538434031708">"Максималниот број обиди на отклучување со лик е надминат"</string> <string name="lockscreen_missing_sim_message_short" msgid="1229301273156907613">"Нема SIM-картичка"</string> <string name="lockscreen_missing_sim_message" product="tablet" msgid="3986843848305639161">"Нема SIM-картичка во таблетот."</string> diff --git a/core/res/res/values-mr/strings.xml b/core/res/res/values-mr/strings.xml index e8b1b8853d03..9d72fcdb6b9c 100644 --- a/core/res/res/values-mr/strings.xml +++ b/core/res/res/values-mr/strings.xml @@ -615,7 +615,7 @@ <string name="permdesc_disableKeyguard" msgid="3223710003098573038">"कीलॉक आणि कोणतीही संबद्ध पासवर्ड सुरक्षितता अक्षम करण्यासाठी अॅप ला अनुमती देते. उदाहरणार्थ, येणारा फोन कॉल प्राप्त करताना फोन कीलॉक अक्षम करतो, नंतर जेव्हा कॉल समाप्त होतो तेव्हा तो कीलॉक पुन्हा-सक्षम करतो."</string> <string name="permlab_requestPasswordComplexity" msgid="1808977190557794109">"स्क्रीन लॉक क्लिष्टतेची विनंती करा"</string> <string name="permdesc_requestPasswordComplexity" msgid="1130556896836258567">"अॅपला स्क्रीन लॉक क्लिष्टता पातळी (उच्च, मध्यम, खालची किंवा काहीही नाही) जाणून घेऊ देते, जी लांबीची संभाव्य रेंज आणि स्क्रीन लॉकचा प्रकार सूचित करते. अॅप वापरकर्त्यांना असेदेखील सुचवू शकते की त्यांनी स्क्रीन लॉक ठरावीक पातळीपर्यंत अपडेट करावे, परंतु वापरकर्ते त्याकडे मोकळेपणाने दुर्लक्ष करू शकतात आणि तेथून नेव्हिगेट करू शकतात. स्क्रीन लॉक प्लेनटेक्स्टमध्ये स्टोअर केले जात नसल्यामुळे अॅपला नेमका पासवर्ड माहीत नसतो याची नोंद घ्या."</string> - <string name="permlab_postNotification" msgid="4875401198597803658">"सूचना दाखवा"</string> + <string name="permlab_postNotification" msgid="4875401198597803658">"नोटिफिकेशन दाखवा"</string> <string name="permdesc_postNotification" msgid="5974977162462877075">"ॲपला सूचना दाखवू देते"</string> <string name="permlab_turnScreenOn" msgid="219344053664171492">"स्क्रीन सुरू करा"</string> <string name="permdesc_turnScreenOn" msgid="4394606875897601559">"अॅपला स्क्रीन सुरू करण्याची परवानगी देते."</string> diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml index add32bb4c6e9..674eac389094 100644 --- a/core/res/res/values-ms/strings.xml +++ b/core/res/res/values-ms/strings.xml @@ -706,10 +706,10 @@ <string name="face_acquired_too_far" msgid="2922278214231064859">"Dekatkan telefon"</string> <string name="face_acquired_too_high" msgid="8278815780046368576">"Tinggikan lagi telefon"</string> <string name="face_acquired_too_low" msgid="4075391872960840081">"Rendahkan lagi telefon"</string> - <string name="face_acquired_too_right" msgid="6245286514593540859">"Gerakkan telefon ke kiri anda"</string> - <string name="face_acquired_too_left" msgid="9201762240918405486">"Gerakkan telefon ke kanan anda"</string> + <string name="face_acquired_too_right" msgid="6245286514593540859">"Gerakkan telefon ke kiri"</string> + <string name="face_acquired_too_left" msgid="9201762240918405486">"Gerakkan telefon ke kanan"</string> <string name="face_acquired_poor_gaze" msgid="4427153558773628020">"Sila lihat terus pada peranti anda."</string> - <string name="face_acquired_not_detected" msgid="1057966913397548150">"Wajah tidak kelihatan. Pegang telefon pada paras mata."</string> + <string name="face_acquired_not_detected" msgid="1057966913397548150">"Wajah tidak kelihatan. Pegang telefon pada aras mata."</string> <string name="face_acquired_too_much_motion" msgid="8199691445085189528">"Terlalu bnyk gerakan. Pegang telefon dgn stabil."</string> <string name="face_acquired_recalibrate" msgid="8724013080976469746">"Sila daftarkan semula wajah anda."</string> <string name="face_acquired_too_different" msgid="4505278456634706967">"Wajah tidak dikenali. Cuba lagi."</string> @@ -1030,7 +1030,7 @@ <string name="lockscreen_failed_attempts_now_wiping" product="tablet" msgid="8682445539263683414">"Anda telah mencuba untuk membuka kunci tablet secara salah sebanyak <xliff:g id="NUMBER">%d</xliff:g> kali. Tablet kini akan ditetapkan semula ke tetapan lalai kilang."</string> <string name="lockscreen_failed_attempts_now_wiping" product="tv" msgid="2205435033340091883">"Anda telah cuba membuka peranti Android TV secara salah sebanyak <xliff:g id="NUMBER">%d</xliff:g> kali. Peranti Android TV anda kini akan ditetapkan semula kepada tetapan lalai kilang."</string> <string name="lockscreen_failed_attempts_now_wiping" product="default" msgid="2203704707679895487">"Anda telah mencuba untuk membuka kunci telefon secara salah sebanyak <xliff:g id="NUMBER">%d</xliff:g> kali. Telefon kini akan ditetapkan semula kepada tetapan lalai kilang."</string> - <string name="lockscreen_too_many_failed_attempts_countdown" msgid="6807200118164539589">"Cuba lagi dalam <xliff:g id="NUMBER">%d</xliff:g> saat."</string> + <string name="lockscreen_too_many_failed_attempts_countdown" msgid="6807200118164539589">"Cuba lagi selepas <xliff:g id="NUMBER">%d</xliff:g> saat."</string> <string name="lockscreen_forgot_pattern_button_text" msgid="8362442730606839031">"Lupa corak?"</string> <string name="lockscreen_glogin_forgot_pattern" msgid="9218940117797602518">"Buka kunci akaun"</string> <string name="lockscreen_glogin_too_many_attempts" msgid="3775904917743034195">"Terlalu banyak percubaan melukis corak"</string> diff --git a/core/res/res/values-my/strings.xml b/core/res/res/values-my/strings.xml index 3de8fb544ac8..030633b9172a 100644 --- a/core/res/res/values-my/strings.xml +++ b/core/res/res/values-my/strings.xml @@ -1894,7 +1894,7 @@ <string name="clone_profile_label_badge" msgid="1871997694718793964">"<xliff:g id="LABEL">%1$s</xliff:g> ပုံတူပွား"</string> <string name="private_profile_label_badge" msgid="1712086003787839183">"သီးသန့် <xliff:g id="LABEL">%1$s</xliff:g>"</string> <string name="lock_to_app_unlock_pin" msgid="3890940811866290782">"ပင်မဖြုတ်မီမှာ PIN ကို မေးကြည့်ရန်"</string> - <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"ပင်မဖြုတ်မီမှာ သော့ဖွင့် ရေးဆွဲမှုပုံစံကို မေးကြည့်ရန်"</string> + <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"ပင်မဖြုတ်မီ လော့ခ်ဖွင့်ပုံစံကို မေးရန်"</string> <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"ပင်မဖြုတ်မီမှာ စကားဝှက်ကို မေးကြည့်ရန်"</string> <string name="package_installed_device_owner" msgid="7035926868974878525">"သင်၏ စီမံခန့်ခွဲသူက ထည့်သွင်းထားသည်"</string> <string name="package_updated_device_owner" msgid="7560272363805506941">"သင်၏ စီမံခန့်ခွဲသူက အပ်ဒိတ်လုပ်ထားသည်"</string> diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml index 098162320d17..d4844051033c 100644 --- a/core/res/res/values-nl/strings.xml +++ b/core/res/res/values-nl/strings.xml @@ -725,7 +725,7 @@ <skip /> <string name="face_acquired_recalibrate_alt" msgid="5702674220280332115">"Kan gezichtsmodel niet maken. Probeer het opnieuw."</string> <string name="face_acquired_dark_glasses_detected_alt" msgid="4052123776406041972">"Donkere bril waargenomen. Je gezicht moet helemaal zichtbaar zijn."</string> - <string name="face_acquired_mouth_covering_detected_alt" msgid="1122294982850589766">"Gezichtsbedekking waargenomen. Je hele gezicht moet zichtbaar zijn."</string> + <string name="face_acquired_mouth_covering_detected_alt" msgid="1122294982850589766">"Gezichtsbedekking waargenomen. Je gezicht moet helemaal zichtbaar zijn."</string> <string-array name="face_acquired_vendor"> </string-array> <string name="face_error_hw_not_available" msgid="5085202213036026288">"Kan gezicht niet verifiëren. Hardware niet beschikbaar."</string> @@ -1894,7 +1894,7 @@ <string name="clone_profile_label_badge" msgid="1871997694718793964">"Kloon van <xliff:g id="LABEL">%1$s</xliff:g>"</string> <string name="private_profile_label_badge" msgid="1712086003787839183">"Privé <xliff:g id="LABEL">%1$s</xliff:g>"</string> <string name="lock_to_app_unlock_pin" msgid="3890940811866290782">"Vraag pin voor losmaken"</string> - <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"Vraag om ontgrendelingspatroon voor losmaken"</string> + <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"Ontgrendelingspatroon vragen om app los te maken"</string> <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"Vraag wachtwoord voor losmaken"</string> <string name="package_installed_device_owner" msgid="7035926868974878525">"Geïnstalleerd door je beheerder"</string> <string name="package_updated_device_owner" msgid="7560272363805506941">"Geüpdatet door je beheerder"</string> diff --git a/core/res/res/values-or/strings.xml b/core/res/res/values-or/strings.xml index 8b49e6c09a81..df78cc9d1b12 100644 --- a/core/res/res/values-or/strings.xml +++ b/core/res/res/values-or/strings.xml @@ -995,7 +995,7 @@ <string name="lockscreen_pattern_correct" msgid="8050630103651508582">"ଠିକ୍!"</string> <string name="lockscreen_pattern_wrong" msgid="2940138714468358458">"ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ"</string> <string name="lockscreen_password_wrong" msgid="8605355913868947490">"ପୁଣି ଚେଷ୍ଟା କରନ୍ତୁ"</string> - <string name="lockscreen_storage_locked" msgid="634993789186443380">"ସମସ୍ତ ସୁବିଧା ତଥା ଡାଟା ପାଇଁ ଅନଲକ୍ କରନ୍ତୁ"</string> + <string name="lockscreen_storage_locked" msgid="634993789186443380">"ସବୁ ଫିଚର ଓ ଡାଟା ପାଇଁ ଅନଲକ କରନ୍ତୁ"</string> <string name="faceunlock_multiple_failures" msgid="681991538434031708">"ସର୍ବାଧିକ ଫେସ୍ ଅନଲକ୍ ପ୍ରଚେଷ୍ଟା ଅତିକ୍ରମ କରିଛି"</string> <string name="lockscreen_missing_sim_message_short" msgid="1229301273156907613">"କୌଣସି SIM ନାହିଁ"</string> <string name="lockscreen_missing_sim_message" product="tablet" msgid="3986843848305639161">"ଟାବଲେଟରେ କୌଣସି SIM ନାହିଁ।"</string> diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml index 470191581044..f80e832b2e74 100644 --- a/core/res/res/values-pl/strings.xml +++ b/core/res/res/values-pl/strings.xml @@ -649,11 +649,11 @@ <string name="biometric_error_generic" msgid="6784371929985434439">"Podczas uwierzytelniania wystąpił błąd"</string> <string name="screen_lock_app_setting_name" msgid="6054944352976789228">"Używaj blokady ekranu"</string> <string name="screen_lock_dialog_default_subtitle" msgid="120359538048533695">"Użyj blokady ekranu, aby kontynuować"</string> - <string name="fingerprint_acquired_partial" msgid="4323789264604479684">"Mocno naciśnij czujnik"</string> + <string name="fingerprint_acquired_partial" msgid="4323789264604479684">"Mocno naciśnij czytnik"</string> <string name="fingerprint_acquired_insufficient" msgid="2410176550915730974">"Nie rozpoznano odcisku palca. Spróbuj ponownie."</string> <string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Wyczyść czytnik linii papilarnych i spróbuj ponownie"</string> <string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Wyczyść czujnik i spróbuj ponownie"</string> - <string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Mocno naciśnij czujnik"</string> + <string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Mocno naciśnij czytnik"</string> <string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Palec został obrócony zbyt wolno. Spróbuj ponownie."</string> <string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Użyj odcisku innego palca"</string> <string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Zbyt jasno"</string> diff --git a/core/res/res/values-pt-rBR/strings.xml b/core/res/res/values-pt-rBR/strings.xml index 73121ce35ce3..3dcd619afa1e 100644 --- a/core/res/res/values-pt-rBR/strings.xml +++ b/core/res/res/values-pt-rBR/strings.xml @@ -726,7 +726,7 @@ <skip /> <string name="face_acquired_recalibrate_alt" msgid="5702674220280332115">"Falha ao criar o modelo de rosto. Tente de novo."</string> <string name="face_acquired_dark_glasses_detected_alt" msgid="4052123776406041972">"Óculos escuros detectados. Seu rosto precisa estar completamente visível."</string> - <string name="face_acquired_mouth_covering_detected_alt" msgid="1122294982850589766">"Máscara detectada. Seu rosto precisa estar visível."</string> + <string name="face_acquired_mouth_covering_detected_alt" msgid="1122294982850589766">"Tem algo cobrindo seu rosto. Ele precisa estar visível."</string> <string-array name="face_acquired_vendor"> </string-array> <string name="face_error_hw_not_available" msgid="5085202213036026288">"Impossível verificar rosto. Hardware indisponível."</string> diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml index 73121ce35ce3..3dcd619afa1e 100644 --- a/core/res/res/values-pt/strings.xml +++ b/core/res/res/values-pt/strings.xml @@ -726,7 +726,7 @@ <skip /> <string name="face_acquired_recalibrate_alt" msgid="5702674220280332115">"Falha ao criar o modelo de rosto. Tente de novo."</string> <string name="face_acquired_dark_glasses_detected_alt" msgid="4052123776406041972">"Óculos escuros detectados. Seu rosto precisa estar completamente visível."</string> - <string name="face_acquired_mouth_covering_detected_alt" msgid="1122294982850589766">"Máscara detectada. Seu rosto precisa estar visível."</string> + <string name="face_acquired_mouth_covering_detected_alt" msgid="1122294982850589766">"Tem algo cobrindo seu rosto. Ele precisa estar visível."</string> <string-array name="face_acquired_vendor"> </string-array> <string name="face_error_hw_not_available" msgid="5085202213036026288">"Impossível verificar rosto. Hardware indisponível."</string> diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml index a92a580e927b..a1da51e5d3d7 100644 --- a/core/res/res/values-ru/strings.xml +++ b/core/res/res/values-ru/strings.xml @@ -653,7 +653,7 @@ <string name="fingerprint_acquired_insufficient" msgid="2410176550915730974">"Отпечаток пальца не распознан. Повторите попытку."</string> <string name="fingerprint_acquired_imager_dirty" msgid="1770676120848224250">"Очистите сканер отпечатков пальцев и повторите попытку."</string> <string name="fingerprint_acquired_imager_dirty_alt" msgid="9169582140486372897">"Очистите сканер и повторите попытку."</string> - <string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Плотно прижмите палец к сканеру."</string> + <string name="fingerprint_acquired_too_fast" msgid="1628459767349116104">"Плотно прижмите палец к сканеру"</string> <string name="fingerprint_acquired_too_slow" msgid="6683510291554497580">"Вы перемещали палец слишком медленно. Повторите попытку."</string> <string name="fingerprint_acquired_already_enrolled" msgid="2285166003936206785">"Попробуйте сохранить отпечаток другого пальца."</string> <string name="fingerprint_acquired_too_bright" msgid="3863560181670915607">"Слишком светло."</string> diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml index 8dc68ca0a9bd..47358665fe74 100644 --- a/core/res/res/values-sk/strings.xml +++ b/core/res/res/values-sk/strings.xml @@ -727,7 +727,7 @@ <skip /> <string name="face_acquired_recalibrate_alt" msgid="5702674220280332115">"Model tváre sa nedá vytvoriť. Skúste to znova."</string> <string name="face_acquired_dark_glasses_detected_alt" msgid="4052123776406041972">"Boli rozpoznané tmavé okuliare. Musí vám byť vidieť celú tvár."</string> - <string name="face_acquired_mouth_covering_detected_alt" msgid="1122294982850589766">"Bolo rozpoznané rúško. Musí vám byť vidieť celú tvár."</string> + <string name="face_acquired_mouth_covering_detected_alt" msgid="1122294982850589766">"Máte čiastočne zakrytú tvár. Musí byť viditeľná celá."</string> <string-array name="face_acquired_vendor"> </string-array> <string name="face_error_hw_not_available" msgid="5085202213036026288">"Tvár sa nedá overiť. Hardvér nie je k dispozícii."</string> diff --git a/core/res/res/values-ta/strings.xml b/core/res/res/values-ta/strings.xml index 870f52e7fce5..02e06fa028a5 100644 --- a/core/res/res/values-ta/strings.xml +++ b/core/res/res/values-ta/strings.xml @@ -725,7 +725,7 @@ <skip /> <string name="face_acquired_recalibrate_alt" msgid="5702674220280332115">"முகத் தோற்றம் பதிவாகவில்லை. மீண்டும் முயலவும்."</string> <string name="face_acquired_dark_glasses_detected_alt" msgid="4052123776406041972">"அடர் நிறக் கண்ணாடிகள் கண்டறியப்பட்டுள்ளது. உங்கள் முகத்தை முழுமையாகக் காட்டவும்."</string> - <string name="face_acquired_mouth_covering_detected_alt" msgid="1122294982850589766">"முகம் மறைக்கப்பட்டுள்ளது. உங்கள் முகத்தை முழுமையாகக் காட்டவும்."</string> + <string name="face_acquired_mouth_covering_detected_alt" msgid="1122294982850589766">"முகத்தை மறைக்காமல் முழுமையாகக் காட்டவும்."</string> <string-array name="face_acquired_vendor"> </string-array> <string name="face_error_hw_not_available" msgid="5085202213036026288">"முகத்தைச் சரிபார்க்க இயலவில்லை. வன்பொருள் இல்லை."</string> diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml index 3547870302b0..a6a6b5019dcb 100644 --- a/core/res/res/values-tl/strings.xml +++ b/core/res/res/values-tl/strings.xml @@ -709,7 +709,7 @@ <string name="face_acquired_too_right" msgid="6245286514593540859">"Iusog pakaliwa ang telepono mo"</string> <string name="face_acquired_too_left" msgid="9201762240918405486">"Iusog pakanan ang telepono mo"</string> <string name="face_acquired_poor_gaze" msgid="4427153558773628020">"Tumingin nang mas direkta sa iyong device."</string> - <string name="face_acquired_not_detected" msgid="1057966913397548150">"Hindi makita ang mukha mo. Hawakan ang telepono kapantay ng mata."</string> + <string name="face_acquired_not_detected" msgid="1057966913397548150">"Hindi makita ang mukha mo. Ipantay ang telepono sa mata."</string> <string name="face_acquired_too_much_motion" msgid="8199691445085189528">"Masyadong magalaw. Hawakang mabuti ang telepono."</string> <string name="face_acquired_recalibrate" msgid="8724013080976469746">"Paki-enroll muli ang iyong mukha."</string> <string name="face_acquired_too_different" msgid="4505278456634706967">"Hindi nakilala ang mukha. Subukan ulit."</string> diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml index 2019249be083..9336c17de745 100644 --- a/core/res/res/values-zh-rCN/strings.xml +++ b/core/res/res/values-zh-rCN/strings.xml @@ -1894,7 +1894,7 @@ <string name="clone_profile_label_badge" msgid="1871997694718793964">"<xliff:g id="LABEL">%1$s</xliff:g>克隆"</string> <string name="private_profile_label_badge" msgid="1712086003787839183">"私人“<xliff:g id="LABEL">%1$s</xliff:g>”"</string> <string name="lock_to_app_unlock_pin" msgid="3890940811866290782">"取消时要求输入PIN码"</string> - <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"取消时要求绘制解锁图案"</string> + <string name="lock_to_app_unlock_pattern" msgid="2694204070499712503">"取消固定前要求绘制解锁图案"</string> <string name="lock_to_app_unlock_password" msgid="9126722403506560473">"取消时要求输入密码"</string> <string name="package_installed_device_owner" msgid="7035926868974878525">"已由您的管理员安装"</string> <string name="package_updated_device_owner" msgid="7560272363805506941">"已由您的管理员更新"</string> @@ -1977,7 +1977,7 @@ <string name="language_selection_title" msgid="52674936078683285">"添加语言"</string> <string name="country_selection_title" msgid="5221495687299014379">"地区偏好设置"</string> <string name="search_language_hint" msgid="7004225294308793583">"输入语言名称"</string> - <string name="language_picker_section_suggested" msgid="6556199184638990447">"建议语言"</string> + <string name="language_picker_section_suggested" msgid="6556199184638990447">"建议的语言"</string> <string name="language_picker_regions_section_suggested" msgid="6080131515268225316">"推荐地区"</string> <string name="language_picker_section_suggested_bilingual" msgid="5932198319583556613">"建议的语言"</string> <string name="region_picker_section_suggested_bilingual" msgid="704607569328224133">"建议的地区"</string> diff --git a/core/tests/coretests/src/android/view/ViewFrameRateTest.java b/core/tests/coretests/src/android/view/ViewFrameRateTest.java index 28343f1faeda..0bf9a4cd47f6 100644 --- a/core/tests/coretests/src/android/view/ViewFrameRateTest.java +++ b/core/tests/coretests/src/android/view/ViewFrameRateTest.java @@ -203,7 +203,9 @@ public class ViewFrameRateTest { mActivityRule.runOnUiThread(() -> { mMovingView.setFrameContentVelocity(1_000_000_000f); mMovingView.invalidate(); - runAfterDraw(() -> assertEquals(140f, mViewRoot.getLastPreferredFrameRate(), 0f)); + runAfterDraw(() -> { + assertEquals(140f, mViewRoot.getLastPreferredFrameRate(), 0f); + }); }); waitForAfterDraw(); } @@ -411,6 +413,26 @@ public class ViewFrameRateTest { waitForAfterDraw(); } + @Test + @RequiresFlagsEnabled({FLAG_TOOLKIT_SET_FRAME_RATE_READ_ONLY, + FLAG_TOOLKIT_FRAME_RATE_VIEW_ENABLING_READ_ONLY, + FLAG_TOOLKIT_FRAME_RATE_VELOCITY_MAPPING_READ_ONLY + }) + public void frameRateAndCategory() throws Throwable { + waitForFrameRateCategoryToSettle(); + mActivityRule.runOnUiThread(() -> { + mMovingView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_LOW); + mMovingView.setFrameContentVelocity(1f); + mMovingView.invalidate(); + runAfterDraw(() -> { + assertEquals(FRAME_RATE_CATEGORY_LOW, + mViewRoot.getLastPreferredFrameRateCategory()); + assertEquals(60f, mViewRoot.getLastPreferredFrameRate()); + }); + }); + waitForAfterDraw(); + } + private void runAfterDraw(@NonNull Runnable runnable) { Handler handler = new Handler(Looper.getMainLooper()); mAfterDrawLatch = new CountDownLatch(1); diff --git a/core/tests/coretests/src/android/view/ViewRootImplTest.java b/core/tests/coretests/src/android/view/ViewRootImplTest.java index e0282a4d10a2..80fef6ca801e 100644 --- a/core/tests/coretests/src/android/view/ViewRootImplTest.java +++ b/core/tests/coretests/src/android/view/ViewRootImplTest.java @@ -52,6 +52,7 @@ import static com.google.common.truth.Truth.assertThat; import static com.google.common.truth.Truth.assertWithMessage; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -806,37 +807,50 @@ public class ViewRootImplTest { assertEquals(0, mViewRootImpl.getPreferredFrameRate(), 0.1); assertEquals(mViewRootImpl.getFrameRateCompatibility(), FRAME_RATE_COMPATIBILITY_FIXED_SOURCE); - assertEquals(false, mViewRootImpl.isFrameRateConflicted()); + assertFalse(mViewRootImpl.isFrameRateConflicted()); mViewRootImpl.votePreferredFrameRate(24, FRAME_RATE_COMPATIBILITY_GTE); - assertEquals(24, mViewRootImpl.getPreferredFrameRate(), 0.1); - assertEquals(FRAME_RATE_COMPATIBILITY_GTE, - mViewRootImpl.getFrameRateCompatibility()); - assertEquals(false, mViewRootImpl.isFrameRateConflicted()); + if (toolkitFrameRateVelocityMappingReadOnly()) { + assertEquals(24, mViewRootImpl.getPreferredFrameRate(), 0.1); + assertEquals(FRAME_RATE_COMPATIBILITY_GTE, + mViewRootImpl.getFrameRateCompatibility()); + assertFalse(mViewRootImpl.isFrameRateConflicted()); + } else { + assertEquals(FRAME_RATE_CATEGORY_HIGH, + mViewRootImpl.getPreferredFrameRateCategory()); + } mViewRootImpl.votePreferredFrameRate(30, FRAME_RATE_COMPATIBILITY_FIXED_SOURCE); assertEquals(30, mViewRootImpl.getPreferredFrameRate(), 0.1); // If there is a conflict, then set compatibility to // FRAME_RATE_COMPATIBILITY_FIXED_SOURCE assertEquals(FRAME_RATE_COMPATIBILITY_FIXED_SOURCE, mViewRootImpl.getFrameRateCompatibility()); - // Should be true since there is a conflict between 24 and 30. - assertTrue(mViewRootImpl.isFrameRateConflicted()); + if (toolkitFrameRateVelocityMappingReadOnly()) { + // Should be true since there is a conflict between 24 and 30. + assertTrue(mViewRootImpl.isFrameRateConflicted()); + } + mView.invalidate(); }); sInstrumentation.waitForIdleSync(); sInstrumentation.runOnMainSync(() -> { - assertEquals(false, mViewRootImpl.isFrameRateConflicted()); + assertFalse(mViewRootImpl.isFrameRateConflicted()); mViewRootImpl.votePreferredFrameRate(60, FRAME_RATE_COMPATIBILITY_GTE); - assertEquals(60, mViewRootImpl.getPreferredFrameRate(), 0.1); - assertEquals(FRAME_RATE_COMPATIBILITY_GTE, - mViewRootImpl.getFrameRateCompatibility()); - assertEquals(mViewRootImpl.isFrameRateConflicted(), false); + if (toolkitFrameRateVelocityMappingReadOnly()) { + assertEquals(60, mViewRootImpl.getPreferredFrameRate(), 0.1); + assertEquals(FRAME_RATE_COMPATIBILITY_GTE, + mViewRootImpl.getFrameRateCompatibility()); + } else { + assertEquals(FRAME_RATE_CATEGORY_HIGH, + mViewRootImpl.getPreferredFrameRateCategory()); + } + assertFalse(mViewRootImpl.isFrameRateConflicted()); mViewRootImpl.votePreferredFrameRate(120, FRAME_RATE_COMPATIBILITY_FIXED_SOURCE); assertEquals(120, mViewRootImpl.getPreferredFrameRate(), 0.1); assertEquals(FRAME_RATE_COMPATIBILITY_FIXED_SOURCE, mViewRootImpl.getFrameRateCompatibility()); // Should be false since 60 is a divisor of 120. - assertEquals(false, mViewRootImpl.isFrameRateConflicted()); + assertFalse(mViewRootImpl.isFrameRateConflicted()); mViewRootImpl.votePreferredFrameRate(60, FRAME_RATE_COMPATIBILITY_GTE); assertEquals(120, mViewRootImpl.getPreferredFrameRate(), 0.1); // compatibility should be remained the same (FRAME_RATE_COMPATIBILITY_FIXED_SOURCE) @@ -844,8 +858,7 @@ public class ViewRootImplTest { assertEquals(FRAME_RATE_COMPATIBILITY_FIXED_SOURCE, mViewRootImpl.getFrameRateCompatibility()); // Should be false since 60 is a divisor of 120. - assertEquals(false, mViewRootImpl.isFrameRateConflicted()); - + assertFalse(mViewRootImpl.isFrameRateConflicted()); }); } @@ -872,7 +885,7 @@ public class ViewRootImplTest { // reset the frame rate category counts for (int i = 0; i < 5; i++) { sInstrumentation.runOnMainSync(() -> { - mView.setRequestedFrameRate(mView.REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE); + mView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE); mView.invalidate(); }); sInstrumentation.waitForIdleSync(); @@ -881,21 +894,21 @@ public class ViewRootImplTest { waitForFrameRateCategoryToSettle(mView); sInstrumentation.runOnMainSync(() -> { - mView.setRequestedFrameRate(mView.REQUESTED_FRAME_RATE_CATEGORY_LOW); + mView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_LOW); mView.invalidate(); runAfterDraw(() -> assertEquals(FRAME_RATE_CATEGORY_LOW, mViewRootImpl.getLastPreferredFrameRateCategory())); }); waitForAfterDraw(); sInstrumentation.runOnMainSync(() -> { - mView.setRequestedFrameRate(mView.REQUESTED_FRAME_RATE_CATEGORY_NORMAL); + mView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_NORMAL); mView.invalidate(); runAfterDraw(() -> assertEquals(FRAME_RATE_CATEGORY_NORMAL, mViewRootImpl.getLastPreferredFrameRateCategory())); }); waitForAfterDraw(); sInstrumentation.runOnMainSync(() -> { - mView.setRequestedFrameRate(mView.REQUESTED_FRAME_RATE_CATEGORY_HIGH); + mView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_HIGH); mView.invalidate(); runAfterDraw(() -> assertEquals(FRAME_RATE_CATEGORY_HIGH, mViewRootImpl.getLastPreferredFrameRateCategory())); @@ -932,24 +945,20 @@ public class ViewRootImplTest { assertEquals(0, mViewRootImpl.getPreferredFrameRate(), 0.1); mView.setFrameContentVelocity(100); mView.invalidate(); - if (toolkitFrameRateVelocityMappingReadOnly()) { - runAfterDraw(() -> assertTrue(mViewRootImpl.getLastPreferredFrameRate() > 0)); - } else { - runAfterDraw(() -> assertEquals(FRAME_RATE_CATEGORY_HIGH, - mViewRootImpl.getLastPreferredFrameRateCategory())); - } + runAfterDraw(() -> { + if (toolkitFrameRateVelocityMappingReadOnly()) { + assertEquals(FRAME_RATE_CATEGORY_LOW, + mViewRootImpl.getLastPreferredFrameRateCategory()); + assertTrue(mViewRootImpl.getLastPreferredFrameRate() >= 60f); + } else { + assertEquals(FRAME_RATE_CATEGORY_HIGH, + mViewRootImpl.getLastPreferredFrameRateCategory()); + assertEquals(0, mViewRootImpl.getLastPreferredFrameRate(), 0.1); + } + }); }); waitForAfterDraw(); sInstrumentation.waitForIdleSync(); - if (toolkitFrameRateVelocityMappingReadOnly()) { - assertEquals(FRAME_RATE_CATEGORY_HIGH, - mViewRootImpl.getLastPreferredFrameRateCategory()); - assertTrue(mViewRootImpl.getLastPreferredFrameRate() >= 60f); - } else { - assertEquals(FRAME_RATE_CATEGORY_HIGH, - mViewRootImpl.getLastPreferredFrameRateCategory()); - assertEquals(0, mViewRootImpl.getLastPreferredFrameRate(), 0.1); - } } /** @@ -1002,9 +1011,9 @@ public class ViewRootImplTest { ViewRootImpl viewRootImpl = mView.getViewRootImpl(); final WindowManager.LayoutParams attrs = viewRootImpl.mWindowAttributes; - assertEquals(true, attrs.getFrameRateBoostOnTouchEnabled()); - assertEquals(viewRootImpl.getFrameRateBoostOnTouchEnabled(), - attrs.getFrameRateBoostOnTouchEnabled()); + assertTrue(attrs.getFrameRateBoostOnTouchEnabled()); + assertEquals(attrs.getFrameRateBoostOnTouchEnabled(), + viewRootImpl.getFrameRateBoostOnTouchEnabled()); sInstrumentation.runOnMainSync(() -> { attrs.setFrameRateBoostOnTouchEnabled(false); @@ -1014,9 +1023,9 @@ public class ViewRootImplTest { sInstrumentation.runOnMainSync(() -> { final WindowManager.LayoutParams newAttrs = viewRootImpl.mWindowAttributes; - assertEquals(false, newAttrs.getFrameRateBoostOnTouchEnabled()); - assertEquals(viewRootImpl.getFrameRateBoostOnTouchEnabled(), - newAttrs.getFrameRateBoostOnTouchEnabled()); + assertFalse(newAttrs.getFrameRateBoostOnTouchEnabled()); + assertEquals(newAttrs.getFrameRateBoostOnTouchEnabled(), + viewRootImpl.getFrameRateBoostOnTouchEnabled()); }); } @@ -1040,24 +1049,24 @@ public class ViewRootImplTest { assertEquals(0, viewRootImpl.getPreferredFrameRate(), 0.1); assertEquals(FRAME_RATE_COMPATIBILITY_FIXED_SOURCE, viewRootImpl.getFrameRateCompatibility()); - assertEquals(false, viewRootImpl.isFrameRateConflicted()); + assertFalse(viewRootImpl.isFrameRateConflicted()); viewRootImpl.votePreferredFrameRate(24, FRAME_RATE_COMPATIBILITY_FIXED_SOURCE); assertEquals(24, viewRootImpl.getPreferredFrameRate(), 0.1); assertEquals(FRAME_RATE_COMPATIBILITY_FIXED_SOURCE, viewRootImpl.getFrameRateCompatibility()); - assertEquals(false, viewRootImpl.isFrameRateConflicted()); + assertFalse(viewRootImpl.isFrameRateConflicted()); mView.invalidate(); assertEquals(24, viewRootImpl.getPreferredFrameRate(), 0.1); assertEquals(FRAME_RATE_COMPATIBILITY_FIXED_SOURCE, viewRootImpl.getFrameRateCompatibility()); - assertEquals(false, viewRootImpl.isFrameRateConflicted()); + assertFalse(viewRootImpl.isFrameRateConflicted()); }); Thread.sleep(delay); assertEquals(0, viewRootImpl.getPreferredFrameRate(), 0.1); assertEquals(FRAME_RATE_COMPATIBILITY_FIXED_SOURCE, viewRootImpl.getFrameRateCompatibility()); - assertEquals(false, viewRootImpl.isFrameRateConflicted()); + assertFalse(viewRootImpl.isFrameRateConflicted()); } /** @@ -1093,14 +1102,14 @@ public class ViewRootImplTest { // reset the frame rate category counts for (int i = 0; i < 5; i++) { sInstrumentation.runOnMainSync(() -> { - mView.setRequestedFrameRate(mView.REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE); + mView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE); mView.invalidate(); }); sInstrumentation.waitForIdleSync(); } sInstrumentation.runOnMainSync(() -> { - mView.setRequestedFrameRate(mView.REQUESTED_FRAME_RATE_CATEGORY_LOW); + mView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_LOW); mView.invalidate(); runAfterDraw(() -> assertEquals(FRAME_RATE_CATEGORY_LOW, mViewRootImpl.getLastPreferredFrameRateCategory())); @@ -1153,7 +1162,7 @@ public class ViewRootImplTest { // reset the frame rate category counts for (int i = 0; i < 5; i++) { sInstrumentation.runOnMainSync(() -> { - mView.setRequestedFrameRate(mView.REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE); + mView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE); mView.invalidate(); }); sInstrumentation.waitForIdleSync(); @@ -1162,15 +1171,17 @@ public class ViewRootImplTest { // In transition from frequent update to infrequent update Thread.sleep(delay); sInstrumentation.runOnMainSync(() -> { - mView.setRequestedFrameRate(mView.REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE); + mView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE); mView.invalidate(); - runAfterDraw(() -> assertEquals(FRAME_RATE_CATEGORY_NO_PREFERENCE, - mViewRootImpl.getLastPreferredFrameRateCategory())); + runAfterDraw(() -> { + assertEquals(FRAME_RATE_CATEGORY_NO_PREFERENCE, + mViewRootImpl.getLastPreferredFrameRateCategory()); + }); }); waitForAfterDraw(); Thread.sleep(delay); sInstrumentation.runOnMainSync(() -> { - mView.setRequestedFrameRate(mView.REQUESTED_FRAME_RATE_CATEGORY_DEFAULT); + mView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_DEFAULT); mView.invalidate(); runAfterDraw(() -> assertEquals(FRAME_RATE_CATEGORY_NO_PREFERENCE, mViewRootImpl.getLastPreferredFrameRateCategory())); @@ -1180,7 +1191,7 @@ public class ViewRootImplTest { // Infrequent update Thread.sleep(delay); sInstrumentation.runOnMainSync(() -> { - mView.setRequestedFrameRate(mView.REQUESTED_FRAME_RATE_CATEGORY_DEFAULT); + mView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_DEFAULT); mView.invalidate(); runAfterDraw(() -> assertEquals(FRAME_RATE_CATEGORY_NORMAL, mViewRootImpl.getLastPreferredFrameRateCategory())); @@ -1201,9 +1212,9 @@ public class ViewRootImplTest { ViewRootImpl viewRoot = mView.getViewRootImpl(); final WindowManager.LayoutParams attrs = viewRoot.mWindowAttributes; - assertEquals(attrs.isFrameRatePowerSavingsBalanced(), true); - assertEquals(viewRoot.isFrameRatePowerSavingsBalanced(), - attrs.isFrameRatePowerSavingsBalanced()); + assertTrue(attrs.isFrameRatePowerSavingsBalanced()); + assertEquals(attrs.isFrameRatePowerSavingsBalanced(), + viewRoot.isFrameRatePowerSavingsBalanced()); sInstrumentation.runOnMainSync(() -> { attrs.setFrameRatePowerSavingsBalanced(false); @@ -1213,9 +1224,9 @@ public class ViewRootImplTest { sInstrumentation.runOnMainSync(() -> { final WindowManager.LayoutParams newAttrs = viewRoot.mWindowAttributes; - assertEquals(false, newAttrs.isFrameRatePowerSavingsBalanced()); - assertEquals(viewRoot.isFrameRatePowerSavingsBalanced(), - newAttrs.isFrameRatePowerSavingsBalanced()); + assertFalse(newAttrs.isFrameRatePowerSavingsBalanced()); + assertEquals(newAttrs.isFrameRatePowerSavingsBalanced(), + viewRoot.isFrameRatePowerSavingsBalanced()); }); } @@ -1266,7 +1277,7 @@ public class ViewRootImplTest { for (int i = 0; i < 5; i++) { Thread.sleep(delay); sInstrumentation.runOnMainSync(() -> { - mView.setRequestedFrameRate(mView.REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE); + mView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_NO_PREFERENCE); mView.invalidate(); }); sInstrumentation.waitForIdleSync(); @@ -1274,7 +1285,7 @@ public class ViewRootImplTest { Thread.sleep(delay); sInstrumentation.runOnMainSync(() -> { - mView.setRequestedFrameRate(mView.REQUESTED_FRAME_RATE_CATEGORY_DEFAULT); + mView.setRequestedFrameRate(View.REQUESTED_FRAME_RATE_CATEGORY_DEFAULT); mView.invalidate(); runAfterDraw(() -> assertEquals(FRAME_RATE_CATEGORY_NORMAL, mViewRootImpl.getLastPreferredFrameRateCategory())); @@ -1410,7 +1421,7 @@ public class ViewRootImplTest { mViewRootImpl.dispatchInputEvent(event); }); sInstrumentation.waitForIdleSync(); - assertEquals(mKeyReceived, shouldReceiveKey); + assertEquals(shouldReceiveKey, mKeyReceived); } private void attachViewToWindow(View view) { diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java index 0f04321d1483..46a3e7f38bed 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java @@ -861,9 +861,9 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen // container update. updateDivider(wct, taskContainer); - // If the last direct activity of the host task is dismissed and the overlay container is - // the only taskFragment, the overlay container should also be dismissed. - dismissOverlayContainerIfNeeded(wct, taskContainer); + // If the last direct activity of the host task is dismissed and there's an always-on-top + // overlay container in the task, the overlay container should also be dismissed. + dismissAlwaysOnTopOverlayIfNeeded(wct, taskContainer); if (!shouldUpdateContainer) { return; @@ -1990,7 +1990,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen @NonNull TaskFragmentContainer container) { final TaskContainer taskContainer = container.getTaskContainer(); - if (dismissOverlayContainerIfNeeded(wct, taskContainer)) { + if (dismissAlwaysOnTopOverlayIfNeeded(wct, taskContainer)) { return; } @@ -2014,22 +2014,27 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen } } - /** Dismisses the overlay container in the {@code taskContainer} if needed. */ + /** + * Dismisses {@link TaskFragmentContainer#isAlwaysOnTopOverlay()} in the {@code taskContainer} + * if needed. + */ @GuardedBy("mLock") - private boolean dismissOverlayContainerIfNeeded(@NonNull WindowContainerTransaction wct, - @NonNull TaskContainer taskContainer) { - final TaskFragmentContainer overlayContainer = taskContainer.getOverlayContainer(); - if (overlayContainer == null) { + private boolean dismissAlwaysOnTopOverlayIfNeeded(@NonNull WindowContainerTransaction wct, + @NonNull TaskContainer taskContainer) { + // Dismiss always-on-top overlay container if it's the only container in the task and + // there's no direct activity in the parent task. + final List<TaskFragmentContainer> containers = taskContainer.getTaskFragmentContainers(); + if (containers.size() != 1 || taskContainer.hasDirectActivity()) { return false; } - // Dismiss the overlay container if it's the only container in the task and there's no - // direct activity in the parent task. - if (taskContainer.getTaskFragmentContainers().size() == 1 - && !taskContainer.hasDirectActivity()) { - mPresenter.cleanupContainer(wct, overlayContainer, false /* shouldFinishDependant */); - return true; + + final TaskFragmentContainer container = containers.getLast(); + if (!container.isAlwaysOnTopOverlay()) { + return false; } - return false; + + mPresenter.cleanupContainer(wct, container, false /* shouldFinishDependant */); + return true; } /** @@ -2620,25 +2625,42 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen /** * Gets all overlay containers from all tasks in this process, or an empty list if there's * no overlay container. - * <p> - * Note that we only support one overlay container for each task, but an app could have multiple - * tasks. */ @VisibleForTesting @GuardedBy("mLock") @NonNull - List<TaskFragmentContainer> getAllOverlayTaskFragmentContainers() { + List<TaskFragmentContainer> getAllNonFinishingOverlayContainers() { final List<TaskFragmentContainer> overlayContainers = new ArrayList<>(); for (int i = 0; i < mTaskContainers.size(); i++) { final TaskContainer taskContainer = mTaskContainers.valueAt(i); - final TaskFragmentContainer overlayContainer = taskContainer.getOverlayContainer(); - if (overlayContainer != null) { - overlayContainers.add(overlayContainer); - } + final List<TaskFragmentContainer> overlayContainersPerTask = taskContainer + .getTaskFragmentContainers() + .stream() + .filter(c -> c.isOverlay() && !c.isFinished()) + .toList(); + overlayContainers.addAll(overlayContainersPerTask); } return overlayContainers; } + /** + * Creates an overlay container or updates a visible overlay container if its + * {@link TaskFragmentContainer#getTaskId()}, {@link TaskFragmentContainer#getOverlayTag()} + * and {@link TaskFragmentContainer#getAssociatedActivityToken()} matches. + * <p> + * This method will also dismiss any existing overlay container if: + * <ul> + * <li>it's visible but not meet the criteria to update overlay</li> + * <li>{@link TaskFragmentContainer#getOverlayTag()} matches but not meet the criteria to + * update overlay</li> + * </ul> + * + * @param wct the {@link WindowContainerTransaction} + * @param options the {@link ActivityOptions} to launch the overlay + * @param intent the intent of activity to launch + * @param launchActivity the activity to launch the overlay container + * @return the overlay container + */ @VisibleForTesting // Suppress GuardedBy warning because lint ask to mark this method as // @GuardedBy(container.mController.mLock), which is mLock itself @@ -2649,7 +2671,7 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen @NonNull WindowContainerTransaction wct, @NonNull Bundle options, @NonNull Intent intent, @NonNull Activity launchActivity) { final List<TaskFragmentContainer> overlayContainers = - getAllOverlayTaskFragmentContainers(); + getAllNonFinishingOverlayContainers(); final String overlayTag = Objects.requireNonNull(options.getString(KEY_OVERLAY_TAG)); final boolean associateLaunchingActivity = options .getBoolean(KEY_OVERLAY_ASSOCIATE_WITH_LAUNCHING_ACTIVITY, true); @@ -2673,62 +2695,86 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen final int taskId = getTaskId(launchActivity); if (!overlayContainers.isEmpty()) { for (final TaskFragmentContainer overlayContainer : overlayContainers) { - if (!overlayTag.equals(overlayContainer.getOverlayTag()) - && taskId == overlayContainer.getTaskId()) { - // If there's an overlay container with different tag shown in the same - // task, dismiss the existing overlay container. - mPresenter.cleanupContainer(wct, overlayContainer, - false /* shouldFinishDependant */); - } - if (overlayTag.equals(overlayContainer.getOverlayTag()) - && taskId != overlayContainer.getTaskId()) { - Log.w(TAG, "The overlay container with tag:" - + overlayContainer.getOverlayTag() + " is dismissed because" - + " there's an existing overlay container with the same tag but" - + " different task ID:" + overlayContainer.getTaskId() + ". " - + "The new associated activity is " + launchActivity); + final boolean isTopNonFinishingOverlay = overlayContainer.equals( + overlayContainer.getTaskContainer().getTopNonFinishingTaskFragmentContainer( + true /* includePin */, true /* includeOverlay */)); + if (taskId != overlayContainer.getTaskId()) { // If there's an overlay container with same tag in a different task, // dismiss the overlay container since the tag must be unique per process. - mPresenter.cleanupContainer(wct, overlayContainer, - false /* shouldFinishDependant */); - } - if (overlayTag.equals(overlayContainer.getOverlayTag()) - && taskId == overlayContainer.getTaskId()) { - if (associateLaunchingActivity && !launchActivity.getActivityToken() - .equals(overlayContainer.getAssociatedActivityToken())) { + if (overlayTag.equals(overlayContainer.getOverlayTag())) { Log.w(TAG, "The overlay container with tag:" + overlayContainer.getOverlayTag() + " is dismissed because" + " there's an existing overlay container with the same tag but" - + " different associated launching activity. The new associated" - + " activity is " + launchActivity); - // The associated activity must be the same, or it will be dismissed. + + " different task ID:" + overlayContainer.getTaskId() + ". " + + "The new associated activity is " + launchActivity); + mPresenter.cleanupContainer(wct, overlayContainer, + false /* shouldFinishDependant */); + } + continue; + } + if (!overlayTag.equals(overlayContainer.getOverlayTag())) { + // If there's an overlay container with different tag on top in the same + // task, dismiss the existing overlay container. + if (isTopNonFinishingOverlay) { mPresenter.cleanupContainer(wct, overlayContainer, false /* shouldFinishDependant */); - } else if (!associateLaunchingActivity - && overlayContainer.isAssociatedWithActivity()) { + } + continue; + } + // The overlay container has the same tag and task ID with the new launching + // overlay container. + if (!isTopNonFinishingOverlay) { + // Dismiss the invisible overlay container regardless of activity + // association if it collides the tag of new launched overlay container . + Log.w(TAG, "The invisible overlay container with tag:" + + overlayContainer.getOverlayTag() + " is dismissed because" + + " there's a launching overlay container with the same tag." + + " The new associated activity is " + launchActivity); + mPresenter.cleanupContainer(wct, overlayContainer, + false /* shouldFinishDependant */); + continue; + } + // Requesting an always-on-top overlay. + if (!associateLaunchingActivity) { + if (overlayContainer.isAssociatedWithActivity()) { + // Dismiss the overlay container since it has associated with an activity. Log.w(TAG, "The overlay container with tag:" + overlayContainer.getOverlayTag() + " is dismissed because" + " there's an existing overlay container with the same tag but" + " different associated launching activity. The overlay container" + " doesn't associate with any activity."); - // Dismiss the overlay container since it has been associated with an - // activity. mPresenter.cleanupContainer(wct, overlayContainer, false /* shouldFinishDependant */); + continue; } else { - // Just update the overlay container if - // - should associate with an activity and associated activity matches - // - should not associate with an activity and the overlay container - // don't have an associated activity + // The existing overlay container doesn't associate an activity as well. + // Just update the overlay and return. + // Note that going to this condition means the tag, task ID matches a + // visible always-on-top overlay, and won't dismiss any overlay any more. mPresenter.applyActivityStackAttributes(wct, overlayContainer, attrs, getMinDimensions(intent)); - // We can just return the updated overlay container and don't need to - // check other condition since we only have one OverlayCreateParams, and - // if the tag and task are matched, it's impossible to match another task - // or tag since tags and tasks are all unique. return overlayContainer; } } + if (launchActivity.getActivityToken() + != overlayContainer.getAssociatedActivityToken()) { + Log.w(TAG, "The overlay container with tag:" + + overlayContainer.getOverlayTag() + " is dismissed because" + + " there's an existing overlay container with the same tag but" + + " different associated launching activity. The new associated" + + " activity is " + launchActivity); + // The associated activity must be the same, or it will be dismissed. + mPresenter.cleanupContainer(wct, overlayContainer, + false /* shouldFinishDependant */); + continue; + } + // Reaching here means the launching activity launch an overlay container with the + // same task ID, tag, while there's a previously launching visible overlay + // container. We'll regard it as updating the existing overlay container. + mPresenter.applyActivityStackAttributes(wct, overlayContainer, attrs, + getMinDimensions(intent)); + return overlayContainer; + } } // Launch the overlay container to the task with taskId. diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java index b56f671359c6..6231ea09e5cc 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java @@ -467,6 +467,11 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer { reorderTaskFragmentToFront(wct, pinnedContainer.getSecondaryContainer().getTaskFragmentToken()); } + final TaskFragmentContainer alwaysOnTopOverlayContainer = container.getTaskContainer() + .getAlwaysOnTopOverlayContainer(); + if (alwaysOnTopOverlayContainer != null) { + reorderTaskFragmentToFront(wct, alwaysOnTopOverlayContainer.getTaskFragmentToken()); + } } @Override diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java index 3dd96c468f9b..fdf0910519b5 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskContainer.java @@ -70,9 +70,11 @@ class TaskContainer { @Nullable private SplitPinContainer mSplitPinContainer; - /** The overlay container in this Task. */ + /** + * The {@link TaskFragmentContainer#isAlwaysOnTopOverlay()} in the Task. + */ @Nullable - private TaskFragmentContainer mOverlayContainer; + private TaskFragmentContainer mAlwaysOnTopOverlayContainer; @NonNull private final Configuration mConfiguration; @@ -316,10 +318,12 @@ class TaskContainer { return null; } - /** Returns the overlay container in the task, or {@code null} if it doesn't exist. */ + /** + * Returns the always-on-top overlay container in the task, or {@code null} if it doesn't exist. + */ @Nullable - TaskFragmentContainer getOverlayContainer() { - return mOverlayContainer; + TaskFragmentContainer getAlwaysOnTopOverlayContainer() { + return mAlwaysOnTopOverlayContainer; } int indexOf(@NonNull TaskFragmentContainer child) { @@ -531,7 +535,21 @@ class TaskContainer { updateSplitPinContainerIfNecessary(); // Update overlay container after split pin container since the overlay should be on top of // pin container. - updateOverlayContainerIfNecessary(); + updateAlwaysOnTopOverlayIfNecessary(); + } + + private void updateAlwaysOnTopOverlayIfNecessary() { + final List<TaskFragmentContainer> alwaysOnTopOverlays = mContainers + .stream().filter(TaskFragmentContainer::isAlwaysOnTopOverlay).toList(); + if (alwaysOnTopOverlays.size() > 1) { + throw new IllegalStateException("There must be at most one always-on-top overlay " + + "container per Task"); + } + mAlwaysOnTopOverlayContainer = alwaysOnTopOverlays.isEmpty() + ? null : alwaysOnTopOverlays.getFirst(); + if (mAlwaysOnTopOverlayContainer != null) { + moveContainerToLastIfNecessary(mAlwaysOnTopOverlayContainer); + } } private void updateSplitPinContainerIfNecessary() { @@ -559,18 +577,6 @@ class TaskContainer { } } - private void updateOverlayContainerIfNecessary() { - final List<TaskFragmentContainer> overlayContainers = mContainers.stream() - .filter(TaskFragmentContainer::isOverlay).toList(); - if (overlayContainers.size() > 1) { - throw new IllegalStateException("There must be at most one overlay container per Task"); - } - mOverlayContainer = overlayContainers.isEmpty() ? null : overlayContainers.get(0); - if (mOverlayContainer != null) { - moveContainerToLastIfNecessary(mOverlayContainer); - } - } - /** Moves the {@code container} to the last to align taskFragments' z-order. */ private void moveContainerToLastIfNecessary(@NonNull TaskFragmentContainer container) { final int index = mContainers.indexOf(container); diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java index 5dbb016bb7e0..094ebcb470f2 100644 --- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java +++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/TaskFragmentContainer.java @@ -1023,6 +1023,14 @@ class TaskFragmentContainer { return mAssociatedActivityToken != null; } + /** + * Returns {@code true} if the overlay container should be always on top, which should be + * a non-fill-parent overlay without activity association. + */ + boolean isAlwaysOnTopOverlay() { + return isOverlay() && !isAssociatedWithActivity(); + } + @Override public String toString() { return toString(true /* includeContainersToFinishOnExit */); diff --git a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java index dcdbe59cb7cb..b1b1984e3a70 100644 --- a/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java +++ b/libs/WindowManager/Jetpack/tests/unittest/src/androidx/window/extensions/embedding/OverlayPresentationTest.java @@ -178,37 +178,59 @@ public class OverlayPresentationTest { } @Test - public void testGetOverlayContainers() { - assertThat(mSplitController.getAllOverlayTaskFragmentContainers()).isEmpty(); + public void testGetAllNonFinishingOverlayContainers() { + assertThat(mSplitController.getAllNonFinishingOverlayContainers()).isEmpty(); final TaskFragmentContainer overlayContainer1 = createTestOverlayContainer(TASK_ID, "test1"); - assertThat(mSplitController.getAllOverlayTaskFragmentContainers()) + assertThat(mSplitController.getAllNonFinishingOverlayContainers()) .containsExactly(overlayContainer1); - assertThrows( - "The exception must throw if there are two overlay containers in the same task.", - IllegalStateException.class, - () -> createTestOverlayContainer(TASK_ID, "test2")); + final TaskFragmentContainer overlayContainer2 = + createTestOverlayContainer(TASK_ID, "test2"); + + assertThat(mSplitController.getAllNonFinishingOverlayContainers()) + .containsExactly(overlayContainer1, overlayContainer2); final TaskFragmentContainer overlayContainer3 = createTestOverlayContainer(TASK_ID + 1, "test3"); - assertThat(mSplitController.getAllOverlayTaskFragmentContainers()) - .containsExactly(overlayContainer1, overlayContainer3); + assertThat(mSplitController.getAllNonFinishingOverlayContainers()) + .containsExactly(overlayContainer1, overlayContainer2, overlayContainer3); + + final TaskFragmentContainer finishingOverlayContainer = + createTestOverlayContainer(TASK_ID, "test4"); + spyOn(finishingOverlayContainer); + doReturn(true).when(finishingOverlayContainer).isFinished(); + + assertThat(mSplitController.getAllNonFinishingOverlayContainers()) + .containsExactly(overlayContainer1, overlayContainer2, overlayContainer3); } @Test - public void testCreateOrUpdateOverlayTaskFragmentIfNeeded_anotherTagInTask_dismissOverlay() { + public void testCreateOrUpdateOverlayTaskFragmentIfNeeded_anotherTagInTask() { + createExistingOverlayContainers(false /* visible */); + createMockTaskFragmentContainer(mActivity); + + final TaskFragmentContainer overlayContainer = + createOrUpdateOverlayTaskFragmentIfNeeded("test3"); + + assertWithMessage("overlayContainer1 is still there since it's not visible.") + .that(mSplitController.getAllNonFinishingOverlayContainers()) + .containsExactly(mOverlayContainer1, mOverlayContainer2, overlayContainer); + } + + @Test + public void testCreateOrUpdateOverlay_visibleOverlaySameTagInTask_dismissOverlay() { createExistingOverlayContainers(); final TaskFragmentContainer overlayContainer = createOrUpdateOverlayTaskFragmentIfNeeded("test3"); - assertWithMessage("overlayContainer1 must be dismissed since the new overlay container" - + " is launched to the same task") - .that(mSplitController.getAllOverlayTaskFragmentContainers()) + assertWithMessage("overlayContainer1 must be dismissed since it's visible" + + " in the same task.") + .that(mSplitController.getAllNonFinishingOverlayContainers()) .containsExactly(mOverlayContainer2, overlayContainer); } @@ -223,7 +245,7 @@ public class OverlayPresentationTest { assertWithMessage("overlayContainer1 must be dismissed since the new overlay container" + " is launched with the same tag as an existing overlay container in a different " + "task") - .that(mSplitController.getAllOverlayTaskFragmentContainers()) + .that(mSplitController.getAllNonFinishingOverlayContainers()) .containsExactly(mOverlayContainer2, overlayContainer); } @@ -240,7 +262,7 @@ public class OverlayPresentationTest { assertWithMessage("overlayContainer1 must be updated since the new overlay container" + " is launched with the same tag and task") - .that(mSplitController.getAllOverlayTaskFragmentContainers()) + .that(mSplitController.getAllNonFinishingOverlayContainers()) .containsExactly(mOverlayContainer1, mOverlayContainer2); assertThat(overlayContainer).isEqualTo(mOverlayContainer1); @@ -260,11 +282,26 @@ public class OverlayPresentationTest { assertWithMessage("overlayContainer1 must be dismissed since the new overlay container" + " is associated with different launching activity") - .that(mSplitController.getAllOverlayTaskFragmentContainers()) + .that(mSplitController.getAllNonFinishingOverlayContainers()) .containsExactly(mOverlayContainer2, overlayContainer); } @Test + public void testCreateOrUpdateOverlayTaskFragmentIfNeeded_dismissOverlay() { + createExistingOverlayContainers(false /* visible */); + createMockTaskFragmentContainer(mActivity); + + final TaskFragmentContainer overlayContainer = + createOrUpdateOverlayTaskFragmentIfNeeded("test2"); + + // OverlayContainer2 is dismissed since new container is launched with the + // same tag in different task. + assertWithMessage("overlayContainer1 must be dismissed") + .that(mSplitController.getAllNonFinishingOverlayContainers()) + .containsExactly(mOverlayContainer1, overlayContainer); + } + + @Test public void testCreateOrUpdateOverlayTaskFragmentIfNeeded_dismissMultipleOverlays() { createExistingOverlayContainers(); @@ -275,15 +312,19 @@ public class OverlayPresentationTest { // different tag. OverlayContainer2 is dismissed since new container is launched with the // same tag in different task. assertWithMessage("overlayContainer1 and overlayContainer2 must be dismissed") - .that(mSplitController.getAllOverlayTaskFragmentContainers()) + .that(mSplitController.getAllNonFinishingOverlayContainers()) .containsExactly(overlayContainer); } private void createExistingOverlayContainers() { - mOverlayContainer1 = createTestOverlayContainer(TASK_ID, "test1"); - mOverlayContainer2 = createTestOverlayContainer(TASK_ID + 1, "test2"); + createExistingOverlayContainers(true /* visible */); + } + + private void createExistingOverlayContainers(boolean visible) { + mOverlayContainer1 = createTestOverlayContainer(TASK_ID, "test1", visible); + mOverlayContainer2 = createTestOverlayContainer(TASK_ID + 1, "test2", visible); List<TaskFragmentContainer> overlayContainers = mSplitController - .getAllOverlayTaskFragmentContainers(); + .getAllNonFinishingOverlayContainers(); assertThat(overlayContainers).containsExactly(mOverlayContainer1, mOverlayContainer2); } @@ -314,7 +355,7 @@ public class OverlayPresentationTest { createOrUpdateOverlayTaskFragmentIfNeeded("test"); setupTaskFragmentInfo(overlayContainer, mActivity, true /* isVisible */); - assertThat(mSplitController.getAllOverlayTaskFragmentContainers()) + assertThat(mSplitController.getAllNonFinishingOverlayContainers()) .containsExactly(overlayContainer); assertThat(overlayContainer.getTaskId()).isEqualTo(TASK_ID); assertThat(overlayContainer.areLastRequestedBoundsEqual(bounds)).isTrue(); @@ -322,14 +363,16 @@ public class OverlayPresentationTest { } @Test - public void testGetTopNonFishingTaskFragmentContainerWithOverlay() { - final TaskFragmentContainer overlayContainer = - createTestOverlayContainer(TASK_ID, "test1"); - - // Add a SplitPinContainer, the overlay should be on top + public void testGetTopNonFishingTaskFragmentContainerWithoutAssociatedOverlay() { final Activity primaryActivity = createMockActivity(); final Activity secondaryActivity = createMockActivity(); - + final Rect bounds = new Rect(0, 0, 100, 100); + mSplitController.setActivityStackAttributesCalculator(params -> + new ActivityStackAttributes.Builder().setRelativeBounds(bounds).build()); + final TaskFragmentContainer overlayContainer = + createTestOverlayContainer(TASK_ID, "test1", true /* isVisible */, + false /* shouldAssociateWithActivity */); + overlayContainer.setIsolatedNavigationEnabled(true); final TaskFragmentContainer primaryContainer = createMockTaskFragmentContainer(primaryActivity); final TaskFragmentContainer secondaryContainer = @@ -371,10 +414,10 @@ public class OverlayPresentationTest { @Test public void testGetTopNonFinishingActivityWithOverlay() { - TaskFragmentContainer overlayContainer = createTestOverlayContainer(TASK_ID, "test1"); - final Activity activity = createMockActivity(); final TaskFragmentContainer container = createMockTaskFragmentContainer(activity); + final TaskFragmentContainer overlayContainer = createTestOverlayContainer(TASK_ID, + "test1"); final TaskContainer task = container.getTaskContainer(); assertThat(task.getTopNonFinishingActivity(true /* includeOverlay */)) @@ -391,8 +434,9 @@ public class OverlayPresentationTest { } @Test - public void testUpdateOverlayContainer_dismissOverlayIfNeeded() { - TaskFragmentContainer overlayContainer = createTestOverlayContainer(TASK_ID, "test"); + public void testUpdateOverlayContainer_dismissNonAssociatedOverlayIfNeeded() { + TaskFragmentContainer overlayContainer = createTestOverlayContainer(TASK_ID, "test", + true /* isVisible */, false /* associatedLaunchingActivity */); mSplitController.updateOverlayContainer(mTransaction, overlayContainer); @@ -461,11 +505,10 @@ public class OverlayPresentationTest { @Test public void testOnTaskFragmentParentInfoChanged_positionOnlyChange_earlyReturn() { - final TaskFragmentContainer overlayContainer = createTestOverlayContainer(TASK_ID, "test"); + final TaskFragmentContainer overlayContainer = createTestOverlayContainer(TASK_ID, "test", + true /* isVisible */, false /* associatedLaunchingActivity */); final TaskContainer taskContainer = overlayContainer.getTaskContainer(); - assertThat(taskContainer.getOverlayContainer()).isEqualTo(overlayContainer); - spyOn(taskContainer); final TaskContainer.TaskProperties taskProperties = taskContainer.getTaskProperties(); final TaskFragmentParentInfo parentInfo = new TaskFragmentParentInfo( @@ -481,16 +524,15 @@ public class OverlayPresentationTest { assertWithMessage("The overlay container must still be dismissed even if " + "#updateContainer is not called") - .that(taskContainer.getOverlayContainer()).isNull(); + .that(taskContainer.getTaskFragmentContainers()).isEmpty(); } @Test - public void testOnTaskFragmentParentInfoChanged_invisibleTask_callDismissOverlayContainer() { - final TaskFragmentContainer overlayContainer = createTestOverlayContainer(TASK_ID, "test"); + public void testOnTaskFragmentParentInfoChanged_invisibleTask_callDismissNonAssocOverlay() { + final TaskFragmentContainer overlayContainer = createTestOverlayContainer(TASK_ID, "test", + true /* isVisible */, false /* associatedLaunchingActivity */); final TaskContainer taskContainer = overlayContainer.getTaskContainer(); - assertThat(taskContainer.getOverlayContainer()).isEqualTo(overlayContainer); - spyOn(taskContainer); final TaskContainer.TaskProperties taskProperties = taskContainer.getTaskProperties(); final TaskFragmentParentInfo parentInfo = new TaskFragmentParentInfo( @@ -505,7 +547,7 @@ public class OverlayPresentationTest { assertWithMessage("The overlay container must still be dismissed even if " + "#updateContainer is not called") - .that(taskContainer.getOverlayContainer()).isNull(); + .that(taskContainer.getTaskFragmentContainers()).isEmpty(); } @Test @@ -529,8 +571,7 @@ public class OverlayPresentationTest { @Test public void testApplyActivityStackAttributesForOverlayContainerAssociatedWithActivity() { - final TaskFragmentContainer container = createTestOverlayContainer(TASK_ID, - TEST_TAG, true /* associatedWithLaunchingActivity */); + final TaskFragmentContainer container = createTestOverlayContainer(TASK_ID, TEST_TAG); final IBinder token = container.getTaskFragmentToken(); final ActivityStackAttributes attributes = new ActivityStackAttributes.Builder() .setRelativeBounds(new Rect(0, 0, 200, 200)) @@ -555,7 +596,7 @@ public class OverlayPresentationTest { @Test public void testApplyActivityStackAttributesForOverlayContainerWithoutAssociatedActivity() { final TaskFragmentContainer container = createTestOverlayContainer(TASK_ID, TEST_TAG, - false /* associatedWithLaunchingActivity */); + true, /* isVisible */ false /* associatedWithLaunchingActivity */); final IBinder token = container.getTaskFragmentToken(); final ActivityStackAttributes attributes = new ActivityStackAttributes.Builder() .setRelativeBounds(new Rect(0, 0, 200, 200)) @@ -610,8 +651,7 @@ public class OverlayPresentationTest { mSplitPresenter.applyActivityStackAttributes(mTransaction, container, attributes, new Size(relativeBounds.width() + 1, relativeBounds.height())); - verify(mSplitPresenter).resizeTaskFragmentIfRegistered(mTransaction, container, - new Rect()); + verify(mSplitPresenter).resizeTaskFragmentIfRegistered(mTransaction, container, new Rect()); verify(mSplitPresenter).updateTaskFragmentWindowingModeIfRegistered(mTransaction, container, WINDOWING_MODE_UNDEFINED); verify(mSplitPresenter).updateAnimationParams(mTransaction, token, @@ -635,14 +675,14 @@ public class OverlayPresentationTest { mActivity.getActivityToken()); verify(mSplitPresenter, never()).cleanupContainer(any(), any(), anyBoolean()); - assertThat(mSplitController.getAllOverlayTaskFragmentContainers()) + assertThat(mSplitController.getAllNonFinishingOverlayContainers()) .contains(overlayWithoutAssociation); TaskFragmentContainer overlayWithAssociation = createOrUpdateOverlayTaskFragmentIfNeeded("test"); overlayWithAssociation.setInfo(mTransaction, createMockTaskFragmentInfo( overlayWithAssociation, mActivity, true /* isVisible */)); - assertThat(mSplitController.getAllOverlayTaskFragmentContainers()) + assertThat(mSplitController.getAllNonFinishingOverlayContainers()) .contains(overlayWithAssociation); clearInvocations(mSplitPresenter); @@ -655,7 +695,7 @@ public class OverlayPresentationTest { verify(mSplitPresenter).cleanupContainer(mTransaction, overlayWithAssociation, false); - assertThat(mSplitController.getAllOverlayTaskFragmentContainers()) + assertThat(mSplitController.getAllNonFinishingOverlayContainers()) .doesNotContain(overlayWithAssociation); } @@ -692,7 +732,14 @@ public class OverlayPresentationTest { @NonNull private TaskFragmentContainer createTestOverlayContainer(int taskId, @NonNull String tag) { - return createTestOverlayContainer(taskId, tag, + return createTestOverlayContainer(taskId, tag, false /* isVisible */, + true /* associateLaunchingActivity */); + } + + @NonNull + private TaskFragmentContainer createTestOverlayContainer(int taskId, @NonNull String tag, + boolean isVisible) { + return createTestOverlayContainer(taskId, tag, isVisible, true /* associateLaunchingActivity */); } @@ -700,13 +747,13 @@ public class OverlayPresentationTest { // once we have use cases. @NonNull private TaskFragmentContainer createTestOverlayContainer(int taskId, @NonNull String tag, - boolean associateLaunchingActivity) { + boolean isVisible, boolean associateLaunchingActivity) { Activity activity = createMockActivity(); TaskFragmentContainer overlayContainer = mSplitController.newContainer( null /* pendingAppearedActivity */, mIntent, activity, taskId, null /* pairedPrimaryContainer */, tag, Bundle.EMPTY, associateLaunchingActivity); - setupTaskFragmentInfo(overlayContainer, activity, false /* isVisible */); + setupTaskFragmentInfo(overlayContainer, activity, isVisible); return overlayContainer; } diff --git a/libs/WindowManager/Shell/AndroidManifest.xml b/libs/WindowManager/Shell/AndroidManifest.xml index 36d3313a9f3b..7a986835359a 100644 --- a/libs/WindowManager/Shell/AndroidManifest.xml +++ b/libs/WindowManager/Shell/AndroidManifest.xml @@ -23,4 +23,12 @@ <uses-permission android:name="android.permission.ROTATE_SURFACE_FLINGER" /> <uses-permission android:name="android.permission.WAKEUP_SURFACE_FLINGER" /> <uses-permission android:name="android.permission.READ_FRAME_BUFFER" /> + + <application> + <activity + android:name=".desktopmode.DesktopWallpaperActivity" + android:excludeFromRecents="true" + android:launchMode="singleInstance" + android:theme="@style/DesktopWallpaperTheme" /> + </application> </manifest> diff --git a/libs/WindowManager/Shell/res/values/styles.xml b/libs/WindowManager/Shell/res/values/styles.xml index 08c2a02acf55..13c0e6646002 100644 --- a/libs/WindowManager/Shell/res/values/styles.xml +++ b/libs/WindowManager/Shell/res/values/styles.xml @@ -23,6 +23,14 @@ <item name="android:windowAnimationStyle">@style/Animation.ForcedResizable</item> </style> + <!-- Theme used for the activity that shows below the desktop mode windows to show wallpaper --> + <style name="DesktopWallpaperTheme" parent="@android:style/Theme.Wallpaper.NoTitleBar"> + <item name="android:statusBarColor">@android:color/transparent</item> + <item name="android:navigationBarColor">@android:color/transparent</item> + <item name="android:windowDrawsSystemBarBackgrounds">true</item> + <item name="android:windowAnimationStyle">@null</item> + </style> + <style name="Animation.ForcedResizable" parent="@android:style/Animation"> <item name="android:activityOpenEnterAnimation">@anim/forced_resizable_enter</item> 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 ff00a7b9ea59..1408eadf544e 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 @@ -59,6 +59,7 @@ import com.android.wm.shell.desktopmode.DesktopModeLoggerTransitionObserver; import com.android.wm.shell.desktopmode.DesktopModeStatus; import com.android.wm.shell.desktopmode.DesktopModeTaskRepository; import com.android.wm.shell.desktopmode.DesktopTasksController; +import com.android.wm.shell.desktopmode.DesktopTasksTransitionObserver; import com.android.wm.shell.desktopmode.DragToDesktopTransitionHandler; import com.android.wm.shell.desktopmode.EnterDesktopTaskTransitionHandler; import com.android.wm.shell.desktopmode.ExitDesktopTaskTransitionHandler; @@ -569,6 +570,18 @@ public abstract class WMShellModule { @WMSingleton @Provides + static Optional<DesktopTasksTransitionObserver> provideDesktopTasksTransitionObserver( + Optional<DesktopModeTaskRepository> desktopModeTaskRepository, + Transitions transitions, + ShellInit shellInit + ) { + return desktopModeTaskRepository.flatMap(repository -> + Optional.of(new DesktopTasksTransitionObserver(repository, transitions, shellInit)) + ); + } + + @WMSingleton + @Provides static DesktopModeLoggerTransitionObserver provideDesktopModeLoggerTransitionObserver( ShellInit shellInit, Transitions transitions, @@ -623,7 +636,8 @@ public abstract class WMShellModule { @Provides static Object provideIndependentShellComponentsToCreate( DragAndDropController dragAndDropController, - DefaultMixedHandler defaultMixedHandler) { + DefaultMixedHandler defaultMixedHandler, + Optional<DesktopTasksTransitionObserver> desktopTasksTransitionObserverOptional) { return new Object(); } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeShellCommandHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeShellCommandHandler.kt index e1e41ee1e64d..f1a475a42452 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeShellCommandHandler.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeShellCommandHandler.kt @@ -36,7 +36,14 @@ class DesktopModeShellCommandHandler(private val controller: DesktopTasksControl true } } - + "moveToNextDisplay" -> { + if (!runMoveToNextDisplay(args, pw)) { + pw.println("Task not found. Please enter a valid taskId.") + false + } else { + true + } + } else -> { pw.println("Invalid command: ${args[0]}") false @@ -61,8 +68,28 @@ class DesktopModeShellCommandHandler(private val controller: DesktopTasksControl return controller.moveToDesktop(taskId, WindowContainerTransaction()) } + private fun runMoveToNextDisplay(args: Array<String>, pw: PrintWriter): Boolean { + if (args.size < 2) { + // First argument is the action name. + pw.println("Error: task id should be provided as arguments") + return false + } + + val taskId = try { + args[1].toInt() + } catch (e: NumberFormatException) { + pw.println("Error: task id should be an integer") + return false + } + + controller.moveToNextDisplay(taskId) + return true + } + override fun printShellCommandHelp(pw: PrintWriter, prefix: String) { pw.println("$prefix moveToDesktop <taskId> ") pw.println("$prefix Move a task with given id to desktop mode.") + pw.println("$prefix moveToNextDisplay <taskId> ") + pw.println("$prefix Move a task with given id to next display.") } }
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt index 120d68120771..50cea01fa281 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepository.kt @@ -22,6 +22,7 @@ import android.util.ArrayMap import android.util.ArraySet import android.util.SparseArray import android.view.Display.INVALID_DISPLAY +import android.window.WindowContainerToken import androidx.core.util.forEach import androidx.core.util.keyIterator import androidx.core.util.valueIterator @@ -49,6 +50,8 @@ class DesktopModeTaskRepository { var stashed: Boolean = false ) + // Token of the current wallpaper activity, used to remove it when the last task is removed + var wallpaperActivityToken: WindowContainerToken? = null // Tasks currently in freeform mode, ordered from top to bottom (top is at index 0). private val freeformTasksInZOrder = mutableListOf<Int>() private val activeTasksListeners = ArraySet<ActiveTasksListener>() @@ -200,6 +203,15 @@ class DesktopModeTaskRepository { } /** + * Check if a task with the given [taskId] is the only active task on its display + */ + fun isOnlyActiveTask(taskId: Int): Boolean { + return displayData.valueIterator().asSequence().any { data -> + data.activeTasks.singleOrNull() == taskId + } + } + + /** * Get a set of the active tasks for given [displayId] */ fun getActiveTasks(displayId: Int): ArraySet<Int> { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt index ec2e67007c4b..068661a6a666 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt @@ -40,6 +40,7 @@ import android.view.SurfaceControl import android.view.WindowManager.TRANSIT_CHANGE import android.view.WindowManager.TRANSIT_NONE import android.view.WindowManager.TRANSIT_OPEN +import android.view.WindowManager.TRANSIT_TO_BACK import android.view.WindowManager.TRANSIT_TO_FRONT import android.window.RemoteTransition import android.window.TransitionInfo @@ -381,7 +382,6 @@ class DesktopTasksController( ) val wct = WindowContainerTransaction() exitSplitIfApplicable(wct, taskInfo) - moveHomeTaskToFront(wct) bringDesktopAppsToFront(taskInfo.displayId, wct) addMoveToDesktopChanges(wct, taskInfo) wct.setBounds(taskInfo.token, freeformBounds) @@ -401,6 +401,22 @@ class DesktopTasksController( shellTaskOrganizer.applyTransaction(wct) } + /** + * Perform clean up of the desktop wallpaper activity if the closed window task is + * the last active task. + * + * @param wct transaction to modify if the last active task is closed + * @param taskId task id of the window that's being closed + */ + fun onDesktopWindowClose( + wct: WindowContainerTransaction, + taskId: Int + ) { + if (desktopModeTaskRepository.isOnlyActiveTask(taskId)) { + removeWallpaperActivity(wct) + } + } + /** Move a task with given `taskId` to fullscreen */ fun moveToFullscreen(taskId: Int) { shellTaskOrganizer.getRunningTaskInfo(taskId)?.let { task -> @@ -676,9 +692,15 @@ class DesktopTasksController( KtProtoLog.v(WM_SHELL_DESKTOP_MODE, "DesktopTasksController: bringDesktopAppsToFront") val activeTasks = desktopModeTaskRepository.getActiveTasks(displayId) - // First move home to front and then other tasks on top of it - moveHomeTaskToFront(wct) + if (Flags.enableDesktopWindowingWallpaperActivity()) { + // Add translucent wallpaper activity to show the wallpaper underneath + addWallpaperActivity(wct) + } else { + // Move home to front + moveHomeTaskToFront(wct) + } + // Then move other tasks on top of it val allTasksInZOrder = desktopModeTaskRepository.getFreeformTasksInZOrder() activeTasks // Sort descending as the top task is at index 0. It should be ordered to top last @@ -694,6 +716,26 @@ class DesktopTasksController( ?.let { homeTask -> wct.reorder(homeTask.getToken(), true /* onTop */) } } + private fun addWallpaperActivity(wct: WindowContainerTransaction) { + KtProtoLog.v(WM_SHELL_DESKTOP_MODE, "DesktopTasksController: addWallpaper") + val intent = Intent(context, DesktopWallpaperActivity::class.java) + val options = ActivityOptions.makeBasic().apply { + isPendingIntentBackgroundActivityLaunchAllowedByPermission = true + pendingIntentBackgroundActivityStartMode = + ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED + } + val pendingIntent = PendingIntent.getActivity(context, /* requestCode = */ 0, intent, + PendingIntent.FLAG_IMMUTABLE) + wct.sendPendingIntent(pendingIntent, intent, options.toBundle()) + } + + private fun removeWallpaperActivity(wct: WindowContainerTransaction) { + desktopModeTaskRepository.wallpaperActivityToken?.let { token -> + KtProtoLog.v(WM_SHELL_DESKTOP_MODE, "DesktopTasksController: removeWallpaper") + wct.removeTask(token) + } + } + fun releaseVisualIndicator() { val t = SurfaceControl.Transaction() visualIndicator?.releaseVisualIndicator(t) @@ -741,6 +783,9 @@ class DesktopTasksController( reason = "recents animation is running" false } + // Handle back navigation for the last window if wallpaper available + shouldRemoveWallpaper(request) -> + true // Only handle open or to front transitions request.type != TRANSIT_OPEN && request.type != TRANSIT_TO_FRONT -> { reason = "transition type not handled (${request.type})" @@ -777,6 +822,7 @@ class DesktopTasksController( val result = triggerTask?.let { task -> when { + request.type == TRANSIT_TO_BACK -> handleBackNavigation(task) // If display has tasks stashed, handle as stashed launch task.isStashed -> handleStashedTaskLaunch(task) // Check if the task has a top transparent activity @@ -824,6 +870,14 @@ class DesktopTasksController( return Flags.enableDesktopWindowingModalsPolicy() && isSingleTopActivityTranslucent(task) } + private fun shouldRemoveWallpaper(request: TransitionRequestInfo): Boolean { + return Flags.enableDesktopWindowingWallpaperActivity() && + request.type == TRANSIT_TO_BACK && + request.triggerTask?.let { task -> + desktopModeTaskRepository.isOnlyActiveTask(task.taskId) + } ?: false + } + private fun handleFreeformTaskLaunch(task: RunningTaskInfo): WindowContainerTransaction? { KtProtoLog.v(WM_SHELL_DESKTOP_MODE, "DesktopTasksController: handleFreeformTaskLaunch") val activeTasks = desktopModeTaskRepository.getActiveTasks(task.displayId) @@ -881,12 +935,26 @@ class DesktopTasksController( } } + /** Handle back navigation by removing wallpaper activity if it's the last active task */ + private fun handleBackNavigation(task: RunningTaskInfo): WindowContainerTransaction? { + if (desktopModeTaskRepository.isOnlyActiveTask(task.taskId) && + desktopModeTaskRepository.wallpaperActivityToken != null) { + // Remove wallpaper activity when the last active task is removed + return WindowContainerTransaction().also { wct -> + removeWallpaperActivity(wct) + } + } else { + return null + } + } + private fun addMoveToDesktopChanges( wct: WindowContainerTransaction, taskInfo: RunningTaskInfo ) { - val displayWindowingMode = taskInfo.configuration.windowConfiguration.displayWindowingMode - val targetWindowingMode = if (displayWindowingMode == WINDOWING_MODE_FREEFORM) { + val tdaInfo = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(taskInfo.displayId)!! + val tdaWindowingMode = tdaInfo.configuration.windowConfiguration.windowingMode + val targetWindowingMode = if (tdaWindowingMode == WINDOWING_MODE_FREEFORM) { // Display windowing is freeform, set to undefined and inherit it WINDOWING_MODE_UNDEFINED } else { @@ -903,8 +971,9 @@ class DesktopTasksController( wct: WindowContainerTransaction, taskInfo: RunningTaskInfo ) { - val displayWindowingMode = taskInfo.configuration.windowConfiguration.displayWindowingMode - val targetWindowingMode = if (displayWindowingMode == WINDOWING_MODE_FULLSCREEN) { + val tdaInfo = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(taskInfo.displayId)!! + val tdaWindowingMode = tdaInfo.configuration.windowConfiguration.windowingMode + val targetWindowingMode = if (tdaWindowingMode == WINDOWING_MODE_FULLSCREEN) { // Display windowing is fullscreen, set to undefined and inherit it WINDOWING_MODE_UNDEFINED } else { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt new file mode 100644 index 000000000000..20df26428649 --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.desktopmode + +import android.os.IBinder +import android.view.SurfaceControl +import android.view.WindowManager +import android.window.TransitionInfo +import com.android.window.flags.Flags.enableDesktopWindowingWallpaperActivity +import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE +import com.android.wm.shell.sysui.ShellInit +import com.android.wm.shell.transition.Transitions +import com.android.wm.shell.util.KtProtoLog + +/** + * A [Transitions.TransitionObserver] that observes shell transitions and updates + * the [DesktopModeTaskRepository] state TODO: b/332682201 + * This observes transitions related to desktop mode + * and other transitions that originate both within and outside shell. + */ +class DesktopTasksTransitionObserver( + private val desktopModeTaskRepository: DesktopModeTaskRepository, + private val transitions: Transitions, + shellInit: ShellInit +) : Transitions.TransitionObserver { + + init { + if (Transitions.ENABLE_SHELL_TRANSITIONS && DesktopModeStatus.isEnabled()) { + shellInit.addInitCallback(::onInit, this) + } + } + + fun onInit() { + KtProtoLog.d(WM_SHELL_DESKTOP_MODE, "DesktopTasksTransitionObserver: onInit") + transitions.registerObserver(this) + } + + override fun onTransitionReady( + transition: IBinder, + info: TransitionInfo, + startTransaction: SurfaceControl.Transaction, + finishTransaction: SurfaceControl.Transaction + ) { + // TODO: b/332682201 Update repository state + updateWallpaperToken(info) + } + + override fun onTransitionStarting(transition: IBinder) { + // TODO: b/332682201 Update repository state + } + + override fun onTransitionMerged(merged: IBinder, playing: IBinder) { + // TODO: b/332682201 Update repository state + } + + override fun onTransitionFinished(transition: IBinder, aborted: Boolean) { + // TODO: b/332682201 Update repository state + } + + private fun updateWallpaperToken(info: TransitionInfo) { + if (!enableDesktopWindowingWallpaperActivity()) { + return + } + info.changes.forEach { change -> + change.taskInfo?.let { taskInfo -> + if (DesktopWallpaperActivity.isWallpaperTask(taskInfo)) { + when (change.mode) { + WindowManager.TRANSIT_OPEN -> + desktopModeTaskRepository.wallpaperActivityToken = taskInfo.token + WindowManager.TRANSIT_CLOSE -> + desktopModeTaskRepository.wallpaperActivityToken = null + else -> {} + } + } + } + } + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopWallpaperActivity.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopWallpaperActivity.kt new file mode 100644 index 000000000000..c4a4474689fa --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopWallpaperActivity.kt @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.wm.shell.desktopmode + +import android.app.Activity +import android.app.ActivityManager +import android.content.ComponentName +import android.os.Bundle +import android.view.WindowManager +import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE +import com.android.wm.shell.util.KtProtoLog + +/** + * A transparent activity used in the desktop mode to show the wallpaper under the freeform windows. + * This activity will be running in `FULLSCREEN` windowing mode, which ensures it hides Launcher. + * When entering desktop, we would ensure that it's added behind desktop apps and removed when + * leaving the desktop mode. + * + * Note! This activity should NOT interact directly with any other code in the Shell without calling + * onto the shell main thread. Activities are always started on the main thread. + */ +class DesktopWallpaperActivity : Activity() { + + override fun onCreate(savedInstanceState: Bundle?) { + KtProtoLog.d(WM_SHELL_DESKTOP_MODE, "DesktopWallpaperActivity: onCreate") + super.onCreate(savedInstanceState) + window.addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) + } + + companion object { + private const val SYSTEM_UI_PACKAGE_NAME = "com.android.systemui" + private val wallpaperActivityComponent = + ComponentName(SYSTEM_UI_PACKAGE_NAME, DesktopWallpaperActivity::class.java.name) + + @JvmStatic + fun isWallpaperTask(taskInfo: ActivityManager.RunningTaskInfo) = + taskInfo.baseIntent.component?.let(::isWallpaperComponent) ?: false + + @JvmStatic + fun isWallpaperComponent(component: ComponentName) = + component == wallpaperActivityComponent + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java index c9185ae39114..b1a1e5999aa9 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/HomeTransitionObserver.java @@ -20,6 +20,7 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME; import static android.view.Display.DEFAULT_DISPLAY; import static android.window.TransitionInfo.FLAG_BACK_GESTURE_ANIMATED; +import static com.android.wm.shell.transition.Transitions.TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP; import static com.android.wm.shell.transition.Transitions.TransitionObserver; import android.annotation.NonNull; @@ -60,7 +61,8 @@ public class HomeTransitionObserver implements TransitionObserver, @NonNull SurfaceControl.Transaction finishTransaction) { for (TransitionInfo.Change change : info.getChanges()) { final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo(); - if (taskInfo == null + if (info.getType() == TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP + || taskInfo == null || taskInfo.displayId != DEFAULT_DISPLAY || taskInfo.taskId == -1 || !taskInfo.isRunning) { diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java index 32f271b0b896..87dc3915082f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/CaptionWindowDecorViewModel.java @@ -19,14 +19,19 @@ package com.android.wm.shell.windowdecor; import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; +import static android.content.pm.PackageManager.FEATURE_PC; +import static android.provider.Settings.Global.DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS; import static android.view.WindowManager.TRANSIT_CHANGE; import android.app.ActivityManager.RunningTaskInfo; +import android.content.ContentResolver; import android.content.Context; import android.graphics.Rect; import android.os.Handler; +import android.provider.Settings; import android.util.SparseArray; import android.view.Choreographer; +import android.view.Display; import android.view.MotionEvent; import android.view.SurfaceControl; import android.view.View; @@ -163,10 +168,33 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel { } private boolean shouldShowWindowDecor(RunningTaskInfo taskInfo) { - return taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM - || (taskInfo.getActivityType() == ACTIVITY_TYPE_STANDARD - && taskInfo.configuration.windowConfiguration.getDisplayWindowingMode() - == WINDOWING_MODE_FREEFORM); + if (taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) { + return true; + } + if (taskInfo.getActivityType() != ACTIVITY_TYPE_STANDARD) { + return false; + } + final DisplayAreaInfo rootDisplayAreaInfo = + mRootTaskDisplayAreaOrganizer.getDisplayAreaInfo(taskInfo.displayId); + if (rootDisplayAreaInfo != null) { + return rootDisplayAreaInfo.configuration.windowConfiguration.getWindowingMode() + == WINDOWING_MODE_FREEFORM; + } + + // It is possible that the rootDisplayAreaInfo is null when a task appears soon enough after + // a new display shows up, because TDA may appear after task appears in WM shell. Instead of + // fixing the synchronization issues, let's use other signals to "guess" the answer. It is + // OK in this context because no other captions other than the legacy developer option + // freeform and Kingyo/CF PC may use this class. WM shell should have full control over the + // condition where captions should show up in all new cases such as desktop mode, for which + // we should use different window decor view models. Ultimately Kingyo/CF PC may need to + // spin up their own window decor view model when they start to care about multiple + // displays. + if (isPc()) { + return true; + } + return taskInfo.displayId != Display.DEFAULT_DISPLAY + && forcesDesktopModeOnExternalDisplays(); } private void createWindowDecoration( @@ -313,4 +341,17 @@ public class CaptionWindowDecorViewModel implements WindowDecorViewModel { return true; } } + + /** + * Returns if this device is a PC. + */ + private boolean isPc() { + return mContext.getPackageManager().hasSystemFeature(FEATURE_PC); + } + + private boolean forcesDesktopModeOnExternalDisplays() { + final ContentResolver resolver = mContext.getContentResolver(); + return Settings.Global.getInt(resolver, + DEVELOPMENT_FORCE_DESKTOP_MODE_ON_EXTERNAL_DISPLAYS, 0) != 0; + } }
\ No newline at end of file diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java index 7649a782818a..777ab9c17218 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java @@ -84,6 +84,7 @@ import com.android.wm.shell.desktopmode.DesktopModeStatus; import com.android.wm.shell.desktopmode.DesktopModeVisualIndicator; import com.android.wm.shell.desktopmode.DesktopTasksController; import com.android.wm.shell.desktopmode.DesktopTasksController.SnapPosition; +import com.android.wm.shell.desktopmode.DesktopWallpaperActivity; import com.android.wm.shell.freeform.FreeformTaskTransitionStarter; import com.android.wm.shell.splitscreen.SplitScreen; import com.android.wm.shell.splitscreen.SplitScreen.StageType; @@ -407,7 +408,9 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { mSplitScreenController.moveTaskToFullscreen(getOtherSplitTask(mTaskId).taskId, SplitScreenController.EXIT_REASON_DESKTOP_MODE); } else { - mTaskOperations.closeTask(mTaskToken); + WindowContainerTransaction wct = new WindowContainerTransaction(); + mDesktopTasksController.onDesktopWindowClose(wct, mTaskId); + mTaskOperations.closeTask(mTaskToken, wct); } } else if (id == R.id.back_button) { mTaskOperations.injectBackKey(); @@ -1005,6 +1008,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel { return false; } return DesktopModeStatus.isEnabled() + && !DesktopWallpaperActivity.isWallpaperTask(taskInfo) && taskInfo.getWindowingMode() != WINDOWING_MODE_PINNED && taskInfo.getActivityType() == ACTIVITY_TYPE_STANDARD && !taskInfo.configuration.windowConfiguration.isAlwaysOnTop() diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskOperations.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskOperations.java index 1763e4f9533c..53d4e2701849 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskOperations.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/TaskOperations.java @@ -72,7 +72,10 @@ class TaskOperations { } void closeTask(WindowContainerToken taskToken) { - WindowContainerTransaction wct = new WindowContainerTransaction(); + closeTask(taskToken, new WindowContainerTransaction()); + } + + void closeTask(WindowContainerToken taskToken, WindowContainerTransaction wct) { wct.removeTask(taskToken); if (Transitions.ENABLE_SHELL_TRANSITIONS) { mTransitionStarter.startRemoveTransition(wct); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java index 3672ae386dc4..24f4d92af9d7 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestRunningTaskInfoBuilder.java @@ -23,8 +23,10 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; +import android.annotation.NonNull; import android.app.ActivityManager; import android.app.WindowConfiguration; +import android.content.Intent; import android.graphics.Point; import android.graphics.Rect; import android.os.IBinder; @@ -38,6 +40,7 @@ public final class TestRunningTaskInfoBuilder { private WindowContainerToken mToken = createMockWCToken(); private int mParentTaskId = INVALID_TASK_ID; + private Intent mBaseIntent = new Intent(); private @WindowConfiguration.ActivityType int mActivityType = ACTIVITY_TYPE_STANDARD; private @WindowConfiguration.WindowingMode int mWindowingMode = WINDOWING_MODE_UNDEFINED; private int mDisplayId = Display.DEFAULT_DISPLAY; @@ -68,6 +71,15 @@ public final class TestRunningTaskInfoBuilder { return this; } + /** + * Set {@link ActivityManager.RunningTaskInfo#baseIntent} for the task info, by default + * an empty intent is assigned + */ + public TestRunningTaskInfoBuilder setBaseIntent(@NonNull Intent intent) { + mBaseIntent = intent; + return this; + } + public TestRunningTaskInfoBuilder setActivityType( @WindowConfiguration.ActivityType int activityType) { mActivityType = activityType; @@ -109,6 +121,7 @@ public final class TestRunningTaskInfoBuilder { public ActivityManager.RunningTaskInfo build() { final ActivityManager.RunningTaskInfo info = new ActivityManager.RunningTaskInfo(); info.taskId = sNextTaskId++; + info.baseIntent = mBaseIntent; info.parentTaskId = mParentTaskId; info.displayId = mDisplayId; info.configuration.windowConfiguration.setBounds(mBounds); diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepositoryTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepositoryTest.kt index 0c45d52d5320..b2b54acf4585 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepositoryTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeTaskRepositoryTest.kt @@ -119,6 +119,57 @@ class DesktopModeTaskRepositoryTest : ShellTestCase() { } @Test + fun isOnlyActiveTask_noActiveTasks() { + // Not an active task + assertThat(repo.isOnlyActiveTask(1)).isFalse() + } + + @Test + fun isOnlyActiveTask_singleActiveTask() { + repo.addActiveTask(DEFAULT_DISPLAY, 1) + // The only active task + assertThat(repo.isActiveTask(1)).isTrue() + assertThat(repo.isOnlyActiveTask(1)).isTrue() + // Not an active task + assertThat(repo.isActiveTask(99)).isFalse() + assertThat(repo.isOnlyActiveTask(99)).isFalse() + } + + @Test + fun isOnlyActiveTask_multipleActiveTasks() { + repo.addActiveTask(DEFAULT_DISPLAY, 1) + repo.addActiveTask(DEFAULT_DISPLAY, 2) + // Not the only task + assertThat(repo.isActiveTask(1)).isTrue() + assertThat(repo.isOnlyActiveTask(1)).isFalse() + // Not the only task + assertThat(repo.isActiveTask(2)).isTrue() + assertThat(repo.isOnlyActiveTask(2)).isFalse() + // Not an active task + assertThat(repo.isActiveTask(99)).isFalse() + assertThat(repo.isOnlyActiveTask(99)).isFalse() + } + + @Test + fun isOnlyActiveTask_multipleDisplays() { + repo.addActiveTask(DEFAULT_DISPLAY, 1) + repo.addActiveTask(DEFAULT_DISPLAY, 2) + repo.addActiveTask(SECOND_DISPLAY, 3) + // Not the only task on DEFAULT_DISPLAY + assertThat(repo.isActiveTask(1)).isTrue() + assertThat(repo.isOnlyActiveTask(1)).isFalse() + // Not the only task on DEFAULT_DISPLAY + assertThat(repo.isActiveTask(2)).isTrue() + assertThat(repo.isOnlyActiveTask(2)).isFalse() + // The only active task on SECOND_DISPLAY + assertThat(repo.isActiveTask(3)).isTrue() + assertThat(repo.isOnlyActiveTask(3)).isTrue() + // Not an active task + assertThat(repo.isActiveTask(99)).isFalse() + assertThat(repo.isOnlyActiveTask(99)).isFalse() + } + + @Test fun addListener_notifiesVisibleFreeformTask() { repo.updateVisibleFreeformTasks(DEFAULT_DISPLAY, taskId = 1, visible = true) val listener = TestVisibilityListener() diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt index 93a967e9bfc9..64f604119a8b 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt @@ -23,10 +23,13 @@ import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN import android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW import android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED +import android.content.Intent import android.graphics.Point import android.graphics.PointF import android.graphics.Rect import android.os.Binder +import android.platform.test.annotations.DisableFlags +import android.platform.test.annotations.EnableFlags import android.platform.test.flag.junit.SetFlagsRule import android.testing.AndroidTestingRunner import android.view.Display.DEFAULT_DISPLAY @@ -34,11 +37,15 @@ import android.view.SurfaceControl import android.view.WindowManager import android.view.WindowManager.TRANSIT_CHANGE import android.view.WindowManager.TRANSIT_OPEN +import android.view.WindowManager.TRANSIT_TO_BACK import android.view.WindowManager.TRANSIT_TO_FRONT import android.window.DisplayAreaInfo import android.window.RemoteTransition import android.window.TransitionRequestInfo +import android.window.WindowContainerToken import android.window.WindowContainerTransaction +import android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_PENDING_INTENT +import android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REMOVE_TASK import android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER import androidx.test.filters.SmallTest import com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn @@ -161,6 +168,10 @@ class DesktopTasksControllerTest : ShellTestCase() { (i.arguments.first() as Rect).set(STABLE_BOUNDS) } + val tda = DisplayAreaInfo(MockToken().token(), DEFAULT_DISPLAY, 0) + tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN + whenever(rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)).thenReturn(tda) + controller = createController() controller.setSplitScreenController(splitScreenController) @@ -219,7 +230,8 @@ class DesktopTasksControllerTest : ShellTestCase() { } @Test - fun showDesktopApps_allAppsInvisible_bringsToFront() { + @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY) + fun showDesktopApps_allAppsInvisible_bringsToFront_desktopWallpaperDisabled() { val homeTask = setUpHomeTask() val task1 = setUpFreeformTask() val task2 = setUpFreeformTask() @@ -238,7 +250,27 @@ class DesktopTasksControllerTest : ShellTestCase() { } @Test - fun showDesktopApps_appsAlreadyVisible_bringsToFront() { + @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY) + fun showDesktopApps_allAppsInvisible_bringsToFront_desktopWallpaperEnabled() { + val task1 = setUpFreeformTask() + val task2 = setUpFreeformTask() + markTaskHidden(task1) + markTaskHidden(task2) + + controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition())) + + val wct = + getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java) + assertThat(wct.hierarchyOps).hasSize(3) + // Expect order to be from bottom: wallpaper intent, task1, task2 + wct.assertPendingIntentAt(index = 0, desktopWallpaperIntent) + wct.assertReorderAt(index = 1, task1) + wct.assertReorderAt(index = 2, task2) + } + + @Test + @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY) + fun showDesktopApps_appsAlreadyVisible_bringsToFront_desktopWallpaperDisabled() { val homeTask = setUpHomeTask() val task1 = setUpFreeformTask() val task2 = setUpFreeformTask() @@ -257,7 +289,27 @@ class DesktopTasksControllerTest : ShellTestCase() { } @Test - fun showDesktopApps_someAppsInvisible_reordersAll() { + @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY) + fun showDesktopApps_appsAlreadyVisible_bringsToFront_desktopWallpaperEnabled() { + val task1 = setUpFreeformTask() + val task2 = setUpFreeformTask() + markTaskVisible(task1) + markTaskVisible(task2) + + controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition())) + + val wct = + getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java) + assertThat(wct.hierarchyOps).hasSize(3) + // Expect order to be from bottom: wallpaper intent, task1, task2 + wct.assertPendingIntentAt(index = 0, desktopWallpaperIntent) + wct.assertReorderAt(index = 1, task1) + wct.assertReorderAt(index = 2, task2) + } + + @Test + @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY) + fun showDesktopApps_someAppsInvisible_reordersAll_desktopWallpaperDisabled() { val homeTask = setUpHomeTask() val task1 = setUpFreeformTask() val task2 = setUpFreeformTask() @@ -276,7 +328,27 @@ class DesktopTasksControllerTest : ShellTestCase() { } @Test - fun showDesktopApps_noActiveTasks_reorderHomeToTop() { + @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY) + fun showDesktopApps_someAppsInvisible_reordersAll_desktopWallpaperEnabled() { + val task1 = setUpFreeformTask() + val task2 = setUpFreeformTask() + markTaskHidden(task1) + markTaskVisible(task2) + + controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition())) + + val wct = + getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java) + assertThat(wct.hierarchyOps).hasSize(3) + // Expect order to be from bottom: wallpaper intent, task1, task2 + wct.assertPendingIntentAt(index = 0, desktopWallpaperIntent) + wct.assertReorderAt(index = 1, task1) + wct.assertReorderAt(index = 2, task2) + } + + @Test + @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY) + fun showDesktopApps_noActiveTasks_reorderHomeToTop_desktopWallpaperDisabled() { val homeTask = setUpHomeTask() controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition())) @@ -288,7 +360,18 @@ class DesktopTasksControllerTest : ShellTestCase() { } @Test - fun showDesktopApps_twoDisplays_bringsToFrontOnlyOneDisplay() { + @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY) + fun showDesktopApps_noActiveTasks_addDesktopWallpaper_desktopWallpaperEnabled() { + controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition())) + + val wct = + getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java) + wct.assertPendingIntentAt(index = 0, desktopWallpaperIntent) + } + + @Test + @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY) + fun showDesktopApps_twoDisplays_bringsToFrontOnlyOneDisplay_desktopWallpaperDisabled() { val homeTaskDefaultDisplay = setUpHomeTask(DEFAULT_DISPLAY) val taskDefaultDisplay = setUpFreeformTask(DEFAULT_DISPLAY) setUpHomeTask(SECOND_DISPLAY) @@ -307,6 +390,25 @@ class DesktopTasksControllerTest : ShellTestCase() { } @Test + @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY) + fun showDesktopApps_twoDisplays_bringsToFrontOnlyOneDisplay_desktopWallpaperEnabled() { + val taskDefaultDisplay = setUpFreeformTask(DEFAULT_DISPLAY) + setUpHomeTask(SECOND_DISPLAY) + val taskSecondDisplay = setUpFreeformTask(SECOND_DISPLAY) + markTaskHidden(taskDefaultDisplay) + markTaskHidden(taskSecondDisplay) + + controller.showDesktopApps(DEFAULT_DISPLAY, RemoteTransition(TestRemoteTransition())) + + val wct = + getLatestWct(type = TRANSIT_TO_FRONT, handlerClass = OneShotRemoteHandler::class.java) + assertThat(wct.hierarchyOps).hasSize(2) + // Expect order to be from bottom: wallpaper intent, task + wct.assertPendingIntentAt(index = 0, desktopWallpaperIntent) + wct.assertReorderAt(index = 1, taskDefaultDisplay) + } + + @Test fun getVisibleTaskCount_noTasks_returnsZero() { assertThat(controller.getVisibleTaskCount(DEFAULT_DISPLAY)).isEqualTo(0) } @@ -336,9 +438,10 @@ class DesktopTasksControllerTest : ShellTestCase() { } @Test - fun moveToDesktop_displayFullscreen_windowingModeSetToFreeform() { + fun moveToDesktop_tdaFullscreen_windowingModeSetToFreeform() { val task = setUpFullscreenTask() - task.configuration.windowConfiguration.displayWindowingMode = WINDOWING_MODE_FULLSCREEN + val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!! + tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN controller.moveToDesktop(task) val wct = getLatestMoveToDesktopWct() assertThat(wct.changes[task.token.asBinder()]?.windowingMode) @@ -346,9 +449,10 @@ class DesktopTasksControllerTest : ShellTestCase() { } @Test - fun moveToDesktop_displayFreeform_windowingModeSetToUndefined() { + fun moveToDesktop_tdaFreeform_windowingModeSetToUndefined() { val task = setUpFullscreenTask() - task.configuration.windowConfiguration.displayWindowingMode = WINDOWING_MODE_FREEFORM + val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!! + tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM controller.moveToDesktop(task) val wct = getLatestMoveToDesktopWct() assertThat(wct.changes[task.token.asBinder()]?.windowingMode) @@ -413,7 +517,8 @@ class DesktopTasksControllerTest : ShellTestCase() { } @Test - fun moveToDesktop_otherFreeformTasksBroughtToFront() { + @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY) + fun moveToDesktop_otherFreeformTasksBroughtToFront_desktopWallpaperDisabled() { val homeTask = setUpHomeTask() val freeformTask = setUpFreeformTask() val fullscreenTask = setUpFullscreenTask() @@ -431,6 +536,26 @@ class DesktopTasksControllerTest : ShellTestCase() { } @Test + @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY) + fun moveToDesktop_otherFreeformTasksBroughtToFront_desktopWallpaperEnabled() { + val freeformTask = setUpFreeformTask() + val fullscreenTask = setUpFullscreenTask() + markTaskHidden(freeformTask) + + controller.moveToDesktop(fullscreenTask) + + with(getLatestMoveToDesktopWct()) { + // Operations should include wallpaper intent, freeform task, fullscreen task + assertThat(hierarchyOps).hasSize(3) + assertPendingIntentAt(index = 0, desktopWallpaperIntent) + assertReorderAt(index = 1, freeformTask) + assertReorderAt(index = 2, fullscreenTask) + assertThat(changes[fullscreenTask.token.asBinder()]?.windowingMode) + .isEqualTo(WINDOWING_MODE_FREEFORM) + } + } + + @Test fun moveToDesktop_onlyFreeformTasksFromCurrentDisplayBroughtToFront() { setUpHomeTask(displayId = DEFAULT_DISPLAY) val freeformTaskDefault = setUpFreeformTask(displayId = DEFAULT_DISPLAY) @@ -481,9 +606,10 @@ class DesktopTasksControllerTest : ShellTestCase() { } @Test - fun moveToFullscreen_displayFullscreen_windowingModeSetToUndefined() { + fun moveToFullscreen_tdaFullscreen_windowingModeSetToUndefined() { val task = setUpFreeformTask() - task.configuration.windowConfiguration.displayWindowingMode = WINDOWING_MODE_FULLSCREEN + val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!! + tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN controller.moveToFullscreen(task.taskId) val wct = getLatestExitDesktopWct() assertThat(wct.changes[task.token.asBinder()]?.windowingMode) @@ -491,9 +617,10 @@ class DesktopTasksControllerTest : ShellTestCase() { } @Test - fun moveToFullscreen_displayFreeform_windowingModeSetToFullscreen() { + fun moveToFullscreen_tdaFreeform_windowingModeSetToFullscreen() { val task = setUpFreeformTask() - task.configuration.windowConfiguration.displayWindowingMode = WINDOWING_MODE_FREEFORM + val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!! + tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM controller.moveToFullscreen(task.taskId) val wct = getLatestExitDesktopWct() assertThat(wct.changes[task.token.asBinder()]?.windowingMode) @@ -595,6 +722,48 @@ class DesktopTasksControllerTest : ShellTestCase() { } @Test + fun onDesktopWindowClose_noActiveTasks() { + val wct = WindowContainerTransaction() + controller.onDesktopWindowClose(wct, 1 /* taskId */) + // Doesn't modify transaction + assertThat(wct.hierarchyOps).isEmpty() + } + + @Test + fun onDesktopWindowClose_singleActiveTask_noWallpaperActivityToken() { + val task = setUpFreeformTask() + val wct = WindowContainerTransaction() + controller.onDesktopWindowClose(wct, task.taskId) + // Doesn't modify transaction + assertThat(wct.hierarchyOps).isEmpty() + } + + @Test + fun onDesktopWindowClose_singleActiveTask_hasWallpaperActivityToken() { + val task = setUpFreeformTask() + val wallpaperToken = MockToken().token() + desktopModeTaskRepository.wallpaperActivityToken = wallpaperToken + + val wct = WindowContainerTransaction() + controller.onDesktopWindowClose(wct, task.taskId) + // Adds remove wallpaper operation + wct.assertRemoveAt(index = 0, wallpaperToken) + } + + @Test + fun onDesktopWindowClose_multipleActiveTasks() { + val task1 = setUpFreeformTask() + setUpFreeformTask() + val wallpaperToken = MockToken().token() + desktopModeTaskRepository.wallpaperActivityToken = wallpaperToken + + val wct = WindowContainerTransaction() + controller.onDesktopWindowClose(wct, task1.taskId) + // Doesn't modify transaction + assertThat(wct.hierarchyOps).isEmpty() + } + + @Test fun handleRequest_fullscreenTask_freeformVisible_returnSwitchToFreeformWCT() { assumeTrue(ENABLE_SHELL_TRANSITIONS) @@ -638,6 +807,7 @@ class DesktopTasksControllerTest : ShellTestCase() { } @Test + @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY) fun handleRequest_fullscreenTask_desktopStashed_returnWCTWithAllAppsBroughtToFront() { assumeTrue(ENABLE_SHELL_TRANSITIONS) whenever(DesktopModeStatus.isStashingEnabled()).thenReturn(true) @@ -684,7 +854,7 @@ class DesktopTasksControllerTest : ShellTestCase() { createTransition(freeformTask2, type = TRANSIT_TO_FRONT) ) assertThat(result?.changes?.get(freeformTask2.token.asBinder())?.windowingMode) - .isEqualTo(WINDOWING_MODE_FULLSCREEN) + .isEqualTo(WINDOWING_MODE_UNDEFINED) // inherited FULLSCREEN } @Test @@ -694,7 +864,7 @@ class DesktopTasksControllerTest : ShellTestCase() { val task = createFreeformTask() val result = controller.handleRequest(Binder(), createTransition(task)) assertThat(result?.changes?.get(task.token.asBinder())?.windowingMode) - .isEqualTo(WINDOWING_MODE_FULLSCREEN) + .isEqualTo(WINDOWING_MODE_UNDEFINED) // inherited FULLSCREEN } @Test @@ -706,10 +876,11 @@ class DesktopTasksControllerTest : ShellTestCase() { val result = controller.handleRequest(Binder(), createTransition(taskDefaultDisplay)) assertThat(result?.changes?.get(taskDefaultDisplay.token.asBinder())?.windowingMode) - .isEqualTo(WINDOWING_MODE_FULLSCREEN) + .isEqualTo(WINDOWING_MODE_UNDEFINED) // inherited FULLSCREEN } @Test + @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY) fun handleRequest_freeformTask_desktopStashed_returnWCTWithAllAppsBroughtToFront() { assumeTrue(ENABLE_SHELL_TRANSITIONS) whenever(DesktopModeStatus.isStashingEnabled()).thenReturn(true) @@ -792,7 +963,56 @@ class DesktopTasksControllerTest : ShellTestCase() { val result = controller.handleRequest(Binder(), createTransition(task)) assertThat(result?.changes?.get(task.token.asBinder())?.windowingMode) - .isEqualTo(WINDOWING_MODE_FULLSCREEN) + .isEqualTo(WINDOWING_MODE_UNDEFINED) // inherited FULLSCREEN + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY) + fun handleRequest_backTransition_singleActiveTask_noToken() { + val task = setUpFreeformTask() + val result = + controller.handleRequest(Binder(), createTransition(task, type = TRANSIT_TO_BACK)) + // Doesn't handle request + assertThat(result).isNull() + } + + @Test + @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY) + fun handleRequest_backTransition_singleActiveTask_hasToken_desktopWallpaperDisabled() { + desktopModeTaskRepository.wallpaperActivityToken = MockToken().token() + + val task = setUpFreeformTask() + val result = + controller.handleRequest(Binder(), createTransition(task, type = TRANSIT_TO_BACK)) + // Doesn't handle request + assertThat(result).isNull() + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY) + fun handleRequest_backTransition_singleActiveTask_hasToken_desktopWallpaperEnabled() { + val wallpaperToken = MockToken().token() + desktopModeTaskRepository.wallpaperActivityToken = wallpaperToken + + val task = setUpFreeformTask() + val result = + controller.handleRequest(Binder(), createTransition(task, type = TRANSIT_TO_BACK)) + assertThat(result).isNotNull() + // Creates remove wallpaper transaction + result!!.assertRemoveAt(index = 0, wallpaperToken) + } + + @Test + @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY) + fun handleRequest_backTransition_multipleActiveTasks() { + desktopModeTaskRepository.wallpaperActivityToken = MockToken().token() + + val task1 = setUpFreeformTask() + setUpFreeformTask() + val result = + controller.handleRequest(Binder(), createTransition(task1, type = TRANSIT_TO_BACK)) + // Doesn't handle request + assertThat(result).isNull() } @Test @@ -895,7 +1115,7 @@ class DesktopTasksControllerTest : ShellTestCase() { val wct = getLatestExitDesktopWct() assertThat(wct.changes[task2.token.asBinder()]?.windowingMode) - .isEqualTo(WINDOWING_MODE_FULLSCREEN) + .isEqualTo(WINDOWING_MODE_UNDEFINED) // inherited FULLSCREEN } @Test @@ -998,6 +1218,9 @@ class DesktopTasksControllerTest : ShellTestCase() { assertThat(desktopModeTaskRepository.removeBoundsBeforeMaximize(task.taskId)).isNull() } + private val desktopWallpaperIntent: Intent + get() = Intent(context, DesktopWallpaperActivity::class.java) + private fun setUpFreeformTask( displayId: Int = DEFAULT_DISPLAY, bounds: Rect? = null @@ -1123,10 +1346,14 @@ class DesktopTasksControllerTest : ShellTestCase() { } } -private fun WindowContainerTransaction.assertReorderAt(index: Int, task: RunningTaskInfo) { +private fun WindowContainerTransaction.assertIndexInBounds(index: Int) { assertWithMessage("WCT does not have a hierarchy operation at index $index") .that(hierarchyOps.size) .isGreaterThan(index) +} + +private fun WindowContainerTransaction.assertReorderAt(index: Int, task: RunningTaskInfo) { + assertIndexInBounds(index) val op = hierarchyOps[index] assertThat(op.type).isEqualTo(HIERARCHY_OP_TYPE_REORDER) assertThat(op.container).isEqualTo(task.token.asBinder()) @@ -1137,3 +1364,17 @@ private fun WindowContainerTransaction.assertReorderSequence(vararg tasks: Runni assertReorderAt(i, tasks[i]) } } + +private fun WindowContainerTransaction.assertRemoveAt(index: Int, token: WindowContainerToken) { + assertIndexInBounds(index) + val op = hierarchyOps[index] + assertThat(op.type).isEqualTo(HIERARCHY_OP_TYPE_REMOVE_TASK) + assertThat(op.container).isEqualTo(token.asBinder()) +} + +private fun WindowContainerTransaction.assertPendingIntentAt(index: Int, intent: Intent) { + assertIndexInBounds(index) + val op = hierarchyOps[index] + assertThat(op.type).isEqualTo(HIERARCHY_OP_TYPE_PENDING_INTENT) + assertThat(op.pendingIntent?.intent?.component).isEqualTo(intent.component) +} diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java index e7d37addb368..0db10ef65a74 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/HomeTransitionObserverTest.java @@ -24,6 +24,7 @@ import static android.view.WindowManager.TRANSIT_TO_BACK; import static android.window.TransitionInfo.FLAG_BACK_GESTURE_ANIMATED; import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; +import static com.android.wm.shell.transition.Transitions.TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.Mockito.mock; @@ -167,6 +168,25 @@ public class HomeTransitionObserverTest extends ShellTestCase { } @Test + public void testStartDragToDesktopDoesNotTriggerCallback() throws RemoteException { + TransitionInfo info = mock(TransitionInfo.class); + TransitionInfo.Change change = mock(TransitionInfo.Change.class); + ActivityManager.RunningTaskInfo taskInfo = mock(ActivityManager.RunningTaskInfo.class); + when(change.getTaskInfo()).thenReturn(taskInfo); + when(info.getChanges()).thenReturn(new ArrayList<>(List.of(change))); + when(info.getType()).thenReturn(TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP); + + setupTransitionInfo(taskInfo, change, ACTIVITY_TYPE_HOME, TRANSIT_OPEN, true); + + mHomeTransitionObserver.onTransitionReady(mock(IBinder.class), + info, + mock(SurfaceControl.Transaction.class), + mock(SurfaceControl.Transaction.class)); + + verify(mListener, times(0)).onHomeVisibilityChanged(anyBoolean()); + } + + @Test public void testHomeActivityWithBackGestureNotifiesHomeIsVisible() throws RemoteException { TransitionInfo info = mock(TransitionInfo.class); TransitionInfo.Change change = mock(TransitionInfo.Change.class); diff --git a/libs/androidfw/ZipFileRO.cpp b/libs/androidfw/ZipFileRO.cpp index 34a6bc27b93f..839c7b6fef37 100644 --- a/libs/androidfw/ZipFileRO.cpp +++ b/libs/androidfw/ZipFileRO.cpp @@ -119,30 +119,41 @@ ZipEntryRO ZipFileRO::findEntryByName(const char* entryName) const * appear to be bogus. */ bool ZipFileRO::getEntryInfo(ZipEntryRO entry, uint16_t* pMethod, + uint32_t* pUncompLen, uint32_t* pCompLen, off64_t* pOffset, + uint32_t* pModWhen, uint32_t* pCrc32) const +{ + return getEntryInfo(entry, pMethod, pUncompLen, pCompLen, pOffset, pModWhen, + pCrc32, nullptr); +} + +bool ZipFileRO::getEntryInfo(ZipEntryRO entry, uint16_t* pMethod, uint32_t* pUncompLen, uint32_t* pCompLen, off64_t* pOffset, - uint32_t* pModWhen, uint32_t* pCrc32) const + uint32_t* pModWhen, uint32_t* pCrc32, uint16_t* pExtraFieldSize) const { const _ZipEntryRO* zipEntry = reinterpret_cast<_ZipEntryRO*>(entry); const ZipEntry& ze = zipEntry->entry; - if (pMethod != NULL) { + if (pMethod != nullptr) { *pMethod = ze.method; } - if (pUncompLen != NULL) { + if (pUncompLen != nullptr) { *pUncompLen = ze.uncompressed_length; } - if (pCompLen != NULL) { + if (pCompLen != nullptr) { *pCompLen = ze.compressed_length; } - if (pOffset != NULL) { + if (pOffset != nullptr) { *pOffset = ze.offset; } - if (pModWhen != NULL) { + if (pModWhen != nullptr) { *pModWhen = ze.mod_time; } - if (pCrc32 != NULL) { + if (pCrc32 != nullptr) { *pCrc32 = ze.crc32; } + if (pExtraFieldSize != nullptr) { + *pExtraFieldSize = ze.extra_field_size; + } return true; } diff --git a/libs/androidfw/include/androidfw/ZipFileRO.h b/libs/androidfw/include/androidfw/ZipFileRO.h index 031d2e8fd48f..f7c5007c80d2 100644 --- a/libs/androidfw/include/androidfw/ZipFileRO.h +++ b/libs/androidfw/include/androidfw/ZipFileRO.h @@ -151,6 +151,10 @@ public: uint32_t* pCompLen, off64_t* pOffset, uint32_t* pModWhen, uint32_t* pCrc32) const; + bool getEntryInfo(ZipEntryRO entry, uint16_t* pMethod, + uint32_t* pUncompLen, uint32_t* pCompLen, off64_t* pOffset, + uint32_t* pModWhen, uint32_t* pCrc32, uint16_t* pExtraFieldSize) const; + /* * Create a new FileMap object that maps a subset of the archive. For * an uncompressed entry this effectively provides a pointer to the diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp index 34932b1b1e25..dc669a5eca73 100644 --- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp @@ -24,7 +24,6 @@ #include <SkImageAndroid.h> #include <SkImageInfo.h> #include <SkMatrix.h> -#include <SkMultiPictureDocument.h> #include <SkOverdrawCanvas.h> #include <SkOverdrawColorFilter.h> #include <SkPicture.h> @@ -38,6 +37,7 @@ #include <android-base/properties.h> #include <gui/TraceUtils.h> #include <include/android/SkSurfaceAndroid.h> +#include <include/docs/SkMultiPictureDocument.h> #include <include/encode/SkPngEncoder.h> #include <include/gpu/ganesh/SkSurfaceGanesh.h> #include <unistd.h> @@ -185,7 +185,7 @@ bool SkiaPipeline::setupMultiFrameCapture() { // we need to keep it until after mMultiPic.close() // procs is passed as a pointer, but just as a method of having an optional default. // procs doesn't need to outlive this Make call. - mMultiPic = SkMakeMultiPictureDocument(mOpenMultiPicStream.get(), &procs, + mMultiPic = SkMultiPictureDocument::Make(mOpenMultiPicStream.get(), &procs, [sharingCtx = mSerialContext.get()](const SkPicture* pic) { SkSharingSerialContext::collectNonTextureImagesFromPicture(pic, sharingCtx); }); diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.h b/libs/hwui/pipeline/skia/SkiaPipeline.h index cf14b1f9ebe3..823b209017a5 100644 --- a/libs/hwui/pipeline/skia/SkiaPipeline.h +++ b/libs/hwui/pipeline/skia/SkiaPipeline.h @@ -18,7 +18,6 @@ #include <SkColorSpace.h> #include <SkDocument.h> -#include <SkMultiPictureDocument.h> #include <SkSurface.h> #include "Lighting.h" diff --git a/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp b/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp index b62711f50c94..21fe6ff14f56 100644 --- a/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp +++ b/libs/hwui/pipeline/skia/VkFunctorDrawable.cpp @@ -16,10 +16,10 @@ #include "VkFunctorDrawable.h" -#include <GrBackendDrawableInfo.h> #include <SkAndroidFrameworkUtils.h> #include <SkImage.h> #include <SkM44.h> +#include <include/gpu/ganesh/vk/GrBackendDrawableInfo.h> #include <gui/TraceUtils.h> #include <private/hwui/DrawVkInfo.h> #include <utils/Color.h> diff --git a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java index c836df3b2c4d..520b76e07a0a 100644 --- a/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java +++ b/media/tests/MediaRouter/src/com/android/mediaroutertest/MediaRouter2ManagerTest.java @@ -361,6 +361,7 @@ public class MediaRouter2ManagerTest { * Tests if MR2.SessionCallback.onSessionCreated is called * when a route is selected from MR2Manager. */ + @Ignore // Ignored due to flakiness. No plans to fix though, in favor of removal (b/334970551). @Test public void testRouterOnSessionCreated() throws Exception { Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(FEATURES_ALL); @@ -512,6 +513,7 @@ public class MediaRouter2ManagerTest { /** * Tests select, transfer, release of routes of a provider */ + @Ignore // Ignored due to flakiness. No plans to fix though, in favor of removal (b/334970551). @Test public void testSelectAndTransferAndRelease() throws Exception { Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(FEATURES_ALL); @@ -908,6 +910,7 @@ public class MediaRouter2ManagerTest { * Tests if getSelectableRoutes and getDeselectableRoutes filter routes based on * selected routes */ + @Ignore // Ignored due to flakiness. No plans to fix though, in favor of removal (b/334970551). @Test public void testGetSelectableRoutes_notReturnsSelectedRoutes() throws Exception { Map<String, MediaRoute2Info> routes = waitAndGetRoutesWithManager(FEATURES_ALL); diff --git a/packages/CompanionDeviceManager/res/values-hi/strings.xml b/packages/CompanionDeviceManager/res/values-hi/strings.xml index 2363886c3484..92d94f3ffd64 100644 --- a/packages/CompanionDeviceManager/res/values-hi/strings.xml +++ b/packages/CompanionDeviceManager/res/values-hi/strings.xml @@ -59,7 +59,7 @@ <string name="permission_microphone" msgid="2152206421428732949">"माइक्रोफ़ोन"</string> <string name="permission_call_logs" msgid="5546761417694586041">"कॉल लॉग"</string> <string name="permission_nearby_devices" msgid="7530973297737123481">"आस-पास मौजूद डिवाइस"</string> - <string name="permission_media_routing_control" msgid="5498639511586715253">"मीडिया आउटपुट बदलें"</string> + <string name="permission_media_routing_control" msgid="5498639511586715253">"मीडिया आउटपुट में बदलाव करे"</string> <string name="permission_storage" msgid="6831099350839392343">"फ़ोटो और मीडिया"</string> <string name="permission_notifications" msgid="4099418516590632909">"सूचनाएं"</string> <string name="permission_app_streaming" msgid="6009695219091526422">"ऐप्लिकेशन"</string> diff --git a/packages/CredentialManager/res/values-be/strings.xml b/packages/CredentialManager/res/values-be/strings.xml index d27f68bf56de..0b8ade7f64ec 100644 --- a/packages/CredentialManager/res/values-be/strings.xml +++ b/packages/CredentialManager/res/values-be/strings.xml @@ -42,12 +42,9 @@ <string name="choose_create_option_passkey_title" msgid="8762295821604276511">"Стварыць ключ доступу для ўваходу ў праграму \"<xliff:g id="APP_NAME">%1$s</xliff:g>\"?"</string> <string name="choose_create_option_password_title" msgid="4481366993598649224">"Захаваць пароль для ўваходу ў праграму \"<xliff:g id="APP_NAME">%1$s</xliff:g>\"?"</string> <string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"Захаваць даныя для ўваходу ў праграму \"<xliff:g id="APP_NAME">%1$s</xliff:g>\"?"</string> - <!-- no translation found for choose_create_single_tap_passkey_title (3872793514041774218) --> - <skip /> - <!-- no translation found for choose_create_single_tap_password_title (5231871886818921622) --> - <skip /> - <!-- no translation found for choose_create_single_tap_sign_in_title (256498714574099587) --> - <skip /> + <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"Выкарыстаць сродак разблакіроўкі экрана, каб стварыць ключ доступу для праграмы \"<xliff:g id="APP_NAME">%1$s</xliff:g>\"?"</string> + <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"Выкарыстаць сродак разблакіроўкі экрана, каб стварыць пароль для праграмы \"<xliff:g id="APP_NAME">%1$s</xliff:g>\"?"</string> + <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"Выкарыстаць сродак разблакіроўкі экрана, каб захаваць даныя для ўваходу для праграмы \"<xliff:g id="APP_NAME">%1$s</xliff:g>\"?"</string> <string name="passkey" msgid="632353688396759522">"ключ доступу"</string> <string name="password" msgid="6738570945182936667">"пароль"</string> <string name="passkeys" msgid="5733880786866559847">"ключы доступу"</string> diff --git a/packages/CredentialManager/res/values-bg/strings.xml b/packages/CredentialManager/res/values-bg/strings.xml index 780699b450f7..6a79b11d2ab3 100644 --- a/packages/CredentialManager/res/values-bg/strings.xml +++ b/packages/CredentialManager/res/values-bg/strings.xml @@ -42,12 +42,9 @@ <string name="choose_create_option_passkey_title" msgid="8762295821604276511">"Искате ли да създадете ключ за достъп, с който да влизате в(ъв) <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="choose_create_option_password_title" msgid="4481366993598649224">"Искате ли да запазите паролата за влизане в(ъв) <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"Искате ли да запазите данните за вход за <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> - <!-- no translation found for choose_create_single_tap_passkey_title (3872793514041774218) --> - <skip /> - <!-- no translation found for choose_create_single_tap_password_title (5231871886818921622) --> - <skip /> - <!-- no translation found for choose_create_single_tap_sign_in_title (256498714574099587) --> - <skip /> + <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"Искате ли да използвате опцията си за заключване на екрана, за да създадете ключ за достъп за <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"Искате ли да използвате опцията си за заключване на екрана, за да създадете парола за <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"Искате ли да използвате опцията си за заключване на екрана, за да запазите данните за вход за <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="passkey" msgid="632353688396759522">"код за достъп"</string> <string name="password" msgid="6738570945182936667">"парола"</string> <string name="passkeys" msgid="5733880786866559847">"ключове за достъп"</string> diff --git a/packages/CredentialManager/res/values-bn/strings.xml b/packages/CredentialManager/res/values-bn/strings.xml index 9d429d4746a2..76fc0a71d7e4 100644 --- a/packages/CredentialManager/res/values-bn/strings.xml +++ b/packages/CredentialManager/res/values-bn/strings.xml @@ -42,12 +42,9 @@ <string name="choose_create_option_passkey_title" msgid="8762295821604276511">"<xliff:g id="APP_NAME">%1$s</xliff:g> অ্যাপে সাইন-ইন করার জন্য পাসকী তৈরি করবেন?"</string> <string name="choose_create_option_password_title" msgid="4481366993598649224">"<xliff:g id="APP_NAME">%1$s</xliff:g> অ্যাপে সাইন-ইন করার জন্য পাসওয়ার্ড সেভ করবেন?"</string> <string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"<xliff:g id="APP_NAME">%1$s</xliff:g> অ্যাপের জন্য সাইন-ইন সংক্রান্ত তথ্য সেভ করবেন?"</string> - <!-- no translation found for choose_create_single_tap_passkey_title (3872793514041774218) --> - <skip /> - <!-- no translation found for choose_create_single_tap_password_title (5231871886818921622) --> - <skip /> - <!-- no translation found for choose_create_single_tap_sign_in_title (256498714574099587) --> - <skip /> + <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"<xliff:g id="APP_NAME">%1$s</xliff:g> অ্যাপের জন্য পাসকী তৈরি করতে আপনার স্ক্রিন লক ব্যবহার করতে চান?"</string> + <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"<xliff:g id="APP_NAME">%1$s</xliff:g> অ্যাপের জন্য পাসওয়ার্ড তৈরি করতে আপনার স্ক্রিন লক ব্যবহার করতে চান?"</string> + <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"<xliff:g id="APP_NAME">%1$s</xliff:g> অ্যাপের জন্য সাইন-ইন সংক্রান্ত তথ্য সেভ করতে আপনার স্ক্রিন লক ব্যবহার করতে চান?"</string> <string name="passkey" msgid="632353688396759522">"পাসকী"</string> <string name="password" msgid="6738570945182936667">"পাসওয়ার্ড"</string> <string name="passkeys" msgid="5733880786866559847">"পাসকী"</string> diff --git a/packages/CredentialManager/res/values-bs/strings.xml b/packages/CredentialManager/res/values-bs/strings.xml index 00c211b48e01..6d3a676e6a17 100644 --- a/packages/CredentialManager/res/values-bs/strings.xml +++ b/packages/CredentialManager/res/values-bs/strings.xml @@ -42,9 +42,9 @@ <string name="choose_create_option_passkey_title" msgid="8762295821604276511">"Kreirati pristupni ključ da se prijavite u aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="choose_create_option_password_title" msgid="4481366993598649224">"Sačuvati lozinku da se prijavite u aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"Sačuvati podatke za prijavu u aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> - <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"Želite li upotrijebiti zaključavanje zaslona za izradu pristupnog ključa za aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> - <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"Želite li upotrijebiti zaključavanje zaslona za izradu zaporke za aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> - <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"Želite li upotrijebiti zaključavanje zaslona za spremanje podataka za prijavu za aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"Koristiti zaključavanje ekrana da kreirate pristupni ključ za aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"Koristiti zaključavanje ekrana da kreirate lozinku za aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"Koristiti zaključavanje ekrana da sačuvate podatke za prijavu za aplikaciju <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="passkey" msgid="632353688396759522">"pristupni ključ"</string> <string name="password" msgid="6738570945182936667">"lozinka"</string> <string name="passkeys" msgid="5733880786866559847">"pristupni ključevi"</string> diff --git a/packages/CredentialManager/res/values-ca/strings.xml b/packages/CredentialManager/res/values-ca/strings.xml index cf08741ebc2f..28762e75ac71 100644 --- a/packages/CredentialManager/res/values-ca/strings.xml +++ b/packages/CredentialManager/res/values-ca/strings.xml @@ -42,12 +42,9 @@ <string name="choose_create_option_passkey_title" msgid="8762295821604276511">"Vols crear una clau d\'accés per iniciar la sessió a <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="choose_create_option_password_title" msgid="4481366993598649224">"Vols desar la contrasenya per iniciar la sessió a <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"Vols desar la informació d\'inici de sessió per a <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> - <!-- no translation found for choose_create_single_tap_passkey_title (3872793514041774218) --> - <skip /> - <!-- no translation found for choose_create_single_tap_password_title (5231871886818921622) --> - <skip /> - <!-- no translation found for choose_create_single_tap_sign_in_title (256498714574099587) --> - <skip /> + <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"Vols fer servir el bloqueig de pantalla per crear una clau d\'accés per a <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"Vols fer servir el bloqueig de pantalla per crear una contrasenya per a <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"Vols fer servir el bloqueig de pantalla per desar la informació d\'inici de sessió de <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="passkey" msgid="632353688396759522">"clau d\'accés"</string> <string name="password" msgid="6738570945182936667">"contrasenya"</string> <string name="passkeys" msgid="5733880786866559847">"claus d\'accés"</string> diff --git a/packages/CredentialManager/res/values-cs/strings.xml b/packages/CredentialManager/res/values-cs/strings.xml index 5e0d42e2d25f..a3ff390a12b5 100644 --- a/packages/CredentialManager/res/values-cs/strings.xml +++ b/packages/CredentialManager/res/values-cs/strings.xml @@ -42,12 +42,9 @@ <string name="choose_create_option_passkey_title" msgid="8762295821604276511">"Vytvořit přístupový klíč k přihlašování do aplikace <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="choose_create_option_password_title" msgid="4481366993598649224">"Uložit heslo k přihlašování do aplikace <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"Uložit přihlašovací údaje pro aplikaci <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> - <!-- no translation found for choose_create_single_tap_passkey_title (3872793514041774218) --> - <skip /> - <!-- no translation found for choose_create_single_tap_password_title (5231871886818921622) --> - <skip /> - <!-- no translation found for choose_create_single_tap_sign_in_title (256498714574099587) --> - <skip /> + <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"Chcete pomocí zámku obrazovky vytvořit přístupový klíč pro aplikaci <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"Chcete pomocí zámku obrazovky vytvořit heslo pro aplikaci <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"Chcete pomocí zámku obrazovky uložit přihlašovací údaje pro aplikaci <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="passkey" msgid="632353688396759522">"přístupový klíč"</string> <string name="password" msgid="6738570945182936667">"heslo"</string> <string name="passkeys" msgid="5733880786866559847">"přístupové klíče"</string> diff --git a/packages/CredentialManager/res/values-da/strings.xml b/packages/CredentialManager/res/values-da/strings.xml index e978b8e86890..b61b81c646d4 100644 --- a/packages/CredentialManager/res/values-da/strings.xml +++ b/packages/CredentialManager/res/values-da/strings.xml @@ -42,12 +42,9 @@ <string name="choose_create_option_passkey_title" msgid="8762295821604276511">"Vil du oprette en adgangsnøgle for at logge ind på <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="choose_create_option_password_title" msgid="4481366993598649224">"Vil du gemme adgangskoden for at logge ind på <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"Vil du gemme loginoplysningerne til <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> - <!-- no translation found for choose_create_single_tap_passkey_title (3872793514041774218) --> - <skip /> - <!-- no translation found for choose_create_single_tap_password_title (5231871886818921622) --> - <skip /> - <!-- no translation found for choose_create_single_tap_sign_in_title (256498714574099587) --> - <skip /> + <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"Vil du bruge din skærmlås til at oprette en adgangsnøgle til <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"Vil du bruge din skærmlås til at oprette en adgangskode til <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"Vil du bruge din skærmlås til at gemme loginoplysningerne til <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="passkey" msgid="632353688396759522">"adgangsnøgle"</string> <string name="password" msgid="6738570945182936667">"adgangskode"</string> <string name="passkeys" msgid="5733880786866559847">"adgangsnøgler"</string> diff --git a/packages/CredentialManager/res/values-de/strings.xml b/packages/CredentialManager/res/values-de/strings.xml index b338b1d92e48..98974439c2da 100644 --- a/packages/CredentialManager/res/values-de/strings.xml +++ b/packages/CredentialManager/res/values-de/strings.xml @@ -42,12 +42,9 @@ <string name="choose_create_option_passkey_title" msgid="8762295821604276511">"Passkey zur Anmeldung in <xliff:g id="APP_NAME">%1$s</xliff:g> erstellen?"</string> <string name="choose_create_option_password_title" msgid="4481366993598649224">"Passwort zur Anmeldung in <xliff:g id="APP_NAME">%1$s</xliff:g> speichern?"</string> <string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"Anmeldedaten für <xliff:g id="APP_NAME">%1$s</xliff:g> speichern?"</string> - <!-- no translation found for choose_create_single_tap_passkey_title (3872793514041774218) --> - <skip /> - <!-- no translation found for choose_create_single_tap_password_title (5231871886818921622) --> - <skip /> - <!-- no translation found for choose_create_single_tap_sign_in_title (256498714574099587) --> - <skip /> + <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"Displaysperre verwenden, um einen Passkey für <xliff:g id="APP_NAME">%1$s</xliff:g> zu erstellen?"</string> + <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"Displaysperre verwenden, um ein Passwort für <xliff:g id="APP_NAME">%1$s</xliff:g> zu erstellen?"</string> + <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"Displaysperre verwenden, um Anmeldedaten für <xliff:g id="APP_NAME">%1$s</xliff:g> zu speichern?"</string> <string name="passkey" msgid="632353688396759522">"Passkey"</string> <string name="password" msgid="6738570945182936667">"Passwort"</string> <string name="passkeys" msgid="5733880786866559847">"Passkeys"</string> diff --git a/packages/CredentialManager/res/values-el/strings.xml b/packages/CredentialManager/res/values-el/strings.xml index 29ad56951449..b1c3506a1b4f 100644 --- a/packages/CredentialManager/res/values-el/strings.xml +++ b/packages/CredentialManager/res/values-el/strings.xml @@ -42,12 +42,9 @@ <string name="choose_create_option_passkey_title" msgid="8762295821604276511">"Δημιουργία κλειδιού πρόσβασης για σύνδεση στην εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g>;"</string> <string name="choose_create_option_password_title" msgid="4481366993598649224">"Αποθήκευση κωδικού πρόσβασης για σύνδεση στην εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g>;"</string> <string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"Αποθήκευση πληροφοριών σύνδεσης για την εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g>;"</string> - <!-- no translation found for choose_create_single_tap_passkey_title (3872793514041774218) --> - <skip /> - <!-- no translation found for choose_create_single_tap_password_title (5231871886818921622) --> - <skip /> - <!-- no translation found for choose_create_single_tap_sign_in_title (256498714574099587) --> - <skip /> + <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"Θέλετε να χρησιμοποιήσετε το κλείδωμα οθόνης για τη δημιουργία ενός κλειδιού πρόσβασης για την εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g>;"</string> + <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"Θέλετε να χρησιμοποιήσετε το κλείδωμα οθόνης για τη δημιουργία ενός κωδικού πρόσβασης για την εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g>;"</string> + <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"Θέλετε να χρησιμοποιήσετε το κλείδωμα οθόνης για την αποθήκευση πληροφοριών σύνδεσης για την εφαρμογή <xliff:g id="APP_NAME">%1$s</xliff:g>;"</string> <string name="passkey" msgid="632353688396759522">"κλειδί πρόσβασης"</string> <string name="password" msgid="6738570945182936667">"κωδικός πρόσβασης"</string> <string name="passkeys" msgid="5733880786866559847">"κλειδιά πρόσβασης"</string> diff --git a/packages/CredentialManager/res/values-en-rAU/strings.xml b/packages/CredentialManager/res/values-en-rAU/strings.xml index b8bef997a383..1afd5f6d7d6e 100644 --- a/packages/CredentialManager/res/values-en-rAU/strings.xml +++ b/packages/CredentialManager/res/values-en-rAU/strings.xml @@ -42,12 +42,9 @@ <string name="choose_create_option_passkey_title" msgid="8762295821604276511">"Create passkey to sign in to <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="choose_create_option_password_title" msgid="4481366993598649224">"Save password to sign in to <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"Save sign-in info for <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> - <!-- no translation found for choose_create_single_tap_passkey_title (3872793514041774218) --> - <skip /> - <!-- no translation found for choose_create_single_tap_password_title (5231871886818921622) --> - <skip /> - <!-- no translation found for choose_create_single_tap_sign_in_title (256498714574099587) --> - <skip /> + <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"Use your screen lock to create a passkey for <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"Use your screen lock to create a password for <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"Use your screen lock to save sign-in info for <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="passkey" msgid="632353688396759522">"passkey"</string> <string name="password" msgid="6738570945182936667">"password"</string> <string name="passkeys" msgid="5733880786866559847">"passkeys"</string> diff --git a/packages/CredentialManager/res/values-en-rGB/strings.xml b/packages/CredentialManager/res/values-en-rGB/strings.xml index b8bef997a383..1afd5f6d7d6e 100644 --- a/packages/CredentialManager/res/values-en-rGB/strings.xml +++ b/packages/CredentialManager/res/values-en-rGB/strings.xml @@ -42,12 +42,9 @@ <string name="choose_create_option_passkey_title" msgid="8762295821604276511">"Create passkey to sign in to <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="choose_create_option_password_title" msgid="4481366993598649224">"Save password to sign in to <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"Save sign-in info for <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> - <!-- no translation found for choose_create_single_tap_passkey_title (3872793514041774218) --> - <skip /> - <!-- no translation found for choose_create_single_tap_password_title (5231871886818921622) --> - <skip /> - <!-- no translation found for choose_create_single_tap_sign_in_title (256498714574099587) --> - <skip /> + <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"Use your screen lock to create a passkey for <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"Use your screen lock to create a password for <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"Use your screen lock to save sign-in info for <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="passkey" msgid="632353688396759522">"passkey"</string> <string name="password" msgid="6738570945182936667">"password"</string> <string name="passkeys" msgid="5733880786866559847">"passkeys"</string> diff --git a/packages/CredentialManager/res/values-en-rIN/strings.xml b/packages/CredentialManager/res/values-en-rIN/strings.xml index b8bef997a383..1afd5f6d7d6e 100644 --- a/packages/CredentialManager/res/values-en-rIN/strings.xml +++ b/packages/CredentialManager/res/values-en-rIN/strings.xml @@ -42,12 +42,9 @@ <string name="choose_create_option_passkey_title" msgid="8762295821604276511">"Create passkey to sign in to <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="choose_create_option_password_title" msgid="4481366993598649224">"Save password to sign in to <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"Save sign-in info for <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> - <!-- no translation found for choose_create_single_tap_passkey_title (3872793514041774218) --> - <skip /> - <!-- no translation found for choose_create_single_tap_password_title (5231871886818921622) --> - <skip /> - <!-- no translation found for choose_create_single_tap_sign_in_title (256498714574099587) --> - <skip /> + <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"Use your screen lock to create a passkey for <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"Use your screen lock to create a password for <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"Use your screen lock to save sign-in info for <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="passkey" msgid="632353688396759522">"passkey"</string> <string name="password" msgid="6738570945182936667">"password"</string> <string name="passkeys" msgid="5733880786866559847">"passkeys"</string> diff --git a/packages/CredentialManager/res/values-es-rUS/strings.xml b/packages/CredentialManager/res/values-es-rUS/strings.xml index 3b4392d924e7..e007ab7a617c 100644 --- a/packages/CredentialManager/res/values-es-rUS/strings.xml +++ b/packages/CredentialManager/res/values-es-rUS/strings.xml @@ -42,12 +42,9 @@ <string name="choose_create_option_passkey_title" msgid="8762295821604276511">"¿Quieres crear una llave de acceso para acceder a <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="choose_create_option_password_title" msgid="4481366993598649224">"¿Quieres guardar la contraseña para acceder a <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"¿Quieres guardar la información de acceso para <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> - <!-- no translation found for choose_create_single_tap_passkey_title (3872793514041774218) --> - <skip /> - <!-- no translation found for choose_create_single_tap_password_title (5231871886818921622) --> - <skip /> - <!-- no translation found for choose_create_single_tap_sign_in_title (256498714574099587) --> - <skip /> + <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"¿Quieres usar el bloqueo de pantalla para crear una llave de acceso para <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"¿Quieres usar el bloqueo de pantalla para crear una contraseña para <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"¿Quieres usar el bloqueo de pantalla para guardar la información de acceso para <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="passkey" msgid="632353688396759522">"llave de acceso"</string> <string name="password" msgid="6738570945182936667">"contraseña"</string> <string name="passkeys" msgid="5733880786866559847">"llaves de acceso"</string> diff --git a/packages/CredentialManager/res/values-es/strings.xml b/packages/CredentialManager/res/values-es/strings.xml index 427ed1d69c25..e82f33162bef 100644 --- a/packages/CredentialManager/res/values-es/strings.xml +++ b/packages/CredentialManager/res/values-es/strings.xml @@ -42,12 +42,9 @@ <string name="choose_create_option_passkey_title" msgid="8762295821604276511">"¿Crear llave de acceso para iniciar sesión en <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="choose_create_option_password_title" msgid="4481366993598649224">"¿Guardar contraseña para iniciar sesión en <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"¿Guardar la información de inicio de sesión de <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> - <!-- no translation found for choose_create_single_tap_passkey_title (3872793514041774218) --> - <skip /> - <!-- no translation found for choose_create_single_tap_password_title (5231871886818921622) --> - <skip /> - <!-- no translation found for choose_create_single_tap_sign_in_title (256498714574099587) --> - <skip /> + <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"¿Usar tu bloqueo de pantalla para crear una llave de acceso para <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"¿Usar tu bloqueo de pantalla para crear una contraseña para <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"¿Usar tu bloqueo de pantalla para guardar tu información de inicio de sesión para <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="passkey" msgid="632353688396759522">"llave de acceso"</string> <string name="password" msgid="6738570945182936667">"contraseña"</string> <string name="passkeys" msgid="5733880786866559847">"llaves de acceso"</string> diff --git a/packages/CredentialManager/res/values-et/strings.xml b/packages/CredentialManager/res/values-et/strings.xml index 44b907e4b08d..a4c3438969cd 100644 --- a/packages/CredentialManager/res/values-et/strings.xml +++ b/packages/CredentialManager/res/values-et/strings.xml @@ -42,12 +42,9 @@ <string name="choose_create_option_passkey_title" msgid="8762295821604276511">"Kas luua rakendusse <xliff:g id="APP_NAME">%1$s</xliff:g> sisselogimiseks pääsuvõti?"</string> <string name="choose_create_option_password_title" msgid="4481366993598649224">"Kas salvestada rakendusse <xliff:g id="APP_NAME">%1$s</xliff:g> sisselogimiseks parool?"</string> <string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"Kas salvestada rakenduse <xliff:g id="APP_NAME">%1$s</xliff:g> jaoks sisselogimisteave?"</string> - <!-- no translation found for choose_create_single_tap_passkey_title (3872793514041774218) --> - <skip /> - <!-- no translation found for choose_create_single_tap_password_title (5231871886818921622) --> - <skip /> - <!-- no translation found for choose_create_single_tap_sign_in_title (256498714574099587) --> - <skip /> + <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"Kas kasutada ekraanilukku, et luua rakenduse <xliff:g id="APP_NAME">%1$s</xliff:g> jaoks pääsuvõti?"</string> + <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"Kas kasutada ekraanilukku, et luua rakenduse <xliff:g id="APP_NAME">%1$s</xliff:g> jaoks parool?"</string> + <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"Kas kasutada ekraanilukku, et salvestada rakenduse <xliff:g id="APP_NAME">%1$s</xliff:g> jaoks sisselogimisteave?"</string> <string name="passkey" msgid="632353688396759522">"pääsuvõti"</string> <string name="password" msgid="6738570945182936667">"parool"</string> <string name="passkeys" msgid="5733880786866559847">"pääsuvõtmed"</string> diff --git a/packages/CredentialManager/res/values-eu/strings.xml b/packages/CredentialManager/res/values-eu/strings.xml index afc91ea2cdb2..2f62ba3add02 100644 --- a/packages/CredentialManager/res/values-eu/strings.xml +++ b/packages/CredentialManager/res/values-eu/strings.xml @@ -42,12 +42,9 @@ <string name="choose_create_option_passkey_title" msgid="8762295821604276511">"<xliff:g id="APP_NAME">%1$s</xliff:g> aplikazioan saioa hasteko sarbide-gako bat sortu nahi duzu?"</string> <string name="choose_create_option_password_title" msgid="4481366993598649224">"<xliff:g id="APP_NAME">%1$s</xliff:g> aplikazioan saioa hasteko pasahitza gorde nahi duzu?"</string> <string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"<xliff:g id="APP_NAME">%1$s</xliff:g> aplikazioan saioa hasteko informazioa gorde nahi duzu?"</string> - <!-- no translation found for choose_create_single_tap_passkey_title (3872793514041774218) --> - <skip /> - <!-- no translation found for choose_create_single_tap_password_title (5231871886818921622) --> - <skip /> - <!-- no translation found for choose_create_single_tap_sign_in_title (256498714574099587) --> - <skip /> + <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"Pantailaren blokeoa erabili nahi duzu <xliff:g id="APP_NAME">%1$s</xliff:g> aplikaziorako sarbide-gako bat sortzeko?"</string> + <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"Pantailaren blokeoa erabili nahi duzu <xliff:g id="APP_NAME">%1$s</xliff:g> aplikaziorako pasahitz bat sortzeko?"</string> + <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"Pantailaren blokeoa erabili nahi duzu <xliff:g id="APP_NAME">%1$s</xliff:g> aplikazioan saioa hasteko informazioa gordetzeko?"</string> <string name="passkey" msgid="632353688396759522">"sarbide-gakoa"</string> <string name="password" msgid="6738570945182936667">"pasahitza"</string> <string name="passkeys" msgid="5733880786866559847">"sarbide-gakoak"</string> diff --git a/packages/CredentialManager/res/values-fa/strings.xml b/packages/CredentialManager/res/values-fa/strings.xml index 095cb0614ca5..6266ed2a4ccb 100644 --- a/packages/CredentialManager/res/values-fa/strings.xml +++ b/packages/CredentialManager/res/values-fa/strings.xml @@ -42,12 +42,9 @@ <string name="choose_create_option_passkey_title" msgid="8762295821604276511">"برای ورود به سیستم <xliff:g id="APP_NAME">%1$s</xliff:g>، گذرکلید ایجاد شود؟"</string> <string name="choose_create_option_password_title" msgid="4481366993598649224">"برای ورود به سیستم <xliff:g id="APP_NAME">%1$s</xliff:g>، گذرواژه ذخیره شود؟"</string> <string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"اطلاعات ورود به سیستم <xliff:g id="APP_NAME">%1$s</xliff:g> ذخیره شود؟"</string> - <!-- no translation found for choose_create_single_tap_passkey_title (3872793514041774218) --> - <skip /> - <!-- no translation found for choose_create_single_tap_password_title (5231871886818921622) --> - <skip /> - <!-- no translation found for choose_create_single_tap_sign_in_title (256498714574099587) --> - <skip /> + <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"برای ایجاد گذرکلید برای <xliff:g id="APP_NAME">%1$s</xliff:g> از قفل صفحه استفاده شود؟"</string> + <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"برای ایجاد گذرواژه برای <xliff:g id="APP_NAME">%1$s</xliff:g> از قفل صفحه استفاده شود؟"</string> + <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"برای ذخیرهسازی اطلاعات ورود به سیستم <xliff:g id="APP_NAME">%1$s</xliff:g> از قفل صفحه استفاده شود؟"</string> <string name="passkey" msgid="632353688396759522">"گذرکلید"</string> <string name="password" msgid="6738570945182936667">"گذرواژه"</string> <string name="passkeys" msgid="5733880786866559847">"گذرکلیدها"</string> diff --git a/packages/CredentialManager/res/values-fi/strings.xml b/packages/CredentialManager/res/values-fi/strings.xml index 7de1151f2117..838d6d9fb0be 100644 --- a/packages/CredentialManager/res/values-fi/strings.xml +++ b/packages/CredentialManager/res/values-fi/strings.xml @@ -42,12 +42,9 @@ <string name="choose_create_option_passkey_title" msgid="8762295821604276511">"Luodaanko avainkoodi sisäänkirjautumista (<xliff:g id="APP_NAME">%1$s</xliff:g>) varten?"</string> <string name="choose_create_option_password_title" msgid="4481366993598649224">"Tallennetaanko salasana sisäänkirjautumista (<xliff:g id="APP_NAME">%1$s</xliff:g>) varten?"</string> <string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"Tallennetaanko kirjautumistiedot (<xliff:g id="APP_NAME">%1$s</xliff:g>)?"</string> - <!-- no translation found for choose_create_single_tap_passkey_title (3872793514041774218) --> - <skip /> - <!-- no translation found for choose_create_single_tap_password_title (5231871886818921622) --> - <skip /> - <!-- no translation found for choose_create_single_tap_sign_in_title (256498714574099587) --> - <skip /> + <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"Käytetäänkö näytön lukitusta aivankoodin luomiseen sovellukselle (<xliff:g id="APP_NAME">%1$s</xliff:g>)?"</string> + <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"Käytetäänkö näytön lukitusta salasanan luomiseen sovellukselle (<xliff:g id="APP_NAME">%1$s</xliff:g>)?"</string> + <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"Käytetäänkö näytön lukitusta sovelluksen (<xliff:g id="APP_NAME">%1$s</xliff:g>) sisäänkirjautumistietojen tallentamiseen?"</string> <string name="passkey" msgid="632353688396759522">"avainkoodi"</string> <string name="password" msgid="6738570945182936667">"salasana"</string> <string name="passkeys" msgid="5733880786866559847">"avainkoodit"</string> diff --git a/packages/CredentialManager/res/values-fr-rCA/strings.xml b/packages/CredentialManager/res/values-fr-rCA/strings.xml index 519b14ace029..834dffef4f99 100644 --- a/packages/CredentialManager/res/values-fr-rCA/strings.xml +++ b/packages/CredentialManager/res/values-fr-rCA/strings.xml @@ -42,12 +42,9 @@ <string name="choose_create_option_passkey_title" msgid="8762295821604276511">"Créer une clé d\'accès pour se connecter à <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="choose_create_option_password_title" msgid="4481366993598649224">"Enregistrer un mot de passe pour se connecter à <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"Enregistrer les renseignements de connexion pour <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> - <!-- no translation found for choose_create_single_tap_passkey_title (3872793514041774218) --> - <skip /> - <!-- no translation found for choose_create_single_tap_password_title (5231871886818921622) --> - <skip /> - <!-- no translation found for choose_create_single_tap_sign_in_title (256498714574099587) --> - <skip /> + <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"Utiliser le Verrouillage de l\'écran pour créer une clé d\'accès pour <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"Utiliser le Verrouillage de l\'écran pour créer un mot de passe pour <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"Utiliser le Verrouillage de l\'écran pour enregistrer les renseignements de connexion pour <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="passkey" msgid="632353688396759522">"clé d\'accès"</string> <string name="password" msgid="6738570945182936667">"mot de passe"</string> <string name="passkeys" msgid="5733880786866559847">"clés d\'accès"</string> diff --git a/packages/CredentialManager/res/values-fr/strings.xml b/packages/CredentialManager/res/values-fr/strings.xml index f63c7c2aee2b..1c86c9b8e573 100644 --- a/packages/CredentialManager/res/values-fr/strings.xml +++ b/packages/CredentialManager/res/values-fr/strings.xml @@ -42,12 +42,9 @@ <string name="choose_create_option_passkey_title" msgid="8762295821604276511">"Créer une clé d\'accès pour se connecter à <xliff:g id="APP_NAME">%1$s</xliff:g> ?"</string> <string name="choose_create_option_password_title" msgid="4481366993598649224">"Enregistrer un mot de passe pour se connecter à <xliff:g id="APP_NAME">%1$s</xliff:g> ?"</string> <string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"Enregistrer les informations de connexion pour <xliff:g id="APP_NAME">%1$s</xliff:g> ?"</string> - <!-- no translation found for choose_create_single_tap_passkey_title (3872793514041774218) --> - <skip /> - <!-- no translation found for choose_create_single_tap_password_title (5231871886818921622) --> - <skip /> - <!-- no translation found for choose_create_single_tap_sign_in_title (256498714574099587) --> - <skip /> + <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"Utiliser le verrouillage de l\'écran afin de créer une clé d\'accès pour <xliff:g id="APP_NAME">%1$s</xliff:g> ?"</string> + <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"Utiliser le verrouillage de l\'écran afin de créer un mot de passe pour <xliff:g id="APP_NAME">%1$s</xliff:g> ?"</string> + <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"Utiliser le verrouillage de l\'écran afin d\'enregistrer les informations de connexion pour <xliff:g id="APP_NAME">%1$s</xliff:g> ?"</string> <string name="passkey" msgid="632353688396759522">"clé d\'accès"</string> <string name="password" msgid="6738570945182936667">"mot de passe"</string> <string name="passkeys" msgid="5733880786866559847">"clés d\'accès"</string> diff --git a/packages/CredentialManager/res/values-gl/strings.xml b/packages/CredentialManager/res/values-gl/strings.xml index d756200c11d4..a95f6b9a768d 100644 --- a/packages/CredentialManager/res/values-gl/strings.xml +++ b/packages/CredentialManager/res/values-gl/strings.xml @@ -42,12 +42,9 @@ <string name="choose_create_option_passkey_title" msgid="8762295821604276511">"Queres crear unha clave de acceso para iniciar sesión en <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="choose_create_option_password_title" msgid="4481366993598649224">"Queres gardar o contrasinal para iniciar sesión en <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"Queres gardar a información de inicio de sesión de <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> - <!-- no translation found for choose_create_single_tap_passkey_title (3872793514041774218) --> - <skip /> - <!-- no translation found for choose_create_single_tap_password_title (5231871886818921622) --> - <skip /> - <!-- no translation found for choose_create_single_tap_sign_in_title (256498714574099587) --> - <skip /> + <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"Queres usar o bloqueo de pantalla para crear unha clave de acceso para <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"Queres usar o bloqueo de pantalla para crear un contrasinal para <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"Queres usar o bloqueo de pantalla para gardar a información de inicio de sesión para <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="passkey" msgid="632353688396759522">"clave de acceso"</string> <string name="password" msgid="6738570945182936667">"contrasinal"</string> <string name="passkeys" msgid="5733880786866559847">"claves de acceso"</string> diff --git a/packages/CredentialManager/res/values-hu/strings.xml b/packages/CredentialManager/res/values-hu/strings.xml index fe749f6129a8..46b80da6181b 100644 --- a/packages/CredentialManager/res/values-hu/strings.xml +++ b/packages/CredentialManager/res/values-hu/strings.xml @@ -42,12 +42,9 @@ <string name="choose_create_option_passkey_title" msgid="8762295821604276511">"Létrehoz azonosítókulcsot a következőbe való bejelentkezéshez: <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="choose_create_option_password_title" msgid="4481366993598649224">"Menti a jelszót a következőbe való bejelentkezéshez: <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"Menti a bejelentkezési adatokat a következőhöz: <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> - <!-- no translation found for choose_create_single_tap_passkey_title (3872793514041774218) --> - <skip /> - <!-- no translation found for choose_create_single_tap_password_title (5231871886818921622) --> - <skip /> - <!-- no translation found for choose_create_single_tap_sign_in_title (256498714574099587) --> - <skip /> + <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"Képernyőzár használatával hoz létre azonosítókulcsot a következőhöz: <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"Képernyőzár használatával hoz létre jelszót a következőhöz: <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"Képernyőzár használatával menti a bejelentkezési adatokat a következőhöz: <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="passkey" msgid="632353688396759522">"azonosítókulcs"</string> <string name="password" msgid="6738570945182936667">"jelszó"</string> <string name="passkeys" msgid="5733880786866559847">"azonosítókulcsait"</string> diff --git a/packages/CredentialManager/res/values-hy/strings.xml b/packages/CredentialManager/res/values-hy/strings.xml index 452acb7a18b2..2a8784cd52f1 100644 --- a/packages/CredentialManager/res/values-hy/strings.xml +++ b/packages/CredentialManager/res/values-hy/strings.xml @@ -42,12 +42,9 @@ <string name="choose_create_option_passkey_title" msgid="8762295821604276511">"Ստեղծե՞լ անցաբառ՝ <xliff:g id="APP_NAME">%1$s</xliff:g> հավելված մուտք գործելու համար"</string> <string name="choose_create_option_password_title" msgid="4481366993598649224">"Պահե՞լ գաղտնաբառը՝ <xliff:g id="APP_NAME">%1$s</xliff:g> հավելված մուտք գործելու համար"</string> <string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"Պահե՞լ «<xliff:g id="APP_NAME">%1$s</xliff:g>» հավելվածի մուտքի տվյալները"</string> - <!-- no translation found for choose_create_single_tap_passkey_title (3872793514041774218) --> - <skip /> - <!-- no translation found for choose_create_single_tap_password_title (5231871886818921622) --> - <skip /> - <!-- no translation found for choose_create_single_tap_sign_in_title (256498714574099587) --> - <skip /> + <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"Օգտագործե՞լ էկրանի կողպումը՝ <xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածի մուտքի բանալի ստեղծելու համար"</string> + <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"Օգտագործե՞լ էկրանի կողպումը՝ <xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածի գաղտնաբառ ստեղծելու համար"</string> + <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"Օգտագործե՞լ էկրանի կողպումը՝ <xliff:g id="APP_NAME">%1$s</xliff:g> հավելվածի մուտքի տվյալները պահելու համար"</string> <string name="passkey" msgid="632353688396759522">"անցաբառ"</string> <string name="password" msgid="6738570945182936667">"գաղտնաբառ"</string> <string name="passkeys" msgid="5733880786866559847">"անցաբառեր"</string> diff --git a/packages/CredentialManager/res/values-it/strings.xml b/packages/CredentialManager/res/values-it/strings.xml index ce04dbc556b5..0d6f8390e4c1 100644 --- a/packages/CredentialManager/res/values-it/strings.xml +++ b/packages/CredentialManager/res/values-it/strings.xml @@ -42,12 +42,9 @@ <string name="choose_create_option_passkey_title" msgid="8762295821604276511">"Creare passkey per accedere all\'app <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="choose_create_option_password_title" msgid="4481366993598649224">"Salvare password per accedere all\'app <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"Vuoi salvare i dati di accesso di <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> - <!-- no translation found for choose_create_single_tap_passkey_title (3872793514041774218) --> - <skip /> - <!-- no translation found for choose_create_single_tap_password_title (5231871886818921622) --> - <skip /> - <!-- no translation found for choose_create_single_tap_sign_in_title (256498714574099587) --> - <skip /> + <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"Usare il blocco schermo per creare una passkey per <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"Usare il blocco schermo per creare una password per <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"Usare il blocco schermo per salvare le informazioni di accesso per <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="passkey" msgid="632353688396759522">"passkey"</string> <string name="password" msgid="6738570945182936667">"password"</string> <string name="passkeys" msgid="5733880786866559847">"passkey"</string> diff --git a/packages/CredentialManager/res/values-iw/strings.xml b/packages/CredentialManager/res/values-iw/strings.xml index 07b26b7d9c59..7d24825581cc 100644 --- a/packages/CredentialManager/res/values-iw/strings.xml +++ b/packages/CredentialManager/res/values-iw/strings.xml @@ -42,12 +42,9 @@ <string name="choose_create_option_passkey_title" msgid="8762295821604276511">"ליצור מפתח גישה כדי להיכנס לחשבון ב-<xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="choose_create_option_password_title" msgid="4481366993598649224">"לשמור את הסיסמה כדי להיכנס לחשבון ב-<xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"לשמור את פרטי הכניסה של <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> - <!-- no translation found for choose_create_single_tap_passkey_title (3872793514041774218) --> - <skip /> - <!-- no translation found for choose_create_single_tap_password_title (5231871886818921622) --> - <skip /> - <!-- no translation found for choose_create_single_tap_sign_in_title (256498714574099587) --> - <skip /> + <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"להשתמש בנעילת המסך כדי ליצור מפתח גישה בשביל <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"להשתמש בנעילת המסך כדי ליצור סיסמה בשביל <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"להשתמש בנעילת המסך כדי לשמור את פרטי הכניסה בשביל <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="passkey" msgid="632353688396759522">"מפתח גישה"</string> <string name="password" msgid="6738570945182936667">"סיסמה"</string> <string name="passkeys" msgid="5733880786866559847">"מפתחות גישה"</string> diff --git a/packages/CredentialManager/res/values-ja/strings.xml b/packages/CredentialManager/res/values-ja/strings.xml index f12bb5e50937..16e3195e345e 100644 --- a/packages/CredentialManager/res/values-ja/strings.xml +++ b/packages/CredentialManager/res/values-ja/strings.xml @@ -42,12 +42,9 @@ <string name="choose_create_option_passkey_title" msgid="8762295821604276511">"<xliff:g id="APP_NAME">%1$s</xliff:g> にログインするためにパスキーを作成しますか?"</string> <string name="choose_create_option_password_title" msgid="4481366993598649224">"<xliff:g id="APP_NAME">%1$s</xliff:g> にログインするためにパスワードを保存しますか?"</string> <string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"<xliff:g id="APP_NAME">%1$s</xliff:g> のログイン情報を保存しますか?"</string> - <!-- no translation found for choose_create_single_tap_passkey_title (3872793514041774218) --> - <skip /> - <!-- no translation found for choose_create_single_tap_password_title (5231871886818921622) --> - <skip /> - <!-- no translation found for choose_create_single_tap_sign_in_title (256498714574099587) --> - <skip /> + <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"画面ロックを使用して <xliff:g id="APP_NAME">%1$s</xliff:g> のパスキーを作成しますか?"</string> + <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"画面ロックを使用して <xliff:g id="APP_NAME">%1$s</xliff:g> のパスワードを作成しますか?"</string> + <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"画面ロックを使用して <xliff:g id="APP_NAME">%1$s</xliff:g> のログイン情報を保存しますか?"</string> <string name="passkey" msgid="632353688396759522">"パスキー"</string> <string name="password" msgid="6738570945182936667">"パスワード"</string> <string name="passkeys" msgid="5733880786866559847">"パスキー"</string> diff --git a/packages/CredentialManager/res/values-ka/strings.xml b/packages/CredentialManager/res/values-ka/strings.xml index 4c6154040a7d..a852f1c3654a 100644 --- a/packages/CredentialManager/res/values-ka/strings.xml +++ b/packages/CredentialManager/res/values-ka/strings.xml @@ -42,12 +42,9 @@ <string name="choose_create_option_passkey_title" msgid="8762295821604276511">"შესასვლელად წვდომის გასაღების შექმნა: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="choose_create_option_password_title" msgid="4481366993598649224">"შესასვლელი პაროლის შენახვა: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> <string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"აპში შესვლის ინფორმაციის შენახვა: <xliff:g id="APP_NAME">%1$s</xliff:g>"</string> - <!-- no translation found for choose_create_single_tap_passkey_title (3872793514041774218) --> - <skip /> - <!-- no translation found for choose_create_single_tap_password_title (5231871886818921622) --> - <skip /> - <!-- no translation found for choose_create_single_tap_sign_in_title (256498714574099587) --> - <skip /> + <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"გსურთ თქვენი ეკრანის დაბლოკვის გამოყენება <xliff:g id="APP_NAME">%1$s</xliff:g>-ისთვის წვდომის გასაღების შესაქმნელად?"</string> + <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"გსურთ თქვენი ეკრანის დაბლოკვის გამოყენება <xliff:g id="APP_NAME">%1$s</xliff:g>-ისთვის პაროლის შესაქმნელად?"</string> + <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"გსურთ თქვენი ეკრანის დაბლოკვის გამოყენება შესვლის ინფორმაციის შესანახად <xliff:g id="APP_NAME">%1$s</xliff:g>-ისთვის?"</string> <string name="passkey" msgid="632353688396759522">"წვდომის გასაღები"</string> <string name="password" msgid="6738570945182936667">"პაროლი"</string> <string name="passkeys" msgid="5733880786866559847">"წვდომის გასაღები"</string> diff --git a/packages/CredentialManager/res/values-kk/strings.xml b/packages/CredentialManager/res/values-kk/strings.xml index 5b76ac89f47e..03eb9d2eb5f3 100644 --- a/packages/CredentialManager/res/values-kk/strings.xml +++ b/packages/CredentialManager/res/values-kk/strings.xml @@ -42,12 +42,9 @@ <string name="choose_create_option_passkey_title" msgid="8762295821604276511">"<xliff:g id="APP_NAME">%1$s</xliff:g> қолданбасына кіру үшін кіру кілті жасалсын ба?"</string> <string name="choose_create_option_password_title" msgid="4481366993598649224">"<xliff:g id="APP_NAME">%1$s</xliff:g> қолданбасына кіру үшін құпия сөз сақталсын ба?"</string> <string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"<xliff:g id="APP_NAME">%1$s</xliff:g> үшін кіру мәліметін сақтау керек пе?"</string> - <!-- no translation found for choose_create_single_tap_passkey_title (3872793514041774218) --> - <skip /> - <!-- no translation found for choose_create_single_tap_password_title (5231871886818921622) --> - <skip /> - <!-- no translation found for choose_create_single_tap_sign_in_title (256498714574099587) --> - <skip /> + <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"<xliff:g id="APP_NAME">%1$s</xliff:g> кіру кілтін жасау үшін экран құлпын пайдаланасыз ба?"</string> + <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"<xliff:g id="APP_NAME">%1$s</xliff:g> құпия сөзін жасау үшін экран құлпын пайдаланасыз ба?"</string> + <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"<xliff:g id="APP_NAME">%1$s</xliff:g> қолданбасындағы аккаунтқа кіру ақпаратын сақтау үшін экран құлпын пайдаланасыз ба?"</string> <string name="passkey" msgid="632353688396759522">"Кіру кілті"</string> <string name="password" msgid="6738570945182936667">"құпия сөз"</string> <string name="passkeys" msgid="5733880786866559847">"кіру кілттері"</string> diff --git a/packages/CredentialManager/res/values-kn/strings.xml b/packages/CredentialManager/res/values-kn/strings.xml index 01d0f59fe985..2f090e4e3478 100644 --- a/packages/CredentialManager/res/values-kn/strings.xml +++ b/packages/CredentialManager/res/values-kn/strings.xml @@ -42,12 +42,9 @@ <string name="choose_create_option_passkey_title" msgid="8762295821604276511">"<xliff:g id="APP_NAME">%1$s</xliff:g> ಗೆ ಸೈನ್ ಇನ್ ಮಾಡಲು ಪಾಸ್ಕೀ ಯನ್ನು ರಚಿಸಬೇಕೇ?"</string> <string name="choose_create_option_password_title" msgid="4481366993598649224">"<xliff:g id="APP_NAME">%1$s</xliff:g> ಗೆ ಸೈನ್ ಇನ್ ಮಾಡಲು ಪಾಸ್ವರ್ಡ್ ಅನ್ನು ಸೇವ್ ಮಾಡಬೇಕೇ?"</string> <string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"<xliff:g id="APP_NAME">%1$s</xliff:g> ಗಾಗಿ ಸೈನ್-ಇನ್ ಮಾಹಿತಿಯನ್ನು ಸೇವ್ ಮಾಡಬೇಕೇ?"</string> - <!-- no translation found for choose_create_single_tap_passkey_title (3872793514041774218) --> - <skip /> - <!-- no translation found for choose_create_single_tap_password_title (5231871886818921622) --> - <skip /> - <!-- no translation found for choose_create_single_tap_sign_in_title (256498714574099587) --> - <skip /> + <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"<xliff:g id="APP_NAME">%1$s</xliff:g> ಗಾಗಿ ಪಾಸ್ಕೀಯನ್ನು ರಚಿಸಲು ನಿಮ್ಮ ಸ್ಕ್ರೀನ್ ಲಾಕ್ ಅನ್ನು ಬಳಸುವುದೇ?"</string> + <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"<xliff:g id="APP_NAME">%1$s</xliff:g> ಗಾಗಿ ಪಾಸ್ವರ್ಡ್ ಅನ್ನು ರಚಿಸಲು ನಿಮ್ಮ ಸ್ಕ್ರೀನ್ ಲಾಕ್ ಅನ್ನು ಬಳಸುವುದೇ?"</string> + <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"<xliff:g id="APP_NAME">%1$s</xliff:g> ಗಾಗಿ ಸೈನ್ ಇನ್ ಮಾಹಿತಿಯನ್ನು ಸೇವ್ ಮಾಡಲು ನಿಮ್ಮ ಸ್ಕ್ರೀನ್ ಲಾಕ್ ಅನ್ನು ಬಳಸುವುದೇ?"</string> <string name="passkey" msgid="632353688396759522">"ಪಾಸ್ಕೀ"</string> <string name="password" msgid="6738570945182936667">"ಪಾಸ್ವರ್ಡ್"</string> <string name="passkeys" msgid="5733880786866559847">"ಪಾಸ್ಕೀಗಳು"</string> diff --git a/packages/CredentialManager/res/values-ko/strings.xml b/packages/CredentialManager/res/values-ko/strings.xml index 1f1fa2990d87..f2ead8582e76 100644 --- a/packages/CredentialManager/res/values-ko/strings.xml +++ b/packages/CredentialManager/res/values-ko/strings.xml @@ -20,7 +20,7 @@ <string name="app_name" msgid="4539824758261855508">"인증서 관리자"</string> <string name="string_cancel" msgid="6369133483981306063">"취소"</string> <string name="string_continue" msgid="1346732695941131882">"계속"</string> - <string name="string_more_options" msgid="2763852250269945472">"다른 방법 저장하기"</string> + <string name="string_more_options" msgid="2763852250269945472">"다른 방법으로 저장하기"</string> <string name="string_learn_more" msgid="4541600451688392447">"자세히 알아보기"</string> <string name="content_description_show_password" msgid="3283502010388521607">"비밀번호 표시"</string> <string name="content_description_hide_password" msgid="6841375971631767996">"비밀번호 숨기기"</string> @@ -39,15 +39,12 @@ <string name="seamless_transition_detail" msgid="4475509237171739843">"비밀번호 없는 미래로 나아가는 과정에서 비밀번호는 여전히 패스키와 함께 사용될 것입니다."</string> <string name="choose_provider_title" msgid="8870795677024868108">"<xliff:g id="CREATETYPES">%1$s</xliff:g> 저장 위치 선택"</string> <string name="choose_provider_body" msgid="4967074531845147434">"정보를 저장해서 다음에 더 빠르게 로그인하려면 비밀번호 관리자를 선택하세요."</string> - <string name="choose_create_option_passkey_title" msgid="8762295821604276511">"패스키를 생성하여 <xliff:g id="APP_NAME">%1$s</xliff:g>에 로그인하시겠습니까?"</string> + <string name="choose_create_option_passkey_title" msgid="8762295821604276511">"<xliff:g id="APP_NAME">%1$s</xliff:g>의 패스키를 생성할까요?"</string> <string name="choose_create_option_password_title" msgid="4481366993598649224">"비밀번호를 저장하여 <xliff:g id="APP_NAME">%1$s</xliff:g>에 로그인하시겠습니까?"</string> <string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"<xliff:g id="APP_NAME">%1$s</xliff:g>의 로그인 정보를 저장하시겠습니까?"</string> - <!-- no translation found for choose_create_single_tap_passkey_title (3872793514041774218) --> - <skip /> - <!-- no translation found for choose_create_single_tap_password_title (5231871886818921622) --> - <skip /> - <!-- no translation found for choose_create_single_tap_sign_in_title (256498714574099587) --> - <skip /> + <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"<xliff:g id="APP_NAME">%1$s</xliff:g>용 패스키를 생성하기 위해 화면 잠금을 사용하시겠습니까?"</string> + <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"<xliff:g id="APP_NAME">%1$s</xliff:g>용 패스키를 생성하기 위해 비밀번호를 사용하시겠습니까?"</string> + <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"<xliff:g id="APP_NAME">%1$s</xliff:g>용 로그인 정보를 저장하기 위해 화면 잠금을 사용하시겠습니까?"</string> <string name="passkey" msgid="632353688396759522">"패스키"</string> <string name="password" msgid="6738570945182936667">"비밀번호"</string> <string name="passkeys" msgid="5733880786866559847">"패스키"</string> diff --git a/packages/CredentialManager/res/values-lt/strings.xml b/packages/CredentialManager/res/values-lt/strings.xml index 3b0124cf2249..5e0fbe07f61b 100644 --- a/packages/CredentialManager/res/values-lt/strings.xml +++ b/packages/CredentialManager/res/values-lt/strings.xml @@ -42,12 +42,9 @@ <string name="choose_create_option_passkey_title" msgid="8762295821604276511">"Sukurti prieigos raktą, skirtą prisijungti prie „<xliff:g id="APP_NAME">%1$s</xliff:g>“?"</string> <string name="choose_create_option_password_title" msgid="4481366993598649224">"Sukurti slaptažodį, skirtą prisijungti prie „<xliff:g id="APP_NAME">%1$s</xliff:g>“?"</string> <string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"Išsaugoti prisijungimo prie „<xliff:g id="APP_NAME">%1$s</xliff:g>“ informaciją?"</string> - <!-- no translation found for choose_create_single_tap_passkey_title (3872793514041774218) --> - <skip /> - <!-- no translation found for choose_create_single_tap_password_title (5231871886818921622) --> - <skip /> - <!-- no translation found for choose_create_single_tap_sign_in_title (256498714574099587) --> - <skip /> + <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"Sukurti „<xliff:g id="APP_NAME">%1$s</xliff:g>“ prieigos raktą naudojant ekrano užraktą?"</string> + <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"Sukurti „<xliff:g id="APP_NAME">%1$s</xliff:g>“ slaptažodį naudojant ekrano užraktą?"</string> + <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"Naudoti ekrano užraktą norint išsaugoti „<xliff:g id="APP_NAME">%1$s</xliff:g>“ prisijungimo informaciją?"</string> <string name="passkey" msgid="632353688396759522">"„passkey“"</string> <string name="password" msgid="6738570945182936667">"slaptažodis"</string> <string name="passkeys" msgid="5733880786866559847">"prieigos raktas"</string> diff --git a/packages/CredentialManager/res/values-lv/strings.xml b/packages/CredentialManager/res/values-lv/strings.xml index 095fbeeea072..f56d7f145d3d 100644 --- a/packages/CredentialManager/res/values-lv/strings.xml +++ b/packages/CredentialManager/res/values-lv/strings.xml @@ -42,12 +42,9 @@ <string name="choose_create_option_passkey_title" msgid="8762295821604276511">"Vai izveidot piekļuves atslēgu, lai pierakstītos lietotnē <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="choose_create_option_password_title" msgid="4481366993598649224">"Vai saglabāt paroli, lai pierakstītos lietotnē <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"Vai saglabāt pierakstīšanās informāciju lietotnei <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> - <!-- no translation found for choose_create_single_tap_passkey_title (3872793514041774218) --> - <skip /> - <!-- no translation found for choose_create_single_tap_password_title (5231871886818921622) --> - <skip /> - <!-- no translation found for choose_create_single_tap_sign_in_title (256498714574099587) --> - <skip /> + <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"Vai izmantot ekrāna bloķēšanas opciju, lai izveidotu piekļuves atslēgu lietotnei <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"Vai izmantot ekrāna bloķēšanas opciju, lai izveidotu paroli lietotnei <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"Vai izmantot ekrāna bloķēšanas opciju, lai saglabātu pierakstīšanās informāciju lietotnei <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="passkey" msgid="632353688396759522">"piekļuves atslēga"</string> <string name="password" msgid="6738570945182936667">"parole"</string> <string name="passkeys" msgid="5733880786866559847">"piekļuves atslēgas"</string> diff --git a/packages/CredentialManager/res/values-mk/strings.xml b/packages/CredentialManager/res/values-mk/strings.xml index 0f22f6fc81fa..ec4df567ef07 100644 --- a/packages/CredentialManager/res/values-mk/strings.xml +++ b/packages/CredentialManager/res/values-mk/strings.xml @@ -42,12 +42,9 @@ <string name="choose_create_option_passkey_title" msgid="8762295821604276511">"Да се создаде криптографски клуч за најавување на <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="choose_create_option_password_title" msgid="4481366993598649224">"Да се зачува лозинката за најавување на <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"Да се зачуваат податоците за најавување за <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> - <!-- no translation found for choose_create_single_tap_passkey_title (3872793514041774218) --> - <skip /> - <!-- no translation found for choose_create_single_tap_password_title (5231871886818921622) --> - <skip /> - <!-- no translation found for choose_create_single_tap_sign_in_title (256498714574099587) --> - <skip /> + <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"Да се користи заклучувањето екран за создавање криптографски клуч за <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"Да се користи заклучувањето екран за создавање лозинка за <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"Да се користи заклучувањето екран за зачувување на податоците за најавување за <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="passkey" msgid="632353688396759522">"криптографски клуч"</string> <string name="password" msgid="6738570945182936667">"лозинка"</string> <string name="passkeys" msgid="5733880786866559847">"криптографски клучеви"</string> diff --git a/packages/CredentialManager/res/values-mn/strings.xml b/packages/CredentialManager/res/values-mn/strings.xml index 5554670b4d2d..093baab33716 100644 --- a/packages/CredentialManager/res/values-mn/strings.xml +++ b/packages/CredentialManager/res/values-mn/strings.xml @@ -42,12 +42,9 @@ <string name="choose_create_option_passkey_title" msgid="8762295821604276511">"<xliff:g id="APP_NAME">%1$s</xliff:g>-д нэвтрэхийн тулд нэвтрэх түлхүүр үүсгэх үү?"</string> <string name="choose_create_option_password_title" msgid="4481366993598649224">"<xliff:g id="APP_NAME">%1$s</xliff:g>-д нэвтрэхийн тулд нууц үгийг хадгалах уу?"</string> <string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"<xliff:g id="APP_NAME">%1$s</xliff:g>-н нэвтрэх мэдээллийг хадгалах уу?"</string> - <!-- no translation found for choose_create_single_tap_passkey_title (3872793514041774218) --> - <skip /> - <!-- no translation found for choose_create_single_tap_password_title (5231871886818921622) --> - <skip /> - <!-- no translation found for choose_create_single_tap_sign_in_title (256498714574099587) --> - <skip /> + <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"<xliff:g id="APP_NAME">%1$s</xliff:g>-д нэвтрэх түлхүүр үүсгэхийн тулд дэлгэцийн түгжээгээ ашиглах уу?"</string> + <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"<xliff:g id="APP_NAME">%1$s</xliff:g>-д нууц үг үүсгэхийн тулд дэлгэцийн түгжээгээ ашиглах уу?"</string> + <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"<xliff:g id="APP_NAME">%1$s</xliff:g>-д нэвтрэх мэдээлэл хадгалахын тулд дэлгэцийн түгжээгээ ашиглах уу?"</string> <string name="passkey" msgid="632353688396759522">"passkey"</string> <string name="password" msgid="6738570945182936667">"нууц үг"</string> <string name="passkeys" msgid="5733880786866559847">"нэвтрэх түлхүүрүүд"</string> diff --git a/packages/CredentialManager/res/values-mr/strings.xml b/packages/CredentialManager/res/values-mr/strings.xml index ce415f5efd41..cc52617b2e1b 100644 --- a/packages/CredentialManager/res/values-mr/strings.xml +++ b/packages/CredentialManager/res/values-mr/strings.xml @@ -42,12 +42,9 @@ <string name="choose_create_option_passkey_title" msgid="8762295821604276511">"<xliff:g id="APP_NAME">%1$s</xliff:g> मध्ये साइन इन करण्यासाठी पासकी तयार करायची आहे का?"</string> <string name="choose_create_option_password_title" msgid="4481366993598649224">"<xliff:g id="APP_NAME">%1$s</xliff:g> मध्ये साइन इन करण्यासाठी पासवर्ड सेव्ह करायचा आहे का?"</string> <string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"<xliff:g id="APP_NAME">%1$s</xliff:g> साठी साइन-इनसंबंधित माहिती सेव्ह करायची का?"</string> - <!-- no translation found for choose_create_single_tap_passkey_title (3872793514041774218) --> - <skip /> - <!-- no translation found for choose_create_single_tap_password_title (5231871886818921622) --> - <skip /> - <!-- no translation found for choose_create_single_tap_sign_in_title (256498714574099587) --> - <skip /> + <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"<xliff:g id="APP_NAME">%1$s</xliff:g> साठी पासकी तयार करण्याकरिता तुमचे स्क्रीन लॉक वापरायचे आहे का?"</string> + <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"<xliff:g id="APP_NAME">%1$s</xliff:g> साठी पासवर्ड तयार करण्याकरिता तुमचे स्क्रीन लॉक वापरायचे आहे का?"</string> + <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"<xliff:g id="APP_NAME">%1$s</xliff:g> साठी साइन-इनसंबंधित माहिती सेव्ह करण्याकरिता तुमचे स्क्रीन लॉक वापरायचे आहे का?"</string> <string name="passkey" msgid="632353688396759522">"पासकी"</string> <string name="password" msgid="6738570945182936667">"पासवर्ड"</string> <string name="passkeys" msgid="5733880786866559847">"पासकी"</string> diff --git a/packages/CredentialManager/res/values-my/strings.xml b/packages/CredentialManager/res/values-my/strings.xml index 5af7b72a48a6..aa08aa7f8690 100644 --- a/packages/CredentialManager/res/values-my/strings.xml +++ b/packages/CredentialManager/res/values-my/strings.xml @@ -42,12 +42,9 @@ <string name="choose_create_option_passkey_title" msgid="8762295821604276511">"<xliff:g id="APP_NAME">%1$s</xliff:g> သို့ လက်မှတ်ထိုးဝင်ရန် လျှို့ဝှက်ကီး ပြုလုပ်မလား။"</string> <string name="choose_create_option_password_title" msgid="4481366993598649224">"<xliff:g id="APP_NAME">%1$s</xliff:g> သို့ လက်မှတ်ထိုးဝင်ရန် စကားဝှက်ကို သိမ်းမလား။"</string> <string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"<xliff:g id="APP_NAME">%1$s</xliff:g> အတွက် လက်မှတ်ထိုးဝင်ရန် အချက်အလက်ကို သိမ်းမလား။"</string> - <!-- no translation found for choose_create_single_tap_passkey_title (3872793514041774218) --> - <skip /> - <!-- no translation found for choose_create_single_tap_password_title (5231871886818921622) --> - <skip /> - <!-- no translation found for choose_create_single_tap_sign_in_title (256498714574099587) --> - <skip /> + <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"<xliff:g id="APP_NAME">%1$s</xliff:g> အတွက် လျှို့ဝှက်ကီးပြုလုပ်ရန် သင့်ဖန်သားပြင်လော့ခ်ကို သုံးမလား။"</string> + <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"<xliff:g id="APP_NAME">%1$s</xliff:g> အတွက် စကားဝှက်ပြုလုပ်ရန် သင့်ဖန်သားပြင်လော့ခ်ကို သုံးမလား။"</string> + <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"<xliff:g id="APP_NAME">%1$s</xliff:g> အတွက် လက်မှတ်ထိုးဝင်ရန် အချက်အလက်များ သိမ်းရန် သင့်ဖန်သားပြင်လော့ခ်ကို သုံးမလား။"</string> <string name="passkey" msgid="632353688396759522">"လျှို့ဝှက်ကီး"</string> <string name="password" msgid="6738570945182936667">"စကားဝှက်"</string> <string name="passkeys" msgid="5733880786866559847">"လျှို့ဝှက်ကီးများ"</string> diff --git a/packages/CredentialManager/res/values-nb/strings.xml b/packages/CredentialManager/res/values-nb/strings.xml index b6a679a49be6..8cf344429fa4 100644 --- a/packages/CredentialManager/res/values-nb/strings.xml +++ b/packages/CredentialManager/res/values-nb/strings.xml @@ -42,12 +42,9 @@ <string name="choose_create_option_passkey_title" msgid="8762295821604276511">"Vil du opprette en passnøkkel for å logge på <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="choose_create_option_password_title" msgid="4481366993598649224">"Vil du lagre passordet for å logge på <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"Vil du lagre påloggingsinformasjon for <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> - <!-- no translation found for choose_create_single_tap_passkey_title (3872793514041774218) --> - <skip /> - <!-- no translation found for choose_create_single_tap_password_title (5231871886818921622) --> - <skip /> - <!-- no translation found for choose_create_single_tap_sign_in_title (256498714574099587) --> - <skip /> + <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"Vil du bruke skjermlåsen til å opprette en passnøkkel for <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"Vil du bruke skjermlåsen til å opprette et passord for <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"Vil du bruke skjermlåsen til å lagre påloggingsinformasjon for <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="passkey" msgid="632353688396759522">"passnøkkel"</string> <string name="password" msgid="6738570945182936667">"passord"</string> <string name="passkeys" msgid="5733880786866559847">"passnøkler"</string> diff --git a/packages/CredentialManager/res/values-nl/strings.xml b/packages/CredentialManager/res/values-nl/strings.xml index 06ebc08d9725..6707ff092dda 100644 --- a/packages/CredentialManager/res/values-nl/strings.xml +++ b/packages/CredentialManager/res/values-nl/strings.xml @@ -42,12 +42,9 @@ <string name="choose_create_option_passkey_title" msgid="8762295821604276511">"Toegangssleutel maken om in te loggen bij <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="choose_create_option_password_title" msgid="4481366993598649224">"Wachtwoord opslaan om in te loggen bij <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"Inloggegevens opslaan voor <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> - <!-- no translation found for choose_create_single_tap_passkey_title (3872793514041774218) --> - <skip /> - <!-- no translation found for choose_create_single_tap_password_title (5231871886818921622) --> - <skip /> - <!-- no translation found for choose_create_single_tap_sign_in_title (256498714574099587) --> - <skip /> + <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"Je schermvergrendeling gebruiken om een toegangssleutel voor <xliff:g id="APP_NAME">%1$s</xliff:g> te maken?"</string> + <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"Je schermvergrendeling gebruiken om een wachtwoord voor <xliff:g id="APP_NAME">%1$s</xliff:g> te maken?"</string> + <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"Je schermvergrendeling gebruiken om inloggegevens voor <xliff:g id="APP_NAME">%1$s</xliff:g> op te slaan?"</string> <string name="passkey" msgid="632353688396759522">"Toegangssleutel"</string> <string name="password" msgid="6738570945182936667">"wachtwoord"</string> <string name="passkeys" msgid="5733880786866559847">"toegangssleutels"</string> diff --git a/packages/CredentialManager/res/values-pa/strings.xml b/packages/CredentialManager/res/values-pa/strings.xml index 4cf8e17172c0..a5aceb7a11c7 100644 --- a/packages/CredentialManager/res/values-pa/strings.xml +++ b/packages/CredentialManager/res/values-pa/strings.xml @@ -42,12 +42,9 @@ <string name="choose_create_option_passkey_title" msgid="8762295821604276511">"ਕੀ <xliff:g id="APP_NAME">%1$s</xliff:g> ਵਿੱਚ ਸਾਈਨ-ਇਨ ਕਰਨ ਲਈ ਪਾਸਕੀ ਬਣਾਉਣੀ ਹੈ?"</string> <string name="choose_create_option_password_title" msgid="4481366993598649224">"ਕੀ <xliff:g id="APP_NAME">%1$s</xliff:g> ਵਿੱਚ ਸਾਈਨ-ਇਨ ਕਰਨ ਲਈ ਪਾਸਵਰਡ ਰੱਖਿਅਤ ਕਰਨਾ ਹੈ?"</string> <string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"ਕੀ <xliff:g id="APP_NAME">%1$s</xliff:g> ਲਈ ਸਾਈਨ-ਇਨ ਜਾਣਕਾਰੀ ਰੱਖਿਅਤ ਕਰਨੀ ਹੈ?"</string> - <!-- no translation found for choose_create_single_tap_passkey_title (3872793514041774218) --> - <skip /> - <!-- no translation found for choose_create_single_tap_password_title (5231871886818921622) --> - <skip /> - <!-- no translation found for choose_create_single_tap_sign_in_title (256498714574099587) --> - <skip /> + <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"ਕੀ <xliff:g id="APP_NAME">%1$s</xliff:g> ਲਈ ਪਾਸਕੀ ਬਣਾਉਣ ਵਾਸਤੇ ਆਪਣੇ ਸਕ੍ਰੀਨ ਲਾਕ ਦੀ ਵਰਤੋਂ ਕਰਨੀ ਹੈ?"</string> + <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"ਕੀ <xliff:g id="APP_NAME">%1$s</xliff:g> ਲਈ ਪਾਸਵਰਡ ਬਣਾਉਣ ਵਾਸਤੇ ਆਪਣੇ ਸਕ੍ਰੀਨ ਲਾਕ ਦੀ ਵਰਤੋਂ ਕਰਨੀ ਹੈ?"</string> + <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"ਕੀ <xliff:g id="APP_NAME">%1$s</xliff:g> ਲਈ ਸਾਈਨ-ਇਨ ਜਾਣਕਾਰੀ ਰੱਖਿਅਤ ਕਰਨ ਵਾਸਤੇ ਆਪਣੇ ਸਕ੍ਰੀਨ ਲਾਕ ਦੀ ਵਰਤੋਂ ਕਰਨੀ ਹੈ?"</string> <string name="passkey" msgid="632353688396759522">"ਪਾਸਕੀ"</string> <string name="password" msgid="6738570945182936667">"ਪਾਸਵਰਡ"</string> <string name="passkeys" msgid="5733880786866559847">"ਪਾਸਕੀਆਂ"</string> diff --git a/packages/CredentialManager/res/values-pt-rBR/strings.xml b/packages/CredentialManager/res/values-pt-rBR/strings.xml index e49387143fef..b508af975c4d 100644 --- a/packages/CredentialManager/res/values-pt-rBR/strings.xml +++ b/packages/CredentialManager/res/values-pt-rBR/strings.xml @@ -42,12 +42,9 @@ <string name="choose_create_option_passkey_title" msgid="8762295821604276511">"Criar chave de acesso para fazer login no app <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="choose_create_option_password_title" msgid="4481366993598649224">"Salvar senha para fazer login no app <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"Salvar informações de login do app <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> - <!-- no translation found for choose_create_single_tap_passkey_title (3872793514041774218) --> - <skip /> - <!-- no translation found for choose_create_single_tap_password_title (5231871886818921622) --> - <skip /> - <!-- no translation found for choose_create_single_tap_sign_in_title (256498714574099587) --> - <skip /> + <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"Usar o bloqueio de tela para criar uma chave de acesso para o app <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"Usar o bloqueio de tela para criar uma senha para o app <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"Usar o bloqueio de tela para salvar as informações de login do app <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="passkey" msgid="632353688396759522">"chave de acesso"</string> <string name="password" msgid="6738570945182936667">"senha"</string> <string name="passkeys" msgid="5733880786866559847">"chaves de acesso"</string> diff --git a/packages/CredentialManager/res/values-pt/strings.xml b/packages/CredentialManager/res/values-pt/strings.xml index e49387143fef..b508af975c4d 100644 --- a/packages/CredentialManager/res/values-pt/strings.xml +++ b/packages/CredentialManager/res/values-pt/strings.xml @@ -42,12 +42,9 @@ <string name="choose_create_option_passkey_title" msgid="8762295821604276511">"Criar chave de acesso para fazer login no app <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="choose_create_option_password_title" msgid="4481366993598649224">"Salvar senha para fazer login no app <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"Salvar informações de login do app <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> - <!-- no translation found for choose_create_single_tap_passkey_title (3872793514041774218) --> - <skip /> - <!-- no translation found for choose_create_single_tap_password_title (5231871886818921622) --> - <skip /> - <!-- no translation found for choose_create_single_tap_sign_in_title (256498714574099587) --> - <skip /> + <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"Usar o bloqueio de tela para criar uma chave de acesso para o app <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"Usar o bloqueio de tela para criar uma senha para o app <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"Usar o bloqueio de tela para salvar as informações de login do app <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="passkey" msgid="632353688396759522">"chave de acesso"</string> <string name="password" msgid="6738570945182936667">"senha"</string> <string name="passkeys" msgid="5733880786866559847">"chaves de acesso"</string> diff --git a/packages/CredentialManager/res/values-ro/strings.xml b/packages/CredentialManager/res/values-ro/strings.xml index 87b551b62f22..ccbf22885731 100644 --- a/packages/CredentialManager/res/values-ro/strings.xml +++ b/packages/CredentialManager/res/values-ro/strings.xml @@ -42,12 +42,9 @@ <string name="choose_create_option_passkey_title" msgid="8762295821604276511">"Creezi o cheie de acces pentru a te conecta la <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="choose_create_option_password_title" msgid="4481366993598649224">"Salvezi parola pentru a te conecta la <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"Salvezi informațiile de conectare pentru <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> - <!-- no translation found for choose_create_single_tap_passkey_title (3872793514041774218) --> - <skip /> - <!-- no translation found for choose_create_single_tap_password_title (5231871886818921622) --> - <skip /> - <!-- no translation found for choose_create_single_tap_sign_in_title (256498714574099587) --> - <skip /> + <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"Folosești blocarea ecranului ca să creezi o cheie de acces pentru <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"Folosești blocarea ecranului ca să creezi o parolă pentru <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"Folosești blocarea ecranului ca să salvezi informațiile de conectare pentru <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="passkey" msgid="632353688396759522">"cheia de acces"</string> <string name="password" msgid="6738570945182936667">"parolă"</string> <string name="passkeys" msgid="5733880786866559847">"cheile de acces"</string> diff --git a/packages/CredentialManager/res/values-si/strings.xml b/packages/CredentialManager/res/values-si/strings.xml index ab78c062bba1..0acc6555fc77 100644 --- a/packages/CredentialManager/res/values-si/strings.xml +++ b/packages/CredentialManager/res/values-si/strings.xml @@ -42,12 +42,9 @@ <string name="choose_create_option_passkey_title" msgid="8762295821604276511">"<xliff:g id="APP_NAME">%1$s</xliff:g> වෙත පුරනය වීමට මුරයතුරක් තනන්න ද?"</string> <string name="choose_create_option_password_title" msgid="4481366993598649224">"<xliff:g id="APP_NAME">%1$s</xliff:g> වෙත පුරනය වීමට මුරපදය සුරකින්න ද?"</string> <string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"<xliff:g id="APP_NAME">%1$s</xliff:g> සඳහා පුරනය වීමේ තතු සුරකින්න ද?"</string> - <!-- no translation found for choose_create_single_tap_passkey_title (3872793514041774218) --> - <skip /> - <!-- no translation found for choose_create_single_tap_password_title (5231871886818921622) --> - <skip /> - <!-- no translation found for choose_create_single_tap_sign_in_title (256498714574099587) --> - <skip /> + <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"<xliff:g id="APP_NAME">%1$s</xliff:g> සඳහා මුරපදයක් තැනීමට ඔබේ තිර අගුල භාවිත කරන්න ද?"</string> + <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"<xliff:g id="APP_NAME">%1$s</xliff:g> සඳහා මුරපදයක් තැනීමට ඔබේ තිර අගුල භාවිත කරන්න ද?"</string> + <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"<xliff:g id="APP_NAME">%1$s</xliff:g> සඳහා පුරනය වීමේ තතු සුරැකීමට ඔබේ තිර අගුල භාවිතා කරන්න ද?"</string> <string name="passkey" msgid="632353688396759522">"මුරයතුර"</string> <string name="password" msgid="6738570945182936667">"මුරපදය"</string> <string name="passkeys" msgid="5733880786866559847">"මුරයතුරු"</string> diff --git a/packages/CredentialManager/res/values-sq/strings.xml b/packages/CredentialManager/res/values-sq/strings.xml index 8038cea71aed..722bee641595 100644 --- a/packages/CredentialManager/res/values-sq/strings.xml +++ b/packages/CredentialManager/res/values-sq/strings.xml @@ -42,12 +42,9 @@ <string name="choose_create_option_passkey_title" msgid="8762295821604276511">"Të krijohet një çelës kalimi për t\'u identifikuar në <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="choose_create_option_password_title" msgid="4481366993598649224">"Të ruhet fjalëkalimi për t\'u identifikuar në <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"Të ruhen informacionet e identifikimit për <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> - <!-- no translation found for choose_create_single_tap_passkey_title (3872793514041774218) --> - <skip /> - <!-- no translation found for choose_create_single_tap_password_title (5231871886818921622) --> - <skip /> - <!-- no translation found for choose_create_single_tap_sign_in_title (256498714574099587) --> - <skip /> + <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"Të përdoret kyçja e ekranit për të krijuar një çelës kalimi për <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"Të përdoret kyçja e ekranit për të krijuar një fjalëkalim për <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"Të përdoret kyçja e ekranit për të ruajtur informacionet e identifikimit për <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="passkey" msgid="632353688396759522">"çelësin e kalimit"</string> <string name="password" msgid="6738570945182936667">"fjalëkalimi"</string> <string name="passkeys" msgid="5733880786866559847">"çelësat e kalimit"</string> diff --git a/packages/CredentialManager/res/values-sv/strings.xml b/packages/CredentialManager/res/values-sv/strings.xml index 6379df248b8d..331b124343a4 100644 --- a/packages/CredentialManager/res/values-sv/strings.xml +++ b/packages/CredentialManager/res/values-sv/strings.xml @@ -42,12 +42,9 @@ <string name="choose_create_option_passkey_title" msgid="8762295821604276511">"Vill du skapa en nyckel för att logga in i <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="choose_create_option_password_title" msgid="4481366993598649224">"Vill du spara lösenordet för att logga in i <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"Vill du spara inloggningsuppgifterna för <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> - <!-- no translation found for choose_create_single_tap_passkey_title (3872793514041774218) --> - <skip /> - <!-- no translation found for choose_create_single_tap_password_title (5231871886818921622) --> - <skip /> - <!-- no translation found for choose_create_single_tap_sign_in_title (256498714574099587) --> - <skip /> + <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"Vill du använda skärmlåset för att skapa en nyckel för <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"Vill du använda skärmlåset för att skapa ett lösenord för <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"Vill du använda skärmlåset för att spara inloggningsuppgifter för <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="passkey" msgid="632353688396759522">"nyckel"</string> <string name="password" msgid="6738570945182936667">"lösenord"</string> <string name="passkeys" msgid="5733880786866559847">"nycklar"</string> diff --git a/packages/CredentialManager/res/values-ta/strings.xml b/packages/CredentialManager/res/values-ta/strings.xml index 008baaba0ec9..ba1eb60a1c26 100644 --- a/packages/CredentialManager/res/values-ta/strings.xml +++ b/packages/CredentialManager/res/values-ta/strings.xml @@ -42,12 +42,9 @@ <string name="choose_create_option_passkey_title" msgid="8762295821604276511">"<xliff:g id="APP_NAME">%1$s</xliff:g> ஆப்ஸில் உள்நுழைய கடவுச்சாவியை உருவாக்கவா?"</string> <string name="choose_create_option_password_title" msgid="4481366993598649224">"<xliff:g id="APP_NAME">%1$s</xliff:g> ஆப்ஸில் உள்நுழைய கடவுச்சொல்லைச் சேமிக்கவா?"</string> <string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"<xliff:g id="APP_NAME">%1$s</xliff:g> ஆப்ஸுக்கான உள்நுழைவுத் தகவலைச் சேமிக்கவா?"</string> - <!-- no translation found for choose_create_single_tap_passkey_title (3872793514041774218) --> - <skip /> - <!-- no translation found for choose_create_single_tap_password_title (5231871886818921622) --> - <skip /> - <!-- no translation found for choose_create_single_tap_sign_in_title (256498714574099587) --> - <skip /> + <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"<xliff:g id="APP_NAME">%1$s</xliff:g> ஆப்ஸுக்கான கடவுச்சாவியை உருவாக்க உங்கள் திரைப் பூட்டைப் பயன்படுத்தவா?"</string> + <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"<xliff:g id="APP_NAME">%1$s</xliff:g> ஆப்ஸுக்கான கடவுச்சொல்லை உருவாக்க உங்கள் திரைப் பூட்டைப் பயன்படுத்தவா?"</string> + <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"<xliff:g id="APP_NAME">%1$s</xliff:g> ஆப்ஸுக்கான உள்நுழைவுத் தகவலைச் சேமிக்க உங்கள் திரைப் பூட்டைப் பயன்படுத்தவா?"</string> <string name="passkey" msgid="632353688396759522">"கடவுச்சாவி"</string> <string name="password" msgid="6738570945182936667">"கடவுச்சொல்"</string> <string name="passkeys" msgid="5733880786866559847">"கடவுச்சாவிகள்"</string> diff --git a/packages/CredentialManager/res/values-tr/strings.xml b/packages/CredentialManager/res/values-tr/strings.xml index 96a1c0083fa5..b11ca074f4a2 100644 --- a/packages/CredentialManager/res/values-tr/strings.xml +++ b/packages/CredentialManager/res/values-tr/strings.xml @@ -42,12 +42,9 @@ <string name="choose_create_option_passkey_title" msgid="8762295821604276511">"<xliff:g id="APP_NAME">%1$s</xliff:g> uygulamasında oturum açmak için geçiş anahtarı oluşturulsun mu?"</string> <string name="choose_create_option_password_title" msgid="4481366993598649224">"<xliff:g id="APP_NAME">%1$s</xliff:g> uygulamasında oturum açmak için şifre kaydedilsin mi?"</string> <string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"<xliff:g id="APP_NAME">%1$s</xliff:g> için oturum açma bilgileri kaydedilsin mi?"</string> - <!-- no translation found for choose_create_single_tap_passkey_title (3872793514041774218) --> - <skip /> - <!-- no translation found for choose_create_single_tap_password_title (5231871886818921622) --> - <skip /> - <!-- no translation found for choose_create_single_tap_sign_in_title (256498714574099587) --> - <skip /> + <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"<xliff:g id="APP_NAME">%1$s</xliff:g> geçiş anahtarı oluşturmak için ekran kilidiniz kullanılsın mı?"</string> + <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"<xliff:g id="APP_NAME">%1$s</xliff:g> şifresi oluşturmak için ekran kilidiniz kullanılsın mı?"</string> + <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"<xliff:g id="APP_NAME">%1$s</xliff:g> oturum açma bilgilerini kaydetmek için ekran kilidiniz kullanılsın mı?"</string> <string name="passkey" msgid="632353688396759522">"Geçiş anahtarı"</string> <string name="password" msgid="6738570945182936667">"Şifre"</string> <string name="passkeys" msgid="5733880786866559847">"Geçiş anahtarlarınızın"</string> diff --git a/packages/CredentialManager/res/values-uk/strings.xml b/packages/CredentialManager/res/values-uk/strings.xml index b867903855a5..cbc67d9d4cfd 100644 --- a/packages/CredentialManager/res/values-uk/strings.xml +++ b/packages/CredentialManager/res/values-uk/strings.xml @@ -42,12 +42,9 @@ <string name="choose_create_option_passkey_title" msgid="8762295821604276511">"Створити ключ доступу для входу в додаток <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="choose_create_option_password_title" msgid="4481366993598649224">"Зберегти пароль для входу в додаток <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"Зберегти дані для входу для додатка <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> - <!-- no translation found for choose_create_single_tap_passkey_title (3872793514041774218) --> - <skip /> - <!-- no translation found for choose_create_single_tap_password_title (5231871886818921622) --> - <skip /> - <!-- no translation found for choose_create_single_tap_sign_in_title (256498714574099587) --> - <skip /> + <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"Використати спосіб розблокування екрана, щоб створити ключ доступу для додатка <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"Використати спосіб розблокування екрана, щоб створити пароль для додатка <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"Використати спосіб розблокування екрана, щоб зберегти дані для входу в додаток <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="passkey" msgid="632353688396759522">"ключ доступу"</string> <string name="password" msgid="6738570945182936667">"пароль"</string> <string name="passkeys" msgid="5733880786866559847">"ключі доступу"</string> diff --git a/packages/CredentialManager/res/values-uz/strings.xml b/packages/CredentialManager/res/values-uz/strings.xml index 796bd87e76e6..ae7f06e2b0f1 100644 --- a/packages/CredentialManager/res/values-uz/strings.xml +++ b/packages/CredentialManager/res/values-uz/strings.xml @@ -42,12 +42,9 @@ <string name="choose_create_option_passkey_title" msgid="8762295821604276511">"<xliff:g id="APP_NAME">%1$s</xliff:g> ilovasiga kirish uchun kirish kaliti yaratilsinmi?"</string> <string name="choose_create_option_password_title" msgid="4481366993598649224">"<xliff:g id="APP_NAME">%1$s</xliff:g> ilovasiga kirish uchun parol saqlansinmi?"</string> <string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"<xliff:g id="APP_NAME">%1$s</xliff:g> uchun kirish maʼlumoti saqlansinmi?"</string> - <!-- no translation found for choose_create_single_tap_passkey_title (3872793514041774218) --> - <skip /> - <!-- no translation found for choose_create_single_tap_password_title (5231871886818921622) --> - <skip /> - <!-- no translation found for choose_create_single_tap_sign_in_title (256498714574099587) --> - <skip /> + <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"<xliff:g id="APP_NAME">%1$s</xliff:g> ilovasida kirish kaliti yaratish uchun ekranni qulflashdan foydalanilsinmi?"</string> + <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"<xliff:g id="APP_NAME">%1$s</xliff:g> ilovasida parol yaratish uchun ekranni qulflashdan foydalanilsinmi?"</string> + <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"<xliff:g id="APP_NAME">%1$s</xliff:g> ilovasiga kirish axborotlarini saqlash uchun ekranni qulflashdan foydalanilsinmi?"</string> <string name="passkey" msgid="632353688396759522">"kalit"</string> <string name="password" msgid="6738570945182936667">"parol"</string> <string name="passkeys" msgid="5733880786866559847">"kalitlar"</string> diff --git a/packages/CredentialManager/res/values-vi/strings.xml b/packages/CredentialManager/res/values-vi/strings.xml index 59bd541061b4..2b5985708be4 100644 --- a/packages/CredentialManager/res/values-vi/strings.xml +++ b/packages/CredentialManager/res/values-vi/strings.xml @@ -42,12 +42,9 @@ <string name="choose_create_option_passkey_title" msgid="8762295821604276511">"Tạo khoá truy cập để đăng nhập vào <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="choose_create_option_password_title" msgid="4481366993598649224">"Lưu mật khẩu để đăng nhập vào <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"Lưu thông tin đăng nhập cho <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> - <!-- no translation found for choose_create_single_tap_passkey_title (3872793514041774218) --> - <skip /> - <!-- no translation found for choose_create_single_tap_password_title (5231871886818921622) --> - <skip /> - <!-- no translation found for choose_create_single_tap_sign_in_title (256498714574099587) --> - <skip /> + <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"Sử dụng phương thức khoá màn hình để tạo khoá truy cập cho ứng dụng <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"Sử dụng phương thức khoá màn hình để tạo mật khẩu cho ứng dụng <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"Sử dụng phương thức khoá màn hình để lưu thông tin đăng nhập cho ứng dụng <xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="passkey" msgid="632353688396759522">"khoá đăng nhập"</string> <string name="password" msgid="6738570945182936667">"mật khẩu"</string> <string name="passkeys" msgid="5733880786866559847">"khoá truy cập"</string> diff --git a/packages/CredentialManager/res/values-zh-rCN/strings.xml b/packages/CredentialManager/res/values-zh-rCN/strings.xml index e9ac45a73088..9b7ae0d88a4f 100644 --- a/packages/CredentialManager/res/values-zh-rCN/strings.xml +++ b/packages/CredentialManager/res/values-zh-rCN/strings.xml @@ -42,12 +42,9 @@ <string name="choose_create_option_passkey_title" msgid="8762295821604276511">"要创建通行密钥以便登录“<xliff:g id="APP_NAME">%1$s</xliff:g>”吗?"</string> <string name="choose_create_option_password_title" msgid="4481366993598649224">"要保存密码以便登录“<xliff:g id="APP_NAME">%1$s</xliff:g>”吗?"</string> <string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"要保存“<xliff:g id="APP_NAME">%1$s</xliff:g>”的登录信息吗?"</string> - <!-- no translation found for choose_create_single_tap_passkey_title (3872793514041774218) --> - <skip /> - <!-- no translation found for choose_create_single_tap_password_title (5231871886818921622) --> - <skip /> - <!-- no translation found for choose_create_single_tap_sign_in_title (256498714574099587) --> - <skip /> + <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"要使用屏锁为“<xliff:g id="APP_NAME">%1$s</xliff:g>”创建通行密钥?"</string> + <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"要使用屏锁为“<xliff:g id="APP_NAME">%1$s</xliff:g>”创建密码?"</string> + <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"要使用屏锁为“<xliff:g id="APP_NAME">%1$s</xliff:g>”保存登录信息?"</string> <string name="passkey" msgid="632353688396759522">"通行密钥"</string> <string name="password" msgid="6738570945182936667">"密码"</string> <string name="passkeys" msgid="5733880786866559847">"通行密钥"</string> diff --git a/packages/CredentialManager/res/values-zh-rHK/strings.xml b/packages/CredentialManager/res/values-zh-rHK/strings.xml index 77855b51c7c4..4ff00c3b1ea8 100644 --- a/packages/CredentialManager/res/values-zh-rHK/strings.xml +++ b/packages/CredentialManager/res/values-zh-rHK/strings.xml @@ -42,12 +42,9 @@ <string name="choose_create_option_passkey_title" msgid="8762295821604276511">"要建立密鑰以登入 <xliff:g id="APP_NAME">%1$s</xliff:g> 嗎?"</string> <string name="choose_create_option_password_title" msgid="4481366993598649224">"要儲存密碼以登入 <xliff:g id="APP_NAME">%1$s</xliff:g> 嗎?"</string> <string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"要儲存 <xliff:g id="APP_NAME">%1$s</xliff:g> 的登入資料嗎?"</string> - <!-- no translation found for choose_create_single_tap_passkey_title (3872793514041774218) --> - <skip /> - <!-- no translation found for choose_create_single_tap_password_title (5231871886818921622) --> - <skip /> - <!-- no translation found for choose_create_single_tap_sign_in_title (256498714574099587) --> - <skip /> + <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"要使用螢幕鎖定方式建立「<xliff:g id="APP_NAME">%1$s</xliff:g>」的密鑰嗎?"</string> + <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"要使用螢幕鎖定方式建立「<xliff:g id="APP_NAME">%1$s</xliff:g>」的密碼嗎?"</string> + <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"要使用螢幕鎖定方式儲存「<xliff:g id="APP_NAME">%1$s</xliff:g>」的登入資料嗎?"</string> <string name="passkey" msgid="632353688396759522">"密鑰"</string> <string name="password" msgid="6738570945182936667">"密碼"</string> <string name="passkeys" msgid="5733880786866559847">"密鑰"</string> diff --git a/packages/CredentialManager/res/values-zh-rTW/strings.xml b/packages/CredentialManager/res/values-zh-rTW/strings.xml index 520d9a8049e7..c8bd87d2b61a 100644 --- a/packages/CredentialManager/res/values-zh-rTW/strings.xml +++ b/packages/CredentialManager/res/values-zh-rTW/strings.xml @@ -42,12 +42,9 @@ <string name="choose_create_option_passkey_title" msgid="8762295821604276511">"要建立用於登入「<xliff:g id="APP_NAME">%1$s</xliff:g>」的密碼金鑰嗎?"</string> <string name="choose_create_option_password_title" msgid="4481366993598649224">"要儲存用於登入「<xliff:g id="APP_NAME">%1$s</xliff:g>」的密碼嗎?"</string> <string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"要儲存「<xliff:g id="APP_NAME">%1$s</xliff:g>」的登入資訊嗎?"</string> - <!-- no translation found for choose_create_single_tap_passkey_title (3872793514041774218) --> - <skip /> - <!-- no translation found for choose_create_single_tap_password_title (5231871886818921622) --> - <skip /> - <!-- no translation found for choose_create_single_tap_sign_in_title (256498714574099587) --> - <skip /> + <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"要使用螢幕鎖定建立「<xliff:g id="APP_NAME">%1$s</xliff:g>」的密碼金鑰嗎?"</string> + <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"要使用螢幕鎖定建立「<xliff:g id="APP_NAME">%1$s</xliff:g>」的密碼嗎?"</string> + <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"要使用螢幕鎖定儲存「<xliff:g id="APP_NAME">%1$s</xliff:g>」的登入資訊嗎?"</string> <string name="passkey" msgid="632353688396759522">"密碼金鑰"</string> <string name="password" msgid="6738570945182936667">"密碼"</string> <string name="passkeys" msgid="5733880786866559847">"密碼金鑰"</string> diff --git a/packages/CredentialManager/res/values-zu/strings.xml b/packages/CredentialManager/res/values-zu/strings.xml index 8cb25cb2ce9c..7e6300b5c5b0 100644 --- a/packages/CredentialManager/res/values-zu/strings.xml +++ b/packages/CredentialManager/res/values-zu/strings.xml @@ -42,12 +42,9 @@ <string name="choose_create_option_passkey_title" msgid="8762295821604276511">"Sungula ukhiye wokudlula ukuze ungene ngemvume ku-<xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="choose_create_option_password_title" msgid="4481366993598649224">"Londoloza iphasiwedi ukuze ungene ngemvume ku-<xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="choose_create_option_sign_in_title" msgid="7092914088455358079">"Londoloza ulwazi lokungena lwe-<xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> - <!-- no translation found for choose_create_single_tap_passkey_title (3872793514041774218) --> - <skip /> - <!-- no translation found for choose_create_single_tap_password_title (5231871886818921622) --> - <skip /> - <!-- no translation found for choose_create_single_tap_sign_in_title (256498714574099587) --> - <skip /> + <string name="choose_create_single_tap_passkey_title" msgid="3872793514041774218">"Sebenzisa ukukhiya isikrini sakho ukuze usungule ukhiye wokudlula we-<xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="choose_create_single_tap_password_title" msgid="5231871886818921622">"Sebenzisa ukukhiya isikrini sakho ukuze usungule iphasiwedi ye-<xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> + <string name="choose_create_single_tap_sign_in_title" msgid="256498714574099587">"Sebenzisa ukukhiya isikrini sakho ukuze ulondoloze ulwazi lokungena ngemvume lwe-<xliff:g id="APP_NAME">%1$s</xliff:g>?"</string> <string name="passkey" msgid="632353688396759522">"ukhiye wokudlula"</string> <string name="password" msgid="6738570945182936667">"iphasiwedi"</string> <string name="passkeys" msgid="5733880786866559847">"okhiye bokudlula"</string> diff --git a/packages/CredentialManager/res/values/strings.xml b/packages/CredentialManager/res/values/strings.xml index 9fd386f38684..9db681a3565d 100644 --- a/packages/CredentialManager/res/values/strings.xml +++ b/packages/CredentialManager/res/values/strings.xml @@ -68,14 +68,6 @@ <string name="choose_create_option_password_title">Save password to sign in to <xliff:g id="app_name" example="Tribank">%1$s</xliff:g>?</string> <!-- This appears as the title of the modal bottom sheet for users to choose the create option inside a provider when the credential type is others. [CHAR LIMIT=200] --> <string name="choose_create_option_sign_in_title">Save sign-in info for <xliff:g id="app_name" example="Tribank">%1$s</xliff:g>?</string> - <!-- This appears as a description of the modal bottom sheet when the single tap sign in flow is used for the create passkey flow. [CHAR LIMIT=200] --> - <string name="choose_create_single_tap_passkey_title">Use your screen lock to create a passkey for <xliff:g id="app_name" example="Shrine">%1$s</xliff:g>?</string> - <!-- This appears as a description of the modal bottom sheet when the single tap sign in flow is used for the create password flow. [CHAR LIMIT=200] --> - <string name="choose_create_single_tap_password_title">Use your screen lock to create a password for <xliff:g id="app_name" example="Shrine">%1$s</xliff:g>?</string> - <!-- This appears as a description of the modal bottom sheet when the single tap sign in flow is used for the create flow when the credential type is others. [CHAR LIMIT=200] --> - <!-- TODO(b/326243891) : Confirm with team on dynamically setting this based on recent product and ux discussions (does not disrupt e2e) --> - <string name="choose_create_single_tap_sign_in_title">Use your screen lock to save sign in info for <xliff:g id="app_name" example="Shrine">%1$s</xliff:g>?</string> - <!-- Types which are inserted as a placeholder as credentialTypes for other strings. [CHAR LIMIT=200] --> <string name="passkey">passkey</string> <string name="password">password</string> <string name="passkeys">passkeys</string> @@ -126,15 +118,21 @@ <!-- Strings for the get flow. --> <!-- This appears as the title of the modal bottom sheet asking for user confirmation to use the single previously saved passkey to sign in to the app. [CHAR LIMIT=200] --> - <string name="get_dialog_title_use_passkey_for">Use your saved passkey for <xliff:g id="app_name" example="YouTube">%1$s</xliff:g>?</string> + <string name="get_dialog_title_use_passkey_for">Use your saved passkey for <xliff:g id="app_name" example="YouTube">%1$s</xliff:g></string> <!-- This appears as the title of the modal bottom sheet asking for user confirmation to use the single previously saved password to sign in to the app. [CHAR LIMIT=200] --> - <string name="get_dialog_title_use_password_for">Use your saved password for <xliff:g id="app_name" example="YouTube">%1$s</xliff:g>?</string> + <string name="get_dialog_title_use_password_for">Use your saved password for <xliff:g id="app_name" example="YouTube">%1$s</xliff:g></string> <!-- This appears as a description of the modal bottom sheet when the single tap sign in flow is used for the get flow. [CHAR LIMIT=200] --> <string name="get_dialog_title_single_tap_for">Use your screen lock to sign in to <xliff:g id="app_name" example="Shrine">%1$s</xliff:g> with <xliff:g id="username" example="beckett-bakery@gmail.com">%2$s</xliff:g></string> <!-- This appears as the title of the dialog asking for user confirmation to use the single user credential (previously saved or to be created) to sign in to the app. [CHAR LIMIT=200] --> - <string name="get_dialog_title_use_sign_in_for">Use your sign-in for <xliff:g id="app_name" example="YouTube">%1$s</xliff:g>?</string> + <string name="get_dialog_title_use_sign_in_for">Use your account for <xliff:g id="app_name" example="YouTube">%1$s</xliff:g></string> + <!-- This appears as the description of the modal bottom sheet asking for user confirmation to use the biometric screen embedded within credential manager for passkey authentication. [CHAR LIMIT=200] --> + <string name="get_dialog_description_single_tap_passkey">Sign in to <xliff:g id="app_name" example="YouTube">%1$s</xliff:g> with your saved passkey for <xliff:g id="username" example="beckett@gmail.com">%2$s</xliff:g>.</string> + <!-- This appears as the description of the modal bottom sheet asking for user confirmation to use the biometric screen embedded within credential manager for password authentication. [CHAR LIMIT=200] --> + <string name="get_dialog_description_single_tap_password">Sign in to <xliff:g id="app_name" example="YouTube">%1$s</xliff:g> with your saved password for <xliff:g id="username" example="beckett@gmail.com">%2$s</xliff:g>.</string> + <!-- This appears as the description of the modal bottom sheet asking for user confirmation to use the biometric screen embedded within credential manager for saved sign-in authentication. [CHAR LIMIT=200] --> + <string name="get_dialog_description_single_tap_saved_sign_in">Sign in to <xliff:g id="app_name" example="YouTube">%1$s</xliff:g> with your saved sign-in info for <xliff:g id="username" example="beckett@gmail.com">%2$s</xliff:g>.</string> <!-- This appears as the title of the dialog asking for user confirmation to unlock / authenticate (e.g. via fingerprint, faceId, passcode etc.) so that we can retrieve their sign-in options. [CHAR LIMIT=200] --> - <string name="get_dialog_title_unlock_options_for">Unlock sign-in options for <xliff:g id="app_name" example="YouTube">%1$s</xliff:g>?</string> + <string name="get_dialog_title_unlock_options_for">Unlock sign-in options for <xliff:g id="app_name" example="YouTube">%1$s</xliff:g></string> <!-- This appears as the title of the dialog asking for user to make a choice from multiple previously saved passkey to sign in to the app. [CHAR LIMIT=200] --> <string name="get_dialog_title_choose_passkey_for">Choose a saved passkey for <xliff:g id="app_name" example="YouTube">%1$s</xliff:g></string> <!-- This appears as the title of the dialog asking for user to make a choice from multiple previously saved passwords to sign in to the app. [CHAR LIMIT=200] --> @@ -142,7 +140,7 @@ <!-- This appears as the title of the dialog asking for user to make a choice from multiple previously saved credentials to sign in to the app. [CHAR LIMIT=200] --> <string name="get_dialog_title_choose_saved_sign_in_for">Choose a saved sign-in for <xliff:g id="app_name" example="YouTube">%1$s</xliff:g></string> <!-- This appears as the title of the dialog asking for user to make a choice from various available user credentials (previously saved or to be created) to sign in to the app. [CHAR LIMIT=200] --> - <string name="get_dialog_title_choose_sign_in_for">Choose a sign-in for <xliff:g id="app_name" example="YouTube">%1$s</xliff:g></string> + <string name="get_dialog_title_choose_sign_in_for">Choose an account for <xliff:g id="app_name" example="YouTube">%1$s</xliff:g></string> <!-- This appears as the title of the dialog asking for user to make a choice from options of available user information (e.g. driver's license, vaccination status) to pass to the app. [CHAR LIMIT=200] --> <string name="get_dialog_title_choose_option_for">Choose an option for <xliff:g id="app_name" example="YouTube">%1$s</xliff:g>?</string> <!-- This appears as the title of the dialog asking user to send a piece of user information (e.g. driver's license, vaccination status) to the app. [CHAR LIMIT=200] --> diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt index 36f6ad2f386d..429bdbf5959b 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorViewModel.kt @@ -270,6 +270,15 @@ class CredentialSelectorViewModel( ) } + fun getFlowOnMoreOptionOnlySelected() { + Log.d(Constants.LOG_TAG, "More Option Only selected") + uiState = uiState.copy( + getCredentialUiState = uiState.getCredentialUiState?.copy( + currentScreenState = GetScreenState.ALL_SIGN_IN_OPTIONS_ONLY + ) + ) + } + fun getFlowOnMoreOptionOnSnackBarSelected(isNoAccount: Boolean) { Log.d(Constants.LOG_TAG, "More Option on snackBar selected") uiState = uiState.copy( diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/BiometricHandler.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/BiometricHandler.kt index e088d3addaf2..95f49e95d3ce 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/common/BiometricHandler.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/common/BiometricHandler.kt @@ -17,9 +17,12 @@ package com.android.credentialmanager.common import android.content.Context +import android.content.DialogInterface import android.graphics.Bitmap import android.hardware.biometrics.BiometricManager +import android.hardware.biometrics.BiometricManager.Authenticators import android.hardware.biometrics.BiometricPrompt +import android.hardware.biometrics.PromptContentViewWithMoreOptionsButton import android.os.CancellationSignal import android.util.Log import androidx.core.content.ContextCompat.getMainExecutor @@ -43,19 +46,23 @@ import java.lang.Exception * Namely, this adds the ability to encapsulate the [providerIcon], the providers icon, the * [providerName], which represents the name of the provider, the [displayTitleText] which is * the large text displaying the flow in progress, and the [descriptionForCredential], which - * describes details of where the credential is being saved, and how. - * (E.g. assume a hypothetical provider 'Any Provider' for *passkey* flows with Your@Email.com: + * describes details of where the credential is being saved, and how. [displaySubtitleText] is only expected + * to be used by the 'create' flow, optionally, and describes the saved name of the creating entity. + * (E.g. assume a hypothetical provider 'Any Provider' for *passkey* flows with Your@Email.com and + * name 'Your', and an rp called 'The App'): * * 'get' flow: * - [providerIcon] and [providerName] = 'Any Provider' (and it's icon) - * - [displayTitleText] = "Use your saved passkey for Any Provider?" - * - [descriptionForCredential] = "Use your screen lock to sign in to Any Provider with - * Your@Email.com" + * - [displayTitleText] = "Use your saved passkey for The App?" + * - [descriptionForCredential] = "Sign in to The App with your saved passkey for + * Your@gmail.com" * * 'create' flow: * - [providerIcon] and [providerName] = 'Any Provider' (and it's icon) * - [displayTitleText] = "Create passkey to sign in to Any Provider?" - * - [descriptionForCredential] = "Use your screen lock to create a passkey for Any Provider?" + * - [subtitle] = "Your" + * - [descriptionForCredential] = "You can use your passkey on other devices. It is saved to + * * Google Password Manager for Your@gmail.com." * ). * * The above are examples; the credential type can change depending on scenario. @@ -65,8 +72,9 @@ data class BiometricDisplayInfo( val providerIcon: Bitmap, val providerName: String, val displayTitleText: String, - val descriptionForCredential: String, + val descriptionForCredential: String?, val biometricRequestInfo: BiometricRequestInfo, + val displaySubtitleText: CharSequence? = null, ) /** @@ -85,7 +93,7 @@ data class BiometricState( * so that should this object exist, the result will be retrievable. */ data class BiometricResult( - val biometricAuthenticationResult: BiometricPrompt.AuthenticationResult + val biometricAuthenticationResult: BiometricPrompt.AuthenticationResult, ) /** @@ -97,15 +105,6 @@ data class BiometricError( ) /** - * Encapsulates the help callback results to easily manage biometric help states in the flow. - * To specify, this allows us to parse the onAuthenticationHelp method in the [BiometricPrompt]. - */ -data class BiometricHelp( - val helpCode: Int, - var helpString: CharSequence? = null -) - -/** * This is the entry point to start the integrated biometric prompt for 'get' flows. It captures * information specific to the get flow, along with required shared callbacks and more general * info across both flows, such as the tapped [EntryInfo] or [sendDataToProvider]. @@ -147,7 +146,7 @@ fun runBiometricFlowForGet( Log.d(TAG, "The BiometricPrompt API call begins.") runBiometricFlow(context, biometricDisplayInfo, callback, openMoreOptionsPage, - onBiometricFailureFallback, BiometricFlowType.GET) + onBiometricFailureFallback, BiometricFlowType.GET, onCancelFlowAndFinish) } /** @@ -191,14 +190,15 @@ fun runBiometricFlowForCreate( Log.d(TAG, "The BiometricPrompt API call begins.") runBiometricFlow(context, biometricDisplayInfo, callback, openMoreOptionsPage, - onBiometricFailureFallback, BiometricFlowType.CREATE) + onBiometricFailureFallback, BiometricFlowType.CREATE, onCancelFlowAndFinish) } /** * This will handle the logic for integrating credential manager with the biometric prompt for the * single account biometric experience. This simultaneously handles both the get and create flows, * by retrieving all the data from credential manager, and properly parsing that data into the - * biometric prompt. + * biometric prompt. It will fallback in cases where the biometric api cannot be called, or when + * only device credentials are requested. */ private fun runBiometricFlow( context: Context, @@ -206,28 +206,98 @@ private fun runBiometricFlow( callback: BiometricPrompt.AuthenticationCallback, openMoreOptionsPage: () -> Unit, onBiometricFailureFallback: (BiometricFlowType) -> Unit, - biometricFlowType: BiometricFlowType + biometricFlowType: BiometricFlowType, + onCancelFlowAndFinish: () -> Unit ) { - val biometricPrompt = setupBiometricPrompt(context, biometricDisplayInfo, openMoreOptionsPage, - biometricDisplayInfo.biometricRequestInfo, biometricFlowType) - - val cancellationSignal = CancellationSignal() - cancellationSignal.setOnCancelListener { - Log.d(TAG, "Your cancellation signal was called.") - // TODO(b/333445112) : Migrate towards passing along the developer cancellation signal - // or validate the necessity for this - } + try { + if (onlyUsingDeviceCredentials(biometricDisplayInfo, context)) { + onBiometricFailureFallback(biometricFlowType) + return + } - val executor = getMainExecutor(context) + val biometricPrompt = setupBiometricPrompt(context, biometricDisplayInfo, + openMoreOptionsPage, biometricDisplayInfo.biometricRequestInfo, onCancelFlowAndFinish) - try { - biometricPrompt.authenticate(cancellationSignal, executor, callback) - } catch (e: IllegalArgumentException) { + val cancellationSignal = CancellationSignal() + cancellationSignal.setOnCancelListener { + Log.d(TAG, "Your cancellation signal was called.") + // TODO(b/333445112) : Migrate towards passing along the developer cancellation signal + // or validate the necessity for this + } + + val executor = getMainExecutor(context) + + val cryptoOpId = getCryptoOpId(biometricDisplayInfo) + if (cryptoOpId != null) { + biometricPrompt.authenticate( + BiometricPrompt.CryptoObject(cryptoOpId.toLong()), + cancellationSignal, executor, callback) + } else { + biometricPrompt.authenticate(cancellationSignal, executor, callback) + } + } catch (e: Exception) { + // TODO(b/334923201) : Specialize exception catching Log.w(TAG, "Calling the biometric prompt API failed with: /n${e.localizedMessage}\n") onBiometricFailureFallback(biometricFlowType) } } +private fun getCryptoOpId(biometricDisplayInfo: BiometricDisplayInfo): Int? { + return biometricDisplayInfo.biometricRequestInfo.opId +} + +/** + * Determines if, given the allowed authenticators, the flow should fallback early. This has + * consistency because for biometrics to exist, **device credentials must exist**. Thus, fallbacks + * occur if *only* device credentials are available, to avoid going right into the PIN screen. + * Note that if device credential is the only available modality but not requested, or if none + * of the requested modalities are available, we propagate the error to the provider instead of + * falling back and expect them to handle it as they would prior. + * // TODO(b/334197980) : Finalize error propagation/not propagation in real use cases + */ +private fun onlyUsingDeviceCredentials( + biometricDisplayInfo: BiometricDisplayInfo, + context: Context +): Boolean { + val allowedAuthenticators = biometricDisplayInfo.biometricRequestInfo.allowedAuthenticators + if (allowedAuthenticators == BiometricManager.Authenticators.DEVICE_CREDENTIAL) { + return true + } + + val allowedAuthContainsDeviceCredential = containsBiometricAuthenticatorWithDeviceCredentials( + allowedAuthenticators) + + if (!allowedAuthContainsDeviceCredential) { + // At this point, allowed authenticators is requesting biometrics without device creds. + // Thus, a fallback mechanism will be displayed via our own negative button - "cancel". + // Beyond this point, fallbacks will occur if none of the stronger authenticators can + // be used. + return false + } + + val biometricManager = context.getSystemService(Context.BIOMETRIC_SERVICE) as BiometricManager + + if (allowedAuthContainsDeviceCredential && + biometricManager.canAuthenticate(Authenticators.BIOMETRIC_WEAK) != + BiometricManager.BIOMETRIC_SUCCESS && + biometricManager.canAuthenticate(Authenticators.BIOMETRIC_STRONG) != + BiometricManager.BIOMETRIC_SUCCESS) { + return true + } + + return false +} + +private fun containsBiometricAuthenticatorWithDeviceCredentials( + allowedAuthenticators: Int +): Boolean { + val allowedAuthContainsDeviceCredential = (allowedAuthenticators == + Authenticators.BIOMETRIC_WEAK or Authenticators.DEVICE_CREDENTIAL) || + (allowedAuthenticators == + Authenticators.BIOMETRIC_STRONG or Authenticators.DEVICE_CREDENTIAL) + return allowedAuthContainsDeviceCredential +} + /** * Sets up the biometric prompt with the UI specific bits. * // TODO(b/333445112) : Pass in opId once dependency is confirmed via CryptoObject @@ -237,49 +307,34 @@ private fun setupBiometricPrompt( biometricDisplayInfo: BiometricDisplayInfo, openMoreOptionsPage: () -> Unit, biometricRequestInfo: BiometricRequestInfo, - biometricFlowType: BiometricFlowType, + onCancelFlowAndFinish: () -> Unit ): BiometricPrompt { - val finalAuthenticators = removeDeviceCredential(biometricRequestInfo.allowedAuthenticators) + val listener = + DialogInterface.OnClickListener { _: DialogInterface?, _: Int -> openMoreOptionsPage() } + + val promptContentViewBuilder = PromptContentViewWithMoreOptionsButton.Builder() + .setMoreOptionsButtonListener(context.mainExecutor, listener) + biometricDisplayInfo.descriptionForCredential?.let { + promptContentViewBuilder.setDescription(it) } - val biometricPrompt = BiometricPrompt.Builder(context) + val biometricPromptBuilder = BiometricPrompt.Builder(context) .setTitle(biometricDisplayInfo.displayTitleText) - // TODO(b/333445112) : Migrate to using new methods and strings recently aligned upon - .setNegativeButton(context.getString(if (biometricFlowType == BiometricFlowType.GET) - R.string - .dropdown_presentation_more_sign_in_options_text else R.string.string_more_options), - getMainExecutor(context)) { _, _ -> - openMoreOptionsPage() - } - .setAllowedAuthenticators(finalAuthenticators) + .setAllowedAuthenticators(biometricRequestInfo.allowedAuthenticators) .setConfirmationRequired(true) .setLogoBitmap(biometricDisplayInfo.providerIcon) .setLogoDescription(biometricDisplayInfo.providerName) - .setDescription(biometricDisplayInfo.descriptionForCredential) - .build() - - return biometricPrompt -} + .setContentView(promptContentViewBuilder.build()) -// TODO(b/333445112) : Remove after larger level alignments made on fallback negative button -// For the time being, we do not support the pin fallback until UX is decided. -private fun removeDeviceCredential(requestAllowedAuthenticators: Int): Int { - var finalAuthenticators = requestAllowedAuthenticators - - if (requestAllowedAuthenticators == (BiometricManager.Authenticators.DEVICE_CREDENTIAL or - BiometricManager.Authenticators.BIOMETRIC_WEAK)) { - finalAuthenticators = BiometricManager.Authenticators.BIOMETRIC_WEAK - } - - if (requestAllowedAuthenticators == (BiometricManager.Authenticators.DEVICE_CREDENTIAL or - BiometricManager.Authenticators.BIOMETRIC_STRONG)) { - finalAuthenticators = BiometricManager.Authenticators.BIOMETRIC_WEAK + if (!containsBiometricAuthenticatorWithDeviceCredentials(biometricDisplayInfo + .biometricRequestInfo.allowedAuthenticators)) { + biometricPromptBuilder.setNegativeButton(context.getString(R.string.string_cancel), + getMainExecutor(context) + ) { _: DialogInterface?, _: Int -> onCancelFlowAndFinish() } } - if (requestAllowedAuthenticators == (BiometricManager.Authenticators.DEVICE_CREDENTIAL)) { - finalAuthenticators = BiometricManager.Authenticators.BIOMETRIC_WEAK - } + biometricDisplayInfo.displaySubtitleText?.let { biometricPromptBuilder.setSubtitle(it) } - return finalAuthenticators + return biometricPromptBuilder.build() } /** @@ -417,15 +472,29 @@ private fun retrieveBiometricGetDisplayValues( } val singleEntryType = selectedEntry.credentialType val username = selectedEntry.userName + + // TODO(b/330396140) : Finalize localization and parsing for specific sign in option flows + // (fingerprint, face, etc...)) displayTitleText = context.getString( generateDisplayTitleTextResCode(singleEntryType), getRequestDisplayInfo.appName ) + descriptionText = context.getString( - R.string.get_dialog_title_single_tap_for, + when (singleEntryType) { + CredentialType.PASSKEY -> + R.string.get_dialog_description_single_tap_passkey + + CredentialType.PASSWORD -> + R.string.get_dialog_description_single_tap_password + + CredentialType.UNKNOWN -> + R.string.get_dialog_description_single_tap_saved_sign_in + }, getRequestDisplayInfo.appName, username ) + return BiometricDisplayInfo(providerIcon = icon, providerName = providerName, displayTitleText = displayTitleText, descriptionForCredential = descriptionText, biometricRequestInfo = selectedEntry.biometricRequest as BiometricRequestInfo) @@ -451,23 +520,12 @@ private fun retrieveBiometricCreateDisplayValues( getCreateTitleResCode(createRequestDisplayInfo), createRequestDisplayInfo.appName ) - val descriptionText: String = context.getString( - when (createRequestDisplayInfo.type) { - CredentialType.PASSKEY -> - R.string.choose_create_single_tap_passkey_title - CredentialType.PASSWORD -> - R.string.choose_create_single_tap_password_title - - CredentialType.UNKNOWN -> - R.string.choose_create_single_tap_sign_in_title - }, - createRequestDisplayInfo.appName, - ) - // TODO(b/333445112) : Add a subtitle and any other recently aligned ideas + // TODO(b/330396140) : If footerDescription is null, determine if we need to fallback return BiometricDisplayInfo(providerIcon = icon, providerName = providerName, - displayTitleText = displayTitleText, descriptionForCredential = descriptionText, - biometricRequestInfo = selectedEntry.biometricRequest as BiometricRequestInfo) + displayTitleText = displayTitleText, descriptionForCredential = selectedEntry + .footerDescription, biometricRequestInfo = selectedEntry.biometricRequest + as BiometricRequestInfo, displaySubtitleText = createRequestDisplayInfo.title) } /** diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Entry.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Entry.kt index d13d86fccc97..149c14a24085 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Entry.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Entry.kt @@ -349,6 +349,38 @@ fun MoreOptionTopAppBar( } } +@Composable +fun MoreOptionTopAppBarWithCustomNavigation( + text: String, + onNavigationIconClicked: () -> Unit, + navigationIcon: ImageVector, + navigationIconContentDescription: String, + bottomPadding: Dp, +) { + Row( + modifier = Modifier.padding(top = 12.dp, bottom = bottomPadding), + verticalAlignment = Alignment.CenterVertically, + ) { + IconButton( + modifier = Modifier.padding(top = 8.dp, bottom = 8.dp, start = 4.dp).size(48.dp), + onClick = onNavigationIconClicked + ) { + Box( + modifier = Modifier.size(48.dp), + contentAlignment = Alignment.Center, + ) { + Icon( + imageVector = navigationIcon, + contentDescription = navigationIconContentDescription, + modifier = Modifier.size(24.dp).autoMirrored(), + tint = LocalAndroidColorScheme.current.onSurfaceVariant, + ) + } + } + LargeTitleText(text = text, modifier = Modifier.padding(horizontal = 4.dp)) + } +} + private fun Modifier.autoMirrored() = composed { when (LocalLayoutDirection.current) { LayoutDirection.Rtl -> graphicsLayer(scaleX = -1f) diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt index c1120bb356b7..ce4f402700d3 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetCredentialComponents.kt @@ -32,6 +32,7 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.foundation.lazy.items import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Close import androidx.compose.material.icons.outlined.QrCodeScanner import androidx.compose.material3.Divider import androidx.compose.material3.TextButton @@ -70,6 +71,7 @@ import com.android.credentialmanager.common.ui.HeadlineText import com.android.credentialmanager.common.ui.LargeLabelTextOnSurfaceVariant import com.android.credentialmanager.common.ui.ModalBottomSheet import com.android.credentialmanager.common.ui.MoreOptionTopAppBar +import com.android.credentialmanager.common.ui.MoreOptionTopAppBarWithCustomNavigation import com.android.credentialmanager.common.ui.SheetContainerCard import com.android.credentialmanager.common.ui.Snackbar import com.android.credentialmanager.common.ui.SnackbarActionText @@ -148,7 +150,7 @@ fun GetCredentialScreen( .currentScreenState == GetScreenState.BIOMETRIC_SELECTION) { BiometricSelectionPage( biometricEntry = getCredentialUiState.activeEntry, - onMoreOptionSelected = viewModel::getFlowOnMoreOptionSelected, + onMoreOptionSelected = viewModel::getFlowOnMoreOptionOnlySelected, onCancelFlowAndFinish = viewModel::onUserCancel, onIllegalStateAndFinish = viewModel::onIllegalUiState, requestDisplayInfo = getCredentialUiState.requestDisplayInfo, @@ -163,6 +165,28 @@ fun GetCredentialScreen( onBiometricPromptStateChange = viewModel::onBiometricPromptStateChange ) + } else if (credmanBiometricApiEnabled() && + getCredentialUiState.currentScreenState + == GetScreenState.ALL_SIGN_IN_OPTIONS_ONLY) { + AllSignInOptionCard( + providerInfoList = getCredentialUiState.providerInfoList, + providerDisplayInfo = getCredentialUiState.providerDisplayInfo, + onEntrySelected = viewModel::getFlowOnEntrySelected, + onBackButtonClicked = viewModel::onUserCancel, + onCancel = viewModel::onUserCancel, + onLog = { viewModel.logUiEvent(it) }, + customTopBar = { MoreOptionTopAppBarWithCustomNavigation( + text = stringResource( + R.string.get_dialog_title_sign_in_options), + onNavigationIconClicked = viewModel::onUserCancel, + navigationIcon = Icons.Filled.Close, + navigationIconContentDescription = + stringResource(R.string.accessibility_close_button), + bottomPadding = 0.dp + ) } + ) + viewModel.uiMetrics.log(GetCredentialEvent + .CREDMAN_GET_CRED_SCREEN_ALL_SIGN_IN_OPTIONS) } else { AllSignInOptionCard( providerInfoList = getCredentialUiState.providerInfoList, @@ -525,19 +549,8 @@ fun PrimarySelectionCardVImpl( R.string.get_dialog_title_choose_password_for else if (areAllPasskeysOnPrimaryScreen) R.string.get_dialog_title_choose_passkey_for - else if (primaryPageLockedEntryList.isNotEmpty() || - primaryPageCredentialEntryList.any { - it.sortedCredentialEntryList.first().credentialType != - CredentialType.PASSWORD && - it.sortedCredentialEntryList.first().credentialType != - CredentialType.PASSKEY - } - ) // An unknown typed / locked entry exists, and we can't say it is - // already saved, strictly speaking. Hence use a different title - // without the mention of "saved". + else R.string.get_dialog_title_choose_sign_in_for - else // All entries on the primary screen are passkeys or passwords - R.string.get_dialog_title_choose_saved_sign_in_for }, requestDisplayInfo.appName ), @@ -642,7 +655,13 @@ private fun findSingleProviderIdForPrimaryPage( return providerId } -/** Draws the secondary credential selection page, where all sign-in options are listed. */ +/** + * Draws the secondary credential selection page, where all sign-in options are listed. + * + * By default, this card has 'back' navigation whereby user can navigate back to invoke + * [onBackButtonClicked]. However if a different top bar with possibly a different navigation + * is required, then the caller of this Composable can set a [customTopBar]. + */ @Composable fun AllSignInOptionCard( providerInfoList: List<ProviderInfo>, @@ -651,16 +670,21 @@ fun AllSignInOptionCard( onBackButtonClicked: () -> Unit, onCancel: () -> Unit, onLog: @Composable (UiEventEnum) -> Unit, + customTopBar: (@Composable() () -> Unit)? = null ) { val sortedUserNameToCredentialEntryList = providerDisplayInfo.sortedUserNameToCredentialEntryList val authenticationEntryList = providerDisplayInfo.authenticationEntryList SheetContainerCard(topAppBar = { - MoreOptionTopAppBar( - text = stringResource(R.string.get_dialog_title_sign_in_options), - onNavigationIconClicked = onBackButtonClicked, - bottomPadding = 0.dp, - ) + if (customTopBar != null) { + customTopBar() + } else { + MoreOptionTopAppBar( + text = stringResource(R.string.get_dialog_title_sign_in_options), + onNavigationIconClicked = onBackButtonClicked, + bottomPadding = 0.dp, + ) + } }) { var isFirstSection = true // For username diff --git a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt index b03407b9ebea..8e7886119a34 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/getflow/GetModel.kt @@ -163,7 +163,11 @@ enum class GetScreenState { /** The single tap biometric selection page. */ BIOMETRIC_SELECTION, - /** The secondary credential selection page, where all sign-in options are listed. */ + /** + * The secondary credential selection page, where all sign-in options are listed. + * + * This state is expected to go back to PRIMARY_SELECTION on back navigation + */ ALL_SIGN_IN_OPTIONS, /** The snackbar only page when there's no account but only a remoteEntry. */ @@ -171,6 +175,14 @@ enum class GetScreenState { /** The snackbar when there are only auth entries and all of them turn out to be empty. */ UNLOCKED_AUTH_ENTRIES_ONLY, + + /** + * The secondary credential selection page, where all sign-in options are listed. + * + * This state has no option for the user to navigate back to PRIMARY_SELECTION, and + * instead can be terminated independently. + */ + ALL_SIGN_IN_OPTIONS_ONLY, } @@ -285,7 +297,7 @@ private fun toGetScreenState( providerDisplayInfo.remoteEntry != null) GetScreenState.REMOTE_ONLY else if (isRequestForAllOptions) - GetScreenState.ALL_SIGN_IN_OPTIONS + GetScreenState.ALL_SIGN_IN_OPTIONS_ONLY else if (isBiometricFlow(providerDisplayInfo, isFlowAutoSelectable(providerDisplayInfo))) GetScreenState.BIOMETRIC_SELECTION else GetScreenState.PRIMARY_SELECTION diff --git a/packages/InputDevices/res/values-en-rCA/strings.xml b/packages/InputDevices/res/values-en-rCA/strings.xml index aa4614b600c7..ac8ad0a57dee 100644 --- a/packages/InputDevices/res/values-en-rCA/strings.xml +++ b/packages/InputDevices/res/values-en-rCA/strings.xml @@ -50,6 +50,5 @@ <string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Belarusian"</string> <string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Mongolian"</string> <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgian"</string> - <!-- no translation found for keyboard_layout_thai_kedmanee (6637147314580760938) --> - <skip /> + <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"Thai (Kedmanee)"</string> </resources> diff --git a/packages/InputDevices/res/values-en-rXC/strings.xml b/packages/InputDevices/res/values-en-rXC/strings.xml index 58dbc43add8e..159b0e0ff165 100644 --- a/packages/InputDevices/res/values-en-rXC/strings.xml +++ b/packages/InputDevices/res/values-en-rXC/strings.xml @@ -50,6 +50,5 @@ <string name="keyboard_layout_belarusian" msgid="7619281752698687588">"Belarusian"</string> <string name="keyboard_layout_mongolian" msgid="7678483495823936626">"Mongolian"</string> <string name="keyboard_layout_georgian" msgid="4596185456863747454">"Georgian"</string> - <!-- no translation found for keyboard_layout_thai_kedmanee (6637147314580760938) --> - <skip /> + <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"Thai (Kedmanee)"</string> </resources> diff --git a/packages/InputDevices/res/values-pl/strings.xml b/packages/InputDevices/res/values-pl/strings.xml index 232a50590f36..31a3daca7539 100644 --- a/packages/InputDevices/res/values-pl/strings.xml +++ b/packages/InputDevices/res/values-pl/strings.xml @@ -50,6 +50,5 @@ <string name="keyboard_layout_belarusian" msgid="7619281752698687588">"białoruski"</string> <string name="keyboard_layout_mongolian" msgid="7678483495823936626">"mongolski"</string> <string name="keyboard_layout_georgian" msgid="4596185456863747454">"gruziński"</string> - <!-- no translation found for keyboard_layout_thai_kedmanee (6637147314580760938) --> - <skip /> + <string name="keyboard_layout_thai_kedmanee" msgid="6637147314580760938">"tajski (Kedmanee)"</string> </resources> diff --git a/packages/PackageInstaller/res/values-af/strings.xml b/packages/PackageInstaller/res/values-af/strings.xml index 140fa36b341c..13661e3c564a 100644 --- a/packages/PackageInstaller/res/values-af/strings.xml +++ b/packages/PackageInstaller/res/values-af/strings.xml @@ -58,7 +58,7 @@ <string name="uninstall_application_title" msgid="4045420072401428123">"Deïnstalleer program"</string> <string name="uninstall_update_title" msgid="824411791011583031">"Deïnstalleer opdatering"</string> <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> is deel van die volgende program:"</string> - <string name="uninstall_application_text" msgid="3816830743706143980">"Wil jy hierdie program deïnstalleer?"</string> + <string name="uninstall_application_text" msgid="3816830743706143980">"Wil jy hierdie app deïnstalleer?"</string> <string name="archive_application_text" msgid="8482325710714386348">"Jou persoonlike data sal gestoor word"</string> <string name="archive_application_text_all_users" msgid="3151229641681672580">"Argiveer hierdie app vir alle gebruikers? Jou persoonlike data sal gestoor word"</string> <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Argiveer hierdie app op jou werkprofiel? Jou persoonlike data sal gestoor word"</string> diff --git a/packages/PackageInstaller/res/values-gu/strings.xml b/packages/PackageInstaller/res/values-gu/strings.xml index f642e145c78f..82d5414291f1 100644 --- a/packages/PackageInstaller/res/values-gu/strings.xml +++ b/packages/PackageInstaller/res/values-gu/strings.xml @@ -58,7 +58,7 @@ <string name="uninstall_application_title" msgid="4045420072401428123">"ઍપ્લિકેશન અનઇન્સ્ટૉલ કરો"</string> <string name="uninstall_update_title" msgid="824411791011583031">"અપડેટ અનઇન્સ્ટૉલ કરો"</string> <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g>, નીચેની ઍપ્લિકેશનનો ભાગ છે:"</string> - <string name="uninstall_application_text" msgid="3816830743706143980">"શું તમે આ ઍપને અનઇન્સ્ટૉલ કરવા માંગો છો?"</string> + <string name="uninstall_application_text" msgid="3816830743706143980">"શું તમે આ ઍપને અનઇન્સ્ટૉલ કરવા માગો છો?"</string> <string name="archive_application_text" msgid="8482325710714386348">"તમારો વ્યક્તિગત ડેટા સાચવવામાં આવશે"</string> <string name="archive_application_text_all_users" msgid="3151229641681672580">"શું આ ઍપને તમામ વપરાશકર્તાઓ માટે આર્કાઇવ કરીએ? તમારો વ્યક્તિગત ડેટા સાચવવામાં આવશે"</string> <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"શું આ ઍપને તમારી ઑફિસની પ્રોફાઇલ પર આર્કાઇવ કરીએ? તમારો વ્યક્તિગત ડેટા સાચવવામાં આવશે"</string> diff --git a/packages/PackageInstaller/res/values-hy/strings.xml b/packages/PackageInstaller/res/values-hy/strings.xml index f2bc41e21e88..d9fb570d891e 100644 --- a/packages/PackageInstaller/res/values-hy/strings.xml +++ b/packages/PackageInstaller/res/values-hy/strings.xml @@ -58,7 +58,7 @@ <string name="uninstall_application_title" msgid="4045420072401428123">"Հավելվածի ապատեղադրում"</string> <string name="uninstall_update_title" msgid="824411791011583031">"Ապատեղադրել թարմացումը"</string> <string name="uninstall_activity_text" msgid="1928194674397770771">"<xliff:g id="ACTIVITY_NAME">%1$s</xliff:g> գործողությունը հետևյալ հավելվածի մասն է`"</string> - <string name="uninstall_application_text" msgid="3816830743706143980">"Ուզո՞ւմ եք ապատեղադրել այս հավելվածը։"</string> + <string name="uninstall_application_text" msgid="3816830743706143980">"Ուզում եք ապատեղադրե՞լ այս հավելվածը։"</string> <string name="archive_application_text" msgid="8482325710714386348">"Ձեր անձնական տվյալները կպահվեն"</string> <string name="archive_application_text_all_users" msgid="3151229641681672580">"Արխիվացնե՞լ այս հավելվածը բոլոր օգտատերերի համար։ Ձեր անձնական տվյալները կպահվեն"</string> <string name="archive_application_text_current_user_work_profile" msgid="1450487362134779752">"Արխիվացնե՞լ այս հավելվածը ձեր աշխատանքային պրոֆիլում։ Ձեր անձնական տվյալները կպահվեն"</string> diff --git a/packages/PrintSpooler/res/values-night/themes.xml b/packages/PrintSpooler/res/values-night/themes.xml index 4428dbbbaaae..3cc64a6ef266 100644 --- a/packages/PrintSpooler/res/values-night/themes.xml +++ b/packages/PrintSpooler/res/values-night/themes.xml @@ -30,6 +30,7 @@ <item name="android:windowIsTranslucent">true</item> <item name="android:windowActionBar">false</item> <item name="android:windowNoTitle">true</item> + <item name="android:windowOptOutEdgeToEdgeEnforcement">true</item> </style> </resources> diff --git a/packages/PrintSpooler/res/values/themes.xml b/packages/PrintSpooler/res/values/themes.xml index 4dcad10c793c..bd9602540878 100644 --- a/packages/PrintSpooler/res/values/themes.xml +++ b/packages/PrintSpooler/res/values/themes.xml @@ -31,6 +31,7 @@ <item name="android:windowActionBar">false</item> <item name="android:windowNoTitle">true</item> <item name="android:windowLightStatusBar">true</item> + <item name="android:windowOptOutEdgeToEdgeEnforcement">true</item> </style> </resources> diff --git a/packages/SettingsLib/DataStore/Android.bp b/packages/SettingsLib/DataStore/Android.bp index 9fafcabad81b..86c8f0da83cb 100644 --- a/packages/SettingsLib/DataStore/Android.bp +++ b/packages/SettingsLib/DataStore/Android.bp @@ -2,12 +2,17 @@ package { default_applicable_licenses: ["frameworks_base_license"], } +filegroup { + name: "SettingsLibDataStore-srcs", + srcs: ["src/**/*"], +} + android_library { name: "SettingsLibDataStore", defaults: [ "SettingsLintDefaults", ], - srcs: ["src/**/*"], + srcs: [":SettingsLibDataStore-srcs"], static_libs: [ "androidx.annotation_annotation", "androidx.collection_collection-ktx", diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreFileArchiver.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreFileArchiver.kt index 9d3fb66c7ce0..7644bc967281 100644 --- a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreFileArchiver.kt +++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreFileArchiver.kt @@ -19,6 +19,7 @@ package com.android.settingslib.datastore import android.app.backup.BackupDataInputStream import android.content.Context import android.util.Log +import androidx.annotation.VisibleForTesting import java.io.File import java.io.InputStream import java.io.OutputStream @@ -33,11 +34,9 @@ import java.util.zip.CheckedInputStream */ internal class BackupRestoreFileArchiver( private val context: Context, - private val fileStorages: List<BackupRestoreFileStorage>, + @get:VisibleForTesting internal val fileStorages: List<BackupRestoreFileStorage>, + override val name: String, ) : BackupRestoreStorage() { - override val name: String - get() = "file_archiver" - override fun createBackupRestoreEntities(): List<BackupRestoreEntity> = fileStorages.map { it.toBackupRestoreEntity() } @@ -88,7 +87,8 @@ internal class BackupRestoreFileArchiver( } } -private fun BackupRestoreFileStorage.toBackupRestoreEntity() = +@VisibleForTesting +internal fun BackupRestoreFileStorage.toBackupRestoreEntity() = object : BackupRestoreEntity { override val key: String get() = storageFilePath @@ -107,7 +107,7 @@ private fun BackupRestoreFileStorage.toBackupRestoreEntity() = Log.i(LOG_TAG, "[$name] $key not exist") return EntityBackupResult.DELETE } - val codec = codec() ?: defaultCodec() + val codec = defaultCodec() // MUST close to flush the data wrapBackupOutputStream(codec, outputStream).use { stream -> val bytesCopied = file.inputStream().use { it.copyTo(stream) } diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreStorage.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreStorage.kt index c4c00cbb8191..935f9ccf6ed9 100644 --- a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreStorage.kt +++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreStorage.kt @@ -22,6 +22,7 @@ import android.app.backup.BackupDataOutput import android.app.backup.BackupHelper import android.os.ParcelFileDescriptor import android.util.Log +import androidx.annotation.VisibleForTesting import androidx.collection.MutableScatterMap import com.google.common.io.ByteStreams import java.io.ByteArrayOutputStream @@ -60,10 +61,11 @@ abstract class BackupRestoreStorage : BackupHelper { * * Map key is the entity key, map value is the checksum of backup data. */ - protected val entityStates = MutableScatterMap<String, Long>() + @VisibleForTesting(otherwise = VisibleForTesting.PROTECTED) + val entityStates = MutableScatterMap<String, Long>() /** Entities created by [createBackupRestoreEntities]. This field is for restore only. */ - private var entities: List<BackupRestoreEntity>? = null + @VisibleForTesting internal var entities: List<BackupRestoreEntity>? = null /** Entities to back up and restore. */ abstract fun createBackupRestoreEntities(): List<BackupRestoreEntity> @@ -76,7 +78,7 @@ abstract class BackupRestoreStorage : BackupHelper { data: BackupDataOutput, newState: ParcelFileDescriptor, ) { - oldState.readEntityStates(entityStates) + readEntityStates(oldState, entityStates) val backupContext = BackupContext(data) if (!enableBackup(backupContext)) { Log.i(LOG_TAG, "[$name] Backup disabled") @@ -94,7 +96,10 @@ abstract class BackupRestoreStorage : BackupHelper { val codec = entity.codec() ?: defaultCodec() val result = try { - entity.backup(backupContext, wrapBackupOutputStream(codec, checkedOutputStream)) + // MUST close to flush all data + wrapBackupOutputStream(codec, checkedOutputStream).use { + entity.backup(backupContext, it) + } } catch (exception: Exception) { Log.e(LOG_TAG, "[$name] Fail to backup entity $key", exception) continue @@ -191,9 +196,13 @@ abstract class BackupRestoreStorage : BackupHelper { /** Callbacks when restore finished. */ open fun onRestoreFinished() {} - private fun ParcelFileDescriptor?.readEntityStates(state: MutableScatterMap<String, Long>) { + @VisibleForTesting + internal fun readEntityStates( + parcelFileDescriptor: ParcelFileDescriptor?, + state: MutableScatterMap<String, Long>, + ) { state.clear() - if (this == null) return + val fileDescriptor = parcelFileDescriptor?.fileDescriptor ?: return // do not close the streams val fileInputStream = FileInputStream(fileDescriptor) val dataInputStream = DataInputStream(fileInputStream) @@ -233,6 +242,7 @@ abstract class BackupRestoreStorage : BackupHelper { dataOutputStream.writeUTF(key) dataOutputStream.writeLong(value) } + dataOutputStream.flush() } catch (exception: Exception) { Log.e(LOG_TAG, "[$name] Fail to write state file", exception) } @@ -241,7 +251,7 @@ abstract class BackupRestoreStorage : BackupHelper { } companion object { - private const val STATE_VERSION: Byte = 0 + internal const val STATE_VERSION: Byte = 0 /** Checksum for entity backup data. */ fun createChecksum(): Checksum = CRC32() diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreStorageManager.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreStorageManager.kt index cfdcaff4d34c..82423473e682 100644 --- a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreStorageManager.kt +++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/BackupRestoreStorageManager.kt @@ -21,23 +21,32 @@ import android.app.backup.BackupAgentHelper import android.app.backup.BackupManager import android.content.Context import android.util.Log +import androidx.annotation.VisibleForTesting import com.google.common.util.concurrent.MoreExecutors import java.util.concurrent.ConcurrentHashMap /** Manager of [BackupRestoreStorage]. */ class BackupRestoreStorageManager private constructor(private val application: Application) { - private val storageWrappers = ConcurrentHashMap<String, StorageWrapper>() + @VisibleForTesting internal val storageWrappers = ConcurrentHashMap<String, StorageWrapper>() private val executor = MoreExecutors.directExecutor() /** * Adds all the registered [BackupRestoreStorage] as the helpers of given [BackupAgentHelper]. * - * All [BackupRestoreFileStorage]s will be wrapped as a single [BackupRestoreFileArchiver]. + * All [BackupRestoreFileStorage]s will be wrapped as a single [BackupRestoreFileArchiver], + * specify [fileArchiverName] to avoid key prefix conflict if needed. * + * @param backupAgentHelper backup agent helper to add helpers + * @param fileArchiverName key prefix of the [BackupRestoreFileArchiver], the value must not be + * changed in future * @see BackupAgentHelper.addHelper */ - fun addBackupAgentHelpers(backupAgentHelper: BackupAgentHelper) { + @JvmOverloads + fun addBackupAgentHelpers( + backupAgentHelper: BackupAgentHelper, + fileArchiverName: String = "file_archiver", + ) { val fileStorages = mutableListOf<BackupRestoreFileStorage>() for ((keyPrefix, storageWrapper) in storageWrappers) { val storage = storageWrapper.storage @@ -48,7 +57,7 @@ class BackupRestoreStorageManager private constructor(private val application: A } } // Always add file archiver even fileStorages is empty to handle forward compatibility - val fileArchiver = BackupRestoreFileArchiver(application, fileStorages) + val fileArchiver = BackupRestoreFileArchiver(application, fileStorages, fileArchiverName) backupAgentHelper.addHelper(fileArchiver.name, fileArchiver) } @@ -106,7 +115,8 @@ class BackupRestoreStorageManager private constructor(private val application: A /** Returns storage with given name, exception is raised if not found. */ fun getOrThrow(name: String): BackupRestoreStorage = storageWrappers[name]!!.storage - private inner class StorageWrapper(val storage: BackupRestoreStorage) : + @VisibleForTesting + internal inner class StorageWrapper(val storage: BackupRestoreStorage) : Observer, KeyedObserver<Any?> { init { when (storage) { @@ -139,7 +149,7 @@ class BackupRestoreStorageManager private constructor(private val application: A LOG_TAG, "Notify BackupManager dataChanged: storage=$name key=$key reason=$reason" ) - BackupManager.dataChanged(application.packageName) + BackupManager(application).dataChanged() } fun removeObserver() { diff --git a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SharedPreferencesStorage.kt b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SharedPreferencesStorage.kt index 0c1b41799f09..9f9c0d839744 100644 --- a/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SharedPreferencesStorage.kt +++ b/packages/SettingsLib/DataStore/src/com/android/settingslib/datastore/SharedPreferencesStorage.kt @@ -20,9 +20,11 @@ import android.content.Context import android.content.SharedPreferences import android.os.Build import android.util.Log -import androidx.core.content.ContextCompat +import androidx.annotation.VisibleForTesting import java.io.File +private fun defaultVerbose() = Build.TYPE == "eng" + /** * [SharedPreferences] based storage. * @@ -43,24 +45,35 @@ import java.io.File * @param verbose Verbose logging on key/value pairs during backup/restore. Enable for dev only! * @param filter Filter of key/value pairs for backup and restore. */ -class SharedPreferencesStorage +open class SharedPreferencesStorage @JvmOverloads constructor( context: Context, override val name: String, - mode: Int, - private val verbose: Boolean = (Build.TYPE == "eng"), + @get:VisibleForTesting internal val sharedPreferences: SharedPreferences, + private val codec: BackupCodec? = null, + private val verbose: Boolean = defaultVerbose(), private val filter: (String, Any?) -> Boolean = { _, _ -> true }, ) : BackupRestoreFileStorage(context, context.getSharedPreferencesFilePath(name)), KeyedObservable<String> by KeyedDataObservable() { - private val sharedPreferences = context.getSharedPreferences(name, mode) + @JvmOverloads + constructor( + context: Context, + name: String, + mode: Int, + codec: BackupCodec? = null, + verbose: Boolean = defaultVerbose(), + filter: (String, Any?) -> Boolean = { _, _ -> true }, + ) : this(context, name, context.getSharedPreferences(name, mode), codec, verbose, filter) /** Name of the intermediate SharedPreferences. */ - private val intermediateName: String + @VisibleForTesting + internal val intermediateName: String get() = "_br_$name" + @Suppress("DEPRECATION") private val intermediateSharedPreferences: SharedPreferences get() { // use MODE_MULTI_PROCESS to ensure a reload @@ -82,12 +95,15 @@ constructor( sharedPreferences.registerOnSharedPreferenceChangeListener(sharedPreferencesListener) } + override fun defaultCodec() = codec ?: super.defaultCodec() + override val backupFile: File // use a different file to avoid multi-thread file write get() = context.getSharedPreferencesFile(intermediateName) override fun prepareBackup(file: File) { - val editor = intermediateSharedPreferences.merge(sharedPreferences.all, "Backup") + val editor = + mergeSharedPreferences(intermediateSharedPreferences, sharedPreferences.all, "Backup") // commit to ensure data is write to disk synchronously if (!editor.commit()) { Log.w(LOG_TAG, "[$name] fail to commit") @@ -104,8 +120,8 @@ constructor( // observers consistently once restore finished. sharedPreferences.unregisterOnSharedPreferenceChangeListener(sharedPreferencesListener) val restored = intermediateSharedPreferences - val editor = sharedPreferences.merge(restored.all, "Restore") - editor.apply() // apply to avoid blocking + val editor = mergeSharedPreferences(sharedPreferences, restored.all, "Restore") + editor.commit() // commit to avoid race condition sharedPreferences.registerOnSharedPreferenceChangeListener(sharedPreferencesListener) // clear the intermediate SharedPreferences restored.delete(intermediateName) @@ -115,7 +131,7 @@ constructor( if (deleteSharedPreferences(name)) { Log.i(LOG_TAG, "SharedPreferences $name deleted") } else { - edit().clear().apply() + edit().clear().commit() // commit to avoid potential race condition } } @@ -126,11 +142,13 @@ constructor( false } - private fun SharedPreferences.merge( + @VisibleForTesting + internal open fun mergeSharedPreferences( + sharedPreferences: SharedPreferences, entries: Map<String, Any?>, - operation: String + operation: String, ): SharedPreferences.Editor { - val editor = edit() + val editor = sharedPreferences.edit() for ((key, value) in entries) { if (!filter.invoke(key, value)) { if (verbose) Log.v(LOG_TAG, "[$name] $operation skips $key=$value") @@ -184,7 +202,7 @@ constructor( companion object { private fun Context.getSharedPreferencesFilePath(name: String): String { val file = getSharedPreferencesFile(name) - return file.relativeTo(ContextCompat.getDataDir(this)!!).toString() + return file.relativeTo(dataDirCompat).toString() } /** Returns the absolute path of shared preferences file. */ diff --git a/packages/SettingsLib/DataStore/tests/Android.bp b/packages/SettingsLib/DataStore/tests/Android.bp index 8770dfa013d0..5d000ebe9417 100644 --- a/packages/SettingsLib/DataStore/tests/Android.bp +++ b/packages/SettingsLib/DataStore/tests/Android.bp @@ -9,11 +9,16 @@ android_app { android_robolectric_test { name: "SettingsLibDataStoreTest", - srcs: ["src/**/*"], + srcs: [ + ":SettingsLibDataStore-srcs", // b/240432457 + "src/**/*", + ], static_libs: [ - "SettingsLibDataStore", + "androidx.collection_collection-ktx", + "androidx.core_core-ktx", "androidx.test.ext.junit", "guava", + "kotlin-test", "mockito-robolectric-prebuilt", // mockito deps order matters! "mockito-kotlin2", ], diff --git a/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/BackupCodecTest.kt b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/BackupCodecTest.kt new file mode 100644 index 000000000000..867831b7f2bd --- /dev/null +++ b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/BackupCodecTest.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.settingslib.datastore + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.google.common.truth.Truth.assertThat +import com.google.common.truth.Truth.assertWithMessage +import java.io.ByteArrayInputStream +import java.io.ByteArrayOutputStream +import kotlin.random.Random +import kotlin.test.assertFailsWith +import org.junit.Test +import org.junit.runner.RunWith + +/** Tests of [BackupCodec]. */ +@RunWith(AndroidJUnit4::class) +class BackupCodecTest { + @Test + fun name() { + val names = mutableSetOf<String>() + for (codec in allCodecs()) { + assertThat(names).doesNotContain(codec.name) + names.add(codec.name) + } + } + + @Test + fun fromId() { + for (codec in allCodecs()) { + assertThat(BackupCodec.fromId(codec.id)).isInstanceOf(codec::class.java) + } + } + + @Test + fun fromId_unknownId() { + assertFailsWith(IllegalArgumentException::class) { BackupCodec.fromId(-1) } + } + + @Test + fun encode_decode() { + val random = Random.Default + fun test(codec: BackupCodec, size: Int) { + val data = random.nextBytes(size) + + // encode + val outputStream = ByteArrayOutputStream() + codec.encode(outputStream).use { it.write(data) } + + // decode + val inputStream = ByteArrayInputStream(outputStream.toByteArray()) + val result = codec.decode(inputStream).use { it.readBytes() } + + assertWithMessage("$size bytes: $data").that(result).isEqualTo(data) + } + + for (codec in allCodecs()) { + test(codec, 0) + repeat(10) { test(codec, random.nextInt(1, 1024)) } + } + } +} diff --git a/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/BackupRestoreContextTest.kt b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/BackupRestoreContextTest.kt new file mode 100644 index 000000000000..911665a28809 --- /dev/null +++ b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/BackupRestoreContextTest.kt @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib.datastore + +import android.app.backup.BackupDataOutput +import android.os.Build +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.google.common.truth.Truth.assertThat +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.mock + +/** Tests of [BackupContext] and [RestoreContext]. */ +@RunWith(AndroidJUnit4::class) +class BackupRestoreContextTest { + @Test + fun backupContext_quota() { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return + val data = mock<BackupDataOutput> { on { quota } doReturn 10L } + assertThat(BackupContext(data).quota).isEqualTo(10) + } + + @Test + fun backupContext_transportFlags() { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) return + val data = mock<BackupDataOutput> { on { transportFlags } doReturn 5 } + assertThat(BackupContext(data).transportFlags).isEqualTo(5) + } +} diff --git a/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/BackupRestoreFileArchiverTest.kt b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/BackupRestoreFileArchiverTest.kt new file mode 100644 index 000000000000..6cce45320a04 --- /dev/null +++ b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/BackupRestoreFileArchiverTest.kt @@ -0,0 +1,275 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib.datastore + +import android.app.Application +import androidx.test.core.app.ApplicationProvider.getApplicationContext +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.google.common.truth.Truth.assertThat +import java.io.ByteArrayInputStream +import java.io.ByteArrayOutputStream +import java.io.File +import java.io.IOException +import java.io.InputStream +import kotlin.random.Random +import org.junit.Rule +import org.junit.Test +import org.junit.rules.TemporaryFolder +import org.junit.runner.RunWith +import org.mockito.kotlin.any +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.mock +import org.mockito.kotlin.never +import org.mockito.kotlin.verify + +/** Tests of [BackupRestoreFileArchiver]. */ +@RunWith(AndroidJUnit4::class) +class BackupRestoreFileArchiverTest { + private val random = Random.Default + private val application: Application = getApplicationContext() + @get:Rule val temporaryFolder = TemporaryFolder(application.dataDirCompat) + + @Test + fun createBackupRestoreEntities() { + val fileStorages = mutableListOf<BackupRestoreFileStorage>() + for (count in 0 until 3) { + val fileArchiver = BackupRestoreFileArchiver(application, fileStorages, "") + fileArchiver.createBackupRestoreEntities().apply { + assertThat(this).hasSize(fileStorages.size) + for (index in 0 until count) { + assertThat(get(index).key).isEqualTo(fileStorages[index].storageFilePath) + } + } + fileStorages.add(FileStorage("storage", "path$count")) + } + } + + @Test + fun wrapBackupOutputStream() { + val fileArchiver = BackupRestoreFileArchiver(application, listOf(), "") + val outputStream = ByteArrayOutputStream() + assertThat(fileArchiver.wrapBackupOutputStream(BackupZipCodec.BEST_SPEED, outputStream)) + .isSameInstanceAs(outputStream) + } + + @Test + fun wrapRestoreInputStream() { + val fileArchiver = BackupRestoreFileArchiver(application, listOf(), "") + val inputStream = ByteArrayInputStream(byteArrayOf()) + assertThat(fileArchiver.wrapRestoreInputStream(BackupZipCodec.BEST_SPEED, inputStream)) + .isSameInstanceAs(inputStream) + } + + @Test + fun restoreEntity_disabled() { + val file = temporaryFolder.newFile() + val key = file.name + val fileStorage = FileStorage("fs", key, restoreEnabled = false) + + BackupRestoreFileArchiver(application, listOf(fileStorage), "archiver").apply { + restoreEntity(newBackupDataInputStream(key, byteArrayOf())) + assertThat(entityStates.asMap()).isEmpty() + } + } + + @Test + fun restoreEntity_raiseIOException() { + val key = "key" + val fileStorage = FileStorage("fs", key) + BackupRestoreFileArchiver(application, listOf(fileStorage), "archiver").apply { + restoreEntity(newBackupDataInputStream(key, byteArrayOf(), IOException())) + assertThat(entityStates.asMap()).isEmpty() + } + } + + @Test + fun restoreEntity_onRestoreFinished_raiseException() { + val key = "key" + val fileStorage = FileStorage("fs", key, restoreException = IllegalStateException()) + BackupRestoreFileArchiver(application, listOf(fileStorage), "archiver").apply { + val data = random.nextBytes(random.nextInt(10)) + val outputStream = ByteArrayOutputStream() + fileStorage.wrapBackupOutputStream(fileStorage.defaultCodec(), outputStream).use { + it.write(data) + } + val payload = outputStream.toByteArray() + restoreEntity(newBackupDataInputStream(key, payload)) + assertThat(entityStates.asMap()).isEmpty() + } + } + + @Test + fun restoreEntity_forwardCompatibility() { + val key = "key" + val fileStorage = FileStorage("fs", key) + for (codec in allCodecs()) { + BackupRestoreFileArchiver(application, listOf(), "archiver").apply { + val data = random.nextBytes(random.nextInt(MAX_DATA_SIZE)) + val outputStream = ByteArrayOutputStream() + fileStorage.wrapBackupOutputStream(codec, outputStream).use { it.write(data) } + val payload = outputStream.toByteArray() + + restoreEntity(newBackupDataInputStream(key, payload)) + + assertThat(entityStates.asMap()).apply { + hasSize(1) + containsKey(key) + } + assertThat(fileStorage.restoreFile.readBytes()).isEqualTo(data) + } + } + } + + @Test + fun restoreEntity() { + val folder = File(application.dataDirCompat, "backup") + val file = File(folder, "file") + val key = "${folder.name}${File.separator}${file.name}" + fun test(codec: BackupCodec, size: Int) { + val fileStorage = FileStorage("fs", key, if (size % 2 == 0) codec else null) + val data = random.nextBytes(size) + val outputStream = ByteArrayOutputStream() + fileStorage.wrapBackupOutputStream(codec, outputStream).use { it.write(data) } + val payload = outputStream.toByteArray() + + val fileArchiver = + BackupRestoreFileArchiver(application, listOf(fileStorage), "archiver") + fileArchiver.restoreEntity(newBackupDataInputStream(key, payload)) + + assertThat(fileArchiver.entityStates.asMap()).apply { + hasSize(1) + containsKey(key) + } + assertThat(file.readBytes()).isEqualTo(data) + } + + for (codec in allCodecs()) { + for (size in 0 until 100) test(codec, size) + repeat(10) { test(codec, random.nextInt(100, MAX_DATA_SIZE)) } + } + } + + @Test + fun onRestoreFinished() { + val fileStorage = mock<BackupRestoreFileStorage>() + val fileArchiver = BackupRestoreFileArchiver(application, listOf(fileStorage), "") + + fileArchiver.onRestoreFinished() + + verify(fileStorage).onRestoreFinished() + } + + @Test + fun toBackupRestoreEntity_backup_disabled() { + val context = BackupContext(mock()) + val fileStorage = + mock<BackupRestoreFileStorage> { on { enableBackup(context) } doReturn false } + + assertThat(fileStorage.toBackupRestoreEntity().backup(context, ByteArrayOutputStream())) + .isEqualTo(EntityBackupResult.INTACT) + + verify(fileStorage, never()).prepareBackup(any()) + } + + @Test + fun toBackupRestoreEntity_backup_fileNotExist() { + val context = BackupContext(mock()) + val file = File("NotExist") + val fileStorage = + mock<BackupRestoreFileStorage> { + on { enableBackup(context) } doReturn true + on { backupFile } doReturn file + } + + assertThat(fileStorage.toBackupRestoreEntity().backup(context, ByteArrayOutputStream())) + .isEqualTo(EntityBackupResult.DELETE) + + verify(fileStorage).prepareBackup(file) + verify(fileStorage, never()).defaultCodec() + } + + @Test + fun toBackupRestoreEntity_backup() { + val context = BackupContext(mock()) + val file = temporaryFolder.newFile() + + fun test(codec: BackupCodec, size: Int) { + val data = random.nextBytes(size) + file.outputStream().use { it.write(data) } + + val outputStream = ByteArrayOutputStream() + val fileStorage = + mock<BackupRestoreFileStorage> { + on { enableBackup(context) } doReturn true + on { backupFile } doReturn file + on { defaultCodec() } doReturn codec + on { wrapBackupOutputStream(any(), any()) }.thenCallRealMethod() + on { wrapRestoreInputStream(any(), any()) }.thenCallRealMethod() + on { prepareBackup(any()) }.thenCallRealMethod() + on { onBackupFinished(any()) }.thenCallRealMethod() + } + + assertThat(fileStorage.toBackupRestoreEntity().backup(context, outputStream)) + .isEqualTo(EntityBackupResult.UPDATE) + + verify(fileStorage).prepareBackup(file) + verify(fileStorage).onBackupFinished(file) + + val decodedData = + fileStorage + .wrapRestoreInputStream(codec, ByteArrayInputStream(outputStream.toByteArray())) + .readBytes() + assertThat(decodedData).isEqualTo(data) + } + + for (codec in allCodecs()) { + // test small data to ensure correctness + for (size in 0 until 100) test(codec, size) + repeat(10) { test(codec, random.nextInt(100, MAX_DATA_SIZE)) } + } + } + + @Test + fun toBackupRestoreEntity_restore() { + val restoreContext = RestoreContext("storage") + val inputStream = + object : InputStream() { + override fun read() = throw IllegalStateException() + + override fun read(b: ByteArray, off: Int, len: Int) = throw IllegalStateException() + } + FileStorage("storage", "path").toBackupRestoreEntity().restore(restoreContext, inputStream) + } + + private open class FileStorage( + override val name: String, + filePath: String, + private val codec: BackupCodec? = null, + private val restoreEnabled: Boolean? = null, + private val restoreException: Exception? = null, + ) : BackupRestoreFileStorage(getApplicationContext(), filePath) { + + override fun defaultCodec() = codec ?: super.defaultCodec() + + override fun enableRestore() = restoreEnabled ?: super.enableRestore() + + override fun onRestoreFinished(file: File) { + super.onRestoreFinished(file) + if (restoreException != null) throw restoreException + } + } +} diff --git a/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/BackupRestoreFileStorageTest.kt b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/BackupRestoreFileStorageTest.kt new file mode 100644 index 000000000000..422273d9ce14 --- /dev/null +++ b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/BackupRestoreFileStorageTest.kt @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib.datastore + +import android.app.Application +import android.os.Build +import androidx.test.core.app.ApplicationProvider.getApplicationContext +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.google.common.truth.Truth.assertThat +import java.io.File +import kotlin.test.assertFailsWith +import org.junit.Test +import org.junit.runner.RunWith + +/** Tests of [BackupRestoreFileStorage]. */ +@RunWith(AndroidJUnit4::class) +class BackupRestoreFileStorageTest { + private val application: Application = getApplicationContext() + + @Test + fun dataDirCompat() { + val expected = + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + application.dataDir + } else { + File(application.applicationInfo.dataDir) + } + assertThat(application.dataDirCompat).isEqualTo(expected) + } + + @Test + fun backupFile() { + assertThat(FileStorage("path").backupFile.toString()) + .startsWith(application.dataDirCompat.toString()) + } + + @Test + fun restoreFile() { + FileStorage("path").apply { assertThat(restoreFile).isEqualTo(backupFile) } + } + + @Test + fun checkFilePaths() { + FileStorage("path").checkFilePaths() + } + + @Test + fun checkFilePaths_emptyFilePath() { + assertFailsWith(IllegalArgumentException::class) { FileStorage("").checkFilePaths() } + } + + @Test + fun checkFilePaths_absoluteFilePath() { + assertFailsWith(IllegalArgumentException::class) { + FileStorage("${File.separatorChar}file").checkFilePaths() + } + } + + @Test + fun checkFilePaths_backupFile() { + assertFailsWith(IllegalArgumentException::class) { + FileStorage("path", fileForBackup = File("path")).checkFilePaths() + } + } + + @Test + fun checkFilePaths_restoreFile() { + assertFailsWith(IllegalArgumentException::class) { + FileStorage("path", fileForRestore = File("path")).checkFilePaths() + } + } + + @Test + fun createBackupRestoreEntities() { + assertThat(FileStorage("path").createBackupRestoreEntities()).isEmpty() + } + + private class FileStorage( + filePath: String, + val fileForBackup: File? = null, + val fileForRestore: File? = null, + ) : BackupRestoreFileStorage(getApplicationContext(), filePath) { + override val name = "storage" + + override val backupFile: File + get() = fileForBackup ?: super.backupFile + + override val restoreFile: File + get() = fileForRestore ?: super.restoreFile + } +} diff --git a/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/BackupRestoreStorageManagerTest.kt b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/BackupRestoreStorageManagerTest.kt new file mode 100644 index 000000000000..d8f502854402 --- /dev/null +++ b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/BackupRestoreStorageManagerTest.kt @@ -0,0 +1,237 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib.datastore + +import android.app.Application +import android.app.backup.BackupAgentHelper +import android.app.backup.BackupHelper +import android.app.backup.BackupManager +import androidx.test.core.app.ApplicationProvider.getApplicationContext +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.google.common.truth.Truth.assertThat +import com.google.common.util.concurrent.MoreExecutors.directExecutor +import kotlin.test.assertFailsWith +import org.junit.After +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.kotlin.any +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.mock +import org.mockito.kotlin.never +import org.mockito.kotlin.reset +import org.mockito.kotlin.verify +import org.robolectric.Shadows +import org.robolectric.shadows.ShadowBackupManager + +/** Tests of [BackupRestoreStorageManager]. */ +@RunWith(AndroidJUnit4::class) +class BackupRestoreStorageManagerTest { + private val application: Application = getApplicationContext() + private val manager = BackupRestoreStorageManager.getInstance(application) + private val fileStorage = FileStorage("fileStorage") + private val keyedStorage = KeyedStorage("keyedStorage") + + private val storage1 = mock<ObservableBackupRestoreStorage> { on { name } doReturn "1" } + private val storage2 = mock<ObservableBackupRestoreStorage> { on { name } doReturn "1" } + + @After + fun tearDown() { + manager.removeAll() + ShadowBackupManager.reset() + } + + @Test + fun getInstance() { + assertThat(BackupRestoreStorageManager.getInstance(application)).isSameInstanceAs(manager) + } + + @Test + fun addBackupAgentHelpers() { + val fs = FileStorage("fs") + manager.add(keyedStorage, fileStorage, storage1, fs) + val backupAgentHelper = DummyBackupAgentHelper() + manager.addBackupAgentHelpers(backupAgentHelper) + backupAgentHelper.backupHelpers.apply { + assertThat(size).isEqualTo(3) + assertThat(remove(keyedStorage.name)).isSameInstanceAs(keyedStorage) + assertThat(remove(storage1.name)).isSameInstanceAs(storage1) + val fileArchiver = entries.first().value as BackupRestoreFileArchiver + assertThat(fileArchiver.fileStorages.toSet()).containsExactly(fs, fileStorage) + } + } + + @Test + fun addBackupAgentHelpers_withoutFileStorage() { + manager.add(keyedStorage, storage1) + val backupAgentHelper = DummyBackupAgentHelper() + manager.addBackupAgentHelpers(backupAgentHelper) + backupAgentHelper.backupHelpers.apply { + assertThat(size).isEqualTo(3) + assertThat(remove(keyedStorage.name)).isSameInstanceAs(keyedStorage) + assertThat(remove(storage1.name)).isSameInstanceAs(storage1) + val fileArchiver = entries.first().value as BackupRestoreFileArchiver + assertThat(fileArchiver.fileStorages).isEmpty() + } + } + + @Test + fun add() { + manager.add(keyedStorage, fileStorage, storage1) + assertThat(manager.storageWrappers).apply { + hasSize(3) + containsKey(keyedStorage.name) + containsKey(fileStorage.name) + containsKey(storage1.name) + } + } + + @Test + fun add_identicalName() { + manager.add(storage1) + assertFailsWith(IllegalStateException::class) { manager.add(storage1) } + assertFailsWith(IllegalStateException::class) { manager.add(storage2) } + } + + @Test + fun add_nonObservable() { + assertFailsWith(IllegalArgumentException::class) { + manager.add(mock<BackupRestoreStorage>()) + } + } + + @Test + fun removeAll() { + add() + manager.removeAll() + assertThat(manager.storageWrappers).isEmpty() + } + + @Test + fun remove() { + manager.add(keyedStorage, fileStorage) + assertThat(manager.remove(storage1.name)).isNull() + assertThat(manager.remove(keyedStorage.name)).isSameInstanceAs(keyedStorage) + assertThat(manager.remove(fileStorage.name)).isSameInstanceAs(fileStorage) + } + + @Test + fun get() { + manager.add(keyedStorage, fileStorage) + assertThat(manager.get(storage1.name)).isNull() + assertThat(manager.get(keyedStorage.name)).isSameInstanceAs(keyedStorage) + assertThat(manager.get(fileStorage.name)).isSameInstanceAs(fileStorage) + } + + @Test + fun getOrThrow() { + manager.add(keyedStorage, fileStorage) + assertFailsWith(NullPointerException::class) { manager.getOrThrow(storage1.name) } + assertThat(manager.getOrThrow(keyedStorage.name)).isSameInstanceAs(keyedStorage) + assertThat(manager.getOrThrow(fileStorage.name)).isSameInstanceAs(fileStorage) + } + + @Test + fun notifyRestoreFinished() { + manager.add(keyedStorage, fileStorage) + val keyedObserver = mock<KeyedObserver<String>>() + val anyKeyObserver = mock<KeyedObserver<String?>>() + val observer = mock<Observer>() + val executor = directExecutor() + keyedStorage.addObserver("key", keyedObserver, executor) + keyedStorage.addObserver(anyKeyObserver, executor) + fileStorage.addObserver(observer, executor) + + manager.onRestoreFinished() + + verify(keyedObserver).onKeyChanged("key", ChangeReason.RESTORE) + verify(anyKeyObserver).onKeyChanged(null, ChangeReason.RESTORE) + verify(observer).onChanged(ChangeReason.RESTORE) + if (isRobolectric()) { + Shadows.shadowOf(BackupManager(application)).apply { + assertThat(isDataChanged).isFalse() + assertThat(dataChangedCount).isEqualTo(0) + } + } + } + + @Test + fun notifyBackupManager() { + manager.add(keyedStorage, fileStorage) + val keyedObserver = mock<KeyedObserver<String>>() + val anyKeyObserver = mock<KeyedObserver<String?>>() + val observer = mock<Observer>() + val executor = directExecutor() + keyedStorage.addObserver("key", keyedObserver, executor) + keyedStorage.addObserver(anyKeyObserver, executor) + fileStorage.addObserver(observer, executor) + + val backupManager = + if (isRobolectric()) Shadows.shadowOf(BackupManager(application)) else null + backupManager?.apply { + assertThat(isDataChanged).isFalse() + assertThat(dataChangedCount).isEqualTo(0) + } + + fileStorage.notifyChange(ChangeReason.UPDATE) + verify(observer).onChanged(ChangeReason.UPDATE) + verify(keyedObserver, never()).onKeyChanged(any(), any()) + verify(anyKeyObserver, never()).onKeyChanged(any(), any()) + reset(observer) + backupManager?.apply { + assertThat(isDataChanged).isTrue() + assertThat(dataChangedCount).isEqualTo(1) + } + + keyedStorage.notifyChange("key", ChangeReason.DELETE) + verify(observer, never()).onChanged(any()) + verify(keyedObserver).onKeyChanged("key", ChangeReason.DELETE) + verify(anyKeyObserver).onKeyChanged("key", ChangeReason.DELETE) + backupManager?.apply { + assertThat(isDataChanged).isTrue() + assertThat(dataChangedCount).isEqualTo(2) + } + reset(keyedObserver) + + // backup manager is not notified for restore event + fileStorage.notifyChange(ChangeReason.RESTORE) + keyedStorage.notifyChange("key", ChangeReason.RESTORE) + verify(observer).onChanged(ChangeReason.RESTORE) + verify(keyedObserver).onKeyChanged("key", ChangeReason.RESTORE) + verify(anyKeyObserver).onKeyChanged("key", ChangeReason.RESTORE) + backupManager?.apply { + assertThat(isDataChanged).isTrue() + assertThat(dataChangedCount).isEqualTo(2) + } + } + + private class KeyedStorage(override val name: String) : + BackupRestoreStorage(), KeyedObservable<String> by KeyedDataObservable() { + + override fun createBackupRestoreEntities(): List<BackupRestoreEntity> = listOf() + } + + private class FileStorage(override val name: String) : + BackupRestoreFileStorage(getApplicationContext(), "file"), Observable by DataObservable() + + private class DummyBackupAgentHelper : BackupAgentHelper() { + val backupHelpers = mutableMapOf<String, BackupHelper>() + + override fun addHelper(keyPrefix: String, helper: BackupHelper) { + backupHelpers[keyPrefix] = helper + } + } +} diff --git a/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/BackupRestoreStorageTest.kt b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/BackupRestoreStorageTest.kt new file mode 100644 index 000000000000..99998ffc13ec --- /dev/null +++ b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/BackupRestoreStorageTest.kt @@ -0,0 +1,414 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib.datastore + +import android.app.backup.BackupAgentHelper +import android.app.backup.BackupDataInput +import android.app.backup.BackupDataInputStream +import android.app.backup.BackupDataOutput +import android.os.ParcelFileDescriptor +import android.os.ParcelFileDescriptor.MODE_APPEND +import android.os.ParcelFileDescriptor.MODE_READ_ONLY +import android.os.ParcelFileDescriptor.MODE_WRITE_ONLY +import androidx.collection.MutableScatterMap +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.google.common.truth.Truth.assertThat +import java.io.DataOutputStream +import java.io.File +import java.io.FileDescriptor +import java.io.FileOutputStream +import java.io.InputStream +import java.io.OutputStream +import kotlin.random.Random +import org.junit.Rule +import org.junit.Test +import org.junit.rules.TemporaryFolder +import org.junit.runner.RunWith +import org.mockito.kotlin.any +import org.mockito.kotlin.argumentCaptor +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.doThrow +import org.mockito.kotlin.mock +import org.mockito.kotlin.never +import org.mockito.kotlin.reset +import org.mockito.kotlin.spy +import org.mockito.kotlin.verify + +/** Tests of [BackupRestoreStorage]. */ +@RunWith(AndroidJUnit4::class) +class BackupRestoreStorageTest { + @get:Rule val temporaryFolder = TemporaryFolder() + + private val entity1 = Entity("key1", "value1".toByteArray()) + private val entity1NoOpCodec = Entity("key1", "value1".toByteArray(), BackupNoOpCodec()) + private val entity2 = Entity("key2", "value2".toByteArray(), BackupZipCodec.BEST_SPEED) + + @Test + fun performBackup_disabled() { + val storage = spy(TestStorage().apply { enabled = false }) + val unused = performBackup { data, newState -> storage.performBackup(null, data, newState) } + verify(storage, never()).createBackupRestoreEntities() + assertThat(storage.entities).isNull() + assertThat(storage.entityStates.size).isEqualTo(0) + } + + @Test + fun performBackup_enabled() { + val storage = spy(TestStorage()) + val unused = performBackup { data, newState -> storage.performBackup(null, data, newState) } + verify(storage).createBackupRestoreEntities() + assertThat(storage.entities).isNull() + assertThat(storage.entityStates.size).isEqualTo(0) + } + + @Test + fun performBackup_entityBackupWithException() { + val entity = + mock<BackupRestoreEntity> { + on { key } doReturn "" + on { backup(any(), any()) } doThrow IllegalStateException() + } + val storage = TestStorage(entity, entity1) + + val (_, stateFile) = + performBackup { data, newState -> storage.performBackup(null, data, newState) } + + assertThat(storage.readEntityStates(stateFile)).apply { + hasSize(1) + containsKey(entity1.key) + } + } + + @Test + fun performBackup_update_unchanged() { + performBackupTest({}) { entityStates, newEntityStates -> + assertThat(entityStates).isEqualTo(newEntityStates) + } + } + + @Test + fun performBackup_intact() { + performBackupTest({ entity1.backupResult = EntityBackupResult.INTACT }) { + entityStates, + newEntityStates -> + assertThat(entityStates).isEqualTo(newEntityStates) + } + } + + @Test + fun performBackup_delete() { + performBackupTest({ entity1.backupResult = EntityBackupResult.DELETE }) { _, newEntityStates + -> + assertThat(newEntityStates.size).isEqualTo(1) + assertThat(newEntityStates).containsKey(entity2.key) + } + } + + private fun performBackupTest( + update: () -> Unit, + verification: (Map<String, Long>, Map<String, Long>) -> Unit, + ) { + val storage = TestStorage(entity1, entity2) + val (_, stateFile) = + performBackup { data, newState -> storage.performBackup(null, data, newState) } + + val entityStates = storage.readEntityStates(stateFile) + assertThat(entityStates).apply { + hasSize(2) + containsKey(entity1.key) + containsKey(entity2.key) + } + + update.invoke() + val (_, newStateFile) = + performBackup { data, newState -> + stateFile.toParcelFileDescriptor(MODE_READ_ONLY).use { + storage.performBackup(it, data, newState) + } + } + verification.invoke(entityStates, storage.readEntityStates(newStateFile)) + } + + @Test + fun restoreEntity_disabled() { + val storage = spy(TestStorage().apply { enabled = false }) + temporaryFolder.newFile().toParcelFileDescriptor(MODE_READ_ONLY).use { + storage.restoreEntity(it.toBackupDataInputStream()) + } + verify(storage, never()).createBackupRestoreEntities() + assertThat(storage.entities).isNull() + assertThat(storage.entityStates.size).isEqualTo(0) + } + + @Test + fun restoreEntity_entityNotFound() { + val storage = TestStorage() + temporaryFolder.newFile().toParcelFileDescriptor(MODE_READ_ONLY).use { + val backupDataInputStream = it.toBackupDataInputStream() + backupDataInputStream.setKey("") + storage.restoreEntity(backupDataInputStream) + } + } + + @Test + fun restoreEntity_exception() { + val storage = TestStorage(entity1) + temporaryFolder.newFile().toParcelFileDescriptor(MODE_READ_ONLY).use { + val backupDataInputStream = it.toBackupDataInputStream() + backupDataInputStream.setKey(entity1.key) + storage.restoreEntity(backupDataInputStream) + } + } + + @Test + fun restoreEntity_codecChanged() { + assertThat(entity1.codec()).isNotEqualTo(entity1NoOpCodec.codec()) + backupAndRestore(entity1) { _, data -> + TestStorage(entity1NoOpCodec).apply { restoreEntity(data) } + } + assertThat(entity1.data).isEqualTo(entity1NoOpCodec.restoredData) + } + + @Test + fun restoreEntity() { + val random = Random.Default + fun test(codec: BackupCodec, size: Int) { + val entity = Entity("key", random.nextBytes(size), codec) + backupAndRestore(entity) + entity.verifyRestoredData() + } + for (codec in allCodecs()) { + // test small data to ensure correctness + for (size in 0 until 100) test(codec, size) + repeat(10) { test(codec, random.nextInt(100, MAX_DATA_SIZE)) } + } + } + + @Test + fun readEntityStates_eof_exception() { + val storage = TestStorage() + val entityStates = MutableScatterMap<String, Long>() + entityStates.put("", 0) // add an item to verify that exiting elements are clear + temporaryFolder.newFile().toParcelFileDescriptor(MODE_READ_ONLY).use { + storage.readEntityStates(it, entityStates) + } + assertThat(entityStates.size).isEqualTo(0) + } + + @Test + fun readEntityStates_other_exception() { + val storage = TestStorage() + val entityStates = MutableScatterMap<String, Long>() + entityStates.put("", 0) // add an item to verify that exiting elements are clear + temporaryFolder.newFile().toParcelFileDescriptor(MODE_READ_ONLY).apply { + close() // cause exception when read state file + storage.readEntityStates(this, entityStates) + } + assertThat(entityStates.size).isEqualTo(0) + } + + @Test + fun readEntityStates_unknownVersion() { + val storage = TestStorage() + val stateFile = temporaryFolder.newFile() + stateFile.toParcelFileDescriptor(MODE_WRITE_ONLY or MODE_APPEND).use { + DataOutputStream(FileOutputStream(it.fileDescriptor)) + .writeByte(BackupRestoreStorage.STATE_VERSION + 1) + } + val entityStates = MutableScatterMap<String, Long>() + entityStates.put("", 0) // add an item to verify that exiting elements are clear + stateFile.toParcelFileDescriptor(MODE_READ_ONLY).use { + storage.readEntityStates(it, entityStates) + } + assertThat(entityStates.size).isEqualTo(0) + } + + @Test + fun writeNewStateDescription() { + val storage = spy(TestStorage()) + // use read only mode to trigger exception when write state file + temporaryFolder.newFile().toParcelFileDescriptor(MODE_READ_ONLY).use { + storage.writeNewStateDescription(it) + } + verify(storage).onRestoreFinished() + } + + @Test + fun backupAndRestore() { + val storage = spy(TestStorage(entity1, entity2)) + val backupAgentHelper = BackupAgentHelper() + backupAgentHelper.addHelper(storage.name, storage) + + // backup + val (dataFile, stateFile) = + performBackup { data, newState -> backupAgentHelper.onBackup(null, data, newState) } + storage.verifyFieldsArePurged() + + // verify state + val entityStates = MutableScatterMap<String, Long>() + entityStates[""] = 1 + storage.readEntityStates(null, entityStates) + assertThat(entityStates.size).isEqualTo(0) + stateFile.toParcelFileDescriptor(MODE_READ_ONLY).use { + storage.readEntityStates(it, entityStates) + } + assertThat(entityStates.asMap()).apply { + hasSize(2) + containsKey(entity1.key) + containsKey(entity2.key) + } + reset(storage) + + // restore + val newStateFile = temporaryFolder.newFile() + dataFile.toParcelFileDescriptor(MODE_READ_ONLY).use { dataPfd -> + newStateFile.toParcelFileDescriptor(MODE_WRITE_ONLY or MODE_APPEND).use { + backupAgentHelper.onRestore(dataPfd.toBackupDataInput(), 0, it) + } + } + verify(storage).onRestoreFinished() + storage.verifyFieldsArePurged() + + // ShadowBackupDataOutput does not write data to file, so restore is bypassed + if (!isRobolectric()) { + entity1.verifyRestoredData() + entity2.verifyRestoredData() + assertThat(entityStates.asMap()).isEqualTo(storage.readEntityStates(newStateFile)) + } + } + + private fun backupAndRestore( + entity: BackupRestoreEntity, + restoreEntity: (TestStorage, BackupDataInputStream) -> TestStorage = { storage, data -> + storage.restoreEntity(data) + storage + }, + ) { + val storage = TestStorage(entity) + val entityKey = argumentCaptor<String>() + val entitySize = argumentCaptor<Int>() + val entityData = argumentCaptor<ByteArray>() + val data = mock<BackupDataOutput>() + + val stateFile = temporaryFolder.newFile() + stateFile.toParcelFileDescriptor(MODE_WRITE_ONLY or MODE_APPEND).use { + storage.performBackup(null, data, it) + } + val entityStates = MutableScatterMap<String, Long>() + stateFile.toParcelFileDescriptor(MODE_READ_ONLY).use { + storage.readEntityStates(it, entityStates) + } + assertThat(entityStates.size).isEqualTo(1) + + verify(data).writeEntityHeader(entityKey.capture(), entitySize.capture()) + verify(data).writeEntityData(entityData.capture(), entitySize.capture()) + assertThat(entityKey.allValues).isEqualTo(listOf(entity.key)) + assertThat(entityData.allValues).hasSize(1) + val payload = entityData.firstValue + assertThat(entitySize.allValues).isEqualTo(listOf(payload.size, payload.size)) + + val dataFile = temporaryFolder.newFile() + dataFile.toParcelFileDescriptor(MODE_WRITE_ONLY or MODE_APPEND).use { + FileOutputStream(it.fileDescriptor).write(payload) + } + + newBackupDataInputStream(entity.key, payload).apply { + restoreEntity.invoke(storage, this).also { + assertThat(it.entityStates).isEqualTo(entityStates) + } + } + } + + fun performBackup(backup: (BackupDataOutput, ParcelFileDescriptor) -> Unit): Pair<File, File> { + val dataFile = temporaryFolder.newFile() + val stateFile = temporaryFolder.newFile() + dataFile.toParcelFileDescriptor(MODE_WRITE_ONLY or MODE_APPEND).use { dataPfd -> + stateFile.toParcelFileDescriptor(MODE_WRITE_ONLY or MODE_APPEND).use { + backup.invoke(dataPfd.toBackupDataOutput(), it) + } + } + return dataFile to stateFile + } + + private fun BackupRestoreStorage.verifyFieldsArePurged() { + assertThat(entities).isNull() + assertThat(entityStates.size).isEqualTo(0) + assertThat(entityStates.capacity).isEqualTo(0) + } + + private fun BackupRestoreStorage.readEntityStates(stateFile: File): Map<String, Long> { + val entityStates = MutableScatterMap<String, Long>() + stateFile.toParcelFileDescriptor(MODE_READ_ONLY).use { readEntityStates(it, entityStates) } + return entityStates.asMap() + } + + private fun File.toParcelFileDescriptor(mode: Int) = ParcelFileDescriptor.open(this, mode) + + private fun ParcelFileDescriptor.toBackupDataOutput() = fileDescriptor.toBackupDataOutput() + + private fun ParcelFileDescriptor.toBackupDataInputStream(): BackupDataInputStream = + BackupDataInputStream::class.java.newInstance(toBackupDataInput()) + + private fun ParcelFileDescriptor.toBackupDataInput() = fileDescriptor.toBackupDataInput() + + private fun FileDescriptor.toBackupDataOutput(): BackupDataOutput = + BackupDataOutput::class.java.newInstance(this) + + private fun FileDescriptor.toBackupDataInput(): BackupDataInput = + BackupDataInput::class.java.newInstance(this) +} + +private open class TestStorage(vararg val backupRestoreEntities: BackupRestoreEntity) : + ObservableBackupRestoreStorage() { + var enabled: Boolean? = null + + override val name + get() = "TestBackup" + + override fun createBackupRestoreEntities() = backupRestoreEntities.toList() + + override fun enableBackup(backupContext: BackupContext) = + enabled ?: super.enableBackup(backupContext) + + override fun enableRestore() = enabled ?: super.enableRestore() +} + +private class Entity( + override val key: String, + val data: ByteArray, + private val codec: BackupCodec? = null, +) : BackupRestoreEntity { + var restoredData: ByteArray? = null + var backupResult = EntityBackupResult.UPDATE + + override fun codec() = codec ?: super.codec() + + override fun backup( + backupContext: BackupContext, + outputStream: OutputStream, + ): EntityBackupResult { + outputStream.write(data) + return backupResult + } + + override fun restore(restoreContext: RestoreContext, inputStream: InputStream) { + restoredData = inputStream.readBytes() + inputStream.close() + } + + fun verifyRestoredData() = assertThat(restoredData).isEqualTo(data) +} diff --git a/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/KeyedObserverTest.kt b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/KeyedObserverTest.kt index b52586c2d8d9..8638b2f20b52 100644 --- a/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/KeyedObserverTest.kt +++ b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/KeyedObserverTest.kt @@ -16,76 +16,58 @@ package com.android.settingslib.datastore +import androidx.test.ext.junit.runners.AndroidJUnit4 import com.google.common.truth.Truth.assertThat -import com.google.common.util.concurrent.MoreExecutors.directExecutor +import com.google.common.util.concurrent.MoreExecutors import java.util.concurrent.Executor import java.util.concurrent.atomic.AtomicInteger import org.junit.Assert -import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith -import org.mockito.Mock -import org.mockito.junit.MockitoJUnit -import org.mockito.junit.MockitoRule -import org.mockito.kotlin.any +import org.mockito.kotlin.mock import org.mockito.kotlin.never import org.mockito.kotlin.reset import org.mockito.kotlin.verify -import org.robolectric.RobolectricTestRunner -@RunWith(RobolectricTestRunner::class) +@RunWith(AndroidJUnit4::class) class KeyedObserverTest { - @get:Rule - val mockitoRule: MockitoRule = MockitoJUnit.rule() + private val observer1 = mock<KeyedObserver<Any?>>() + private val observer2 = mock<KeyedObserver<Any?>>() + private val keyedObserver1 = mock<KeyedObserver<Any>>() + private val keyedObserver2 = mock<KeyedObserver<Any>>() - @Mock - private lateinit var observer1: KeyedObserver<Any?> - - @Mock - private lateinit var observer2: KeyedObserver<Any?> - - @Mock - private lateinit var keyedObserver1: KeyedObserver<Any> - - @Mock - private lateinit var keyedObserver2: KeyedObserver<Any> - - @Mock - private lateinit var key1: Any - - @Mock - private lateinit var key2: Any - - @Mock - private lateinit var executor: Executor + private val key1 = Object() + private val key2 = Object() + private val executor1: Executor = MoreExecutors.directExecutor() + private val executor2: Executor = MoreExecutors.newDirectExecutorService() private val keyedObservable = KeyedDataObservable<Any>() @Test fun addObserver_sameExecutor() { - keyedObservable.addObserver(observer1, executor) - keyedObservable.addObserver(observer1, executor) + keyedObservable.addObserver(observer1, executor1) + keyedObservable.addObserver(observer1, executor1) } @Test fun addObserver_keyedObserver_sameExecutor() { - keyedObservable.addObserver(key1, keyedObserver1, executor) - keyedObservable.addObserver(key1, keyedObserver1, executor) + keyedObservable.addObserver(key1, keyedObserver1, executor1) + keyedObservable.addObserver(key1, keyedObserver1, executor1) } @Test fun addObserver_differentExecutor() { - keyedObservable.addObserver(observer1, executor) + keyedObservable.addObserver(observer1, executor1) Assert.assertThrows(IllegalStateException::class.java) { - keyedObservable.addObserver(observer1, directExecutor()) + keyedObservable.addObserver(observer1, executor2) } } @Test fun addObserver_keyedObserver_differentExecutor() { - keyedObservable.addObserver(key1, keyedObserver1, executor) + keyedObservable.addObserver(key1, keyedObserver1, executor1) Assert.assertThrows(IllegalStateException::class.java) { - keyedObservable.addObserver(key1, keyedObserver1, directExecutor()) + keyedObservable.addObserver(key1, keyedObserver1, executor2) } } @@ -93,7 +75,7 @@ class KeyedObserverTest { fun addObserver_weaklyReferenced() { val counter = AtomicInteger() var observer: KeyedObserver<Any?>? = KeyedObserver { _, _ -> counter.incrementAndGet() } - keyedObservable.addObserver(observer!!, directExecutor()) + keyedObservable.addObserver(observer!!, executor1) keyedObservable.notifyChange(ChangeReason.UPDATE) assertThat(counter.get()).isEqualTo(1) @@ -111,7 +93,7 @@ class KeyedObserverTest { fun addObserver_keyedObserver_weaklyReferenced() { val counter = AtomicInteger() var keyObserver: KeyedObserver<Any>? = KeyedObserver { _, _ -> counter.incrementAndGet() } - keyedObservable.addObserver(key1, keyObserver!!, directExecutor()) + keyedObservable.addObserver(key1, keyObserver!!, executor1) keyedObservable.notifyChange(key1, ChangeReason.UPDATE) assertThat(counter.get()).isEqualTo(1) @@ -127,45 +109,43 @@ class KeyedObserverTest { @Test fun addObserver_notifyObservers_removeObserver() { - keyedObservable.addObserver(observer1, directExecutor()) - keyedObservable.addObserver(observer2, executor) + keyedObservable.addObserver(observer1, executor1) + keyedObservable.addObserver(observer2, executor2) keyedObservable.notifyChange(ChangeReason.UPDATE) verify(observer1).onKeyChanged(null, ChangeReason.UPDATE) - verify(observer2, never()).onKeyChanged(any(), any()) - verify(executor).execute(any()) + verify(observer2).onKeyChanged(null, ChangeReason.UPDATE) - reset(observer1, executor) + reset(observer1, observer2) keyedObservable.removeObserver(observer2) keyedObservable.notifyChange(ChangeReason.DELETE) verify(observer1).onKeyChanged(null, ChangeReason.DELETE) - verify(executor, never()).execute(any()) + verify(observer2, never()).onKeyChanged(null, ChangeReason.DELETE) } @Test fun addObserver_keyedObserver_notifyObservers_removeObserver() { - keyedObservable.addObserver(key1, keyedObserver1, directExecutor()) - keyedObservable.addObserver(key2, keyedObserver2, executor) + keyedObservable.addObserver(key1, keyedObserver1, executor1) + keyedObservable.addObserver(key2, keyedObserver2, executor2) keyedObservable.notifyChange(key1, ChangeReason.UPDATE) verify(keyedObserver1).onKeyChanged(key1, ChangeReason.UPDATE) - verify(keyedObserver2, never()).onKeyChanged(any(), any()) - verify(executor, never()).execute(any()) + verify(keyedObserver2, never()).onKeyChanged(key2, ChangeReason.UPDATE) - reset(keyedObserver1, executor) - keyedObservable.removeObserver(key2, keyedObserver2) + reset(keyedObserver1, keyedObserver2) + keyedObservable.removeObserver(key1, keyedObserver1) keyedObservable.notifyChange(key1, ChangeReason.DELETE) - verify(keyedObserver1).onKeyChanged(key1, ChangeReason.DELETE) - verify(executor, never()).execute(any()) + verify(keyedObserver1, never()).onKeyChanged(key1, ChangeReason.DELETE) + verify(keyedObserver2, never()).onKeyChanged(key2, ChangeReason.DELETE) } @Test fun notifyChange_addMoreTypeObservers_checkOnKeyChanged() { - keyedObservable.addObserver(observer1, directExecutor()) - keyedObservable.addObserver(key1, keyedObserver1, directExecutor()) - keyedObservable.addObserver(key2, keyedObserver2, directExecutor()) + keyedObservable.addObserver(observer1, executor1) + keyedObservable.addObserver(key1, keyedObserver1, executor1) + keyedObservable.addObserver(key2, keyedObserver2, executor1) keyedObservable.notifyChange(ChangeReason.UPDATE) verify(observer1).onKeyChanged(null, ChangeReason.UPDATE) @@ -191,10 +171,10 @@ class KeyedObserverTest { fun notifyChange_addObserverWithinCallback() { // ConcurrentModificationException is raised if it is not implemented correctly val observer: KeyedObserver<Any?> = KeyedObserver { _, _ -> - keyedObservable.addObserver(observer1, executor) + keyedObservable.addObserver(observer1, executor1) } - keyedObservable.addObserver(observer, directExecutor()) + keyedObservable.addObserver(observer, executor1) keyedObservable.notifyChange(ChangeReason.UPDATE) keyedObservable.removeObserver(observer) @@ -204,12 +184,12 @@ class KeyedObserverTest { fun notifyChange_KeyedObserver_addObserverWithinCallback() { // ConcurrentModificationException is raised if it is not implemented correctly val keyObserver: KeyedObserver<Any?> = KeyedObserver { _, _ -> - keyedObservable.addObserver(key1, keyedObserver1, executor) + keyedObservable.addObserver(key1, keyedObserver1, executor1) } - keyedObservable.addObserver(key1, keyObserver, directExecutor()) + keyedObservable.addObserver(key1, keyObserver, executor1) keyedObservable.notifyChange(key1, ChangeReason.UPDATE) keyedObservable.removeObserver(key1, keyObserver) } -}
\ No newline at end of file +} diff --git a/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/ObserverTest.kt b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/ObserverTest.kt index f0658290beb0..173c2b1d4b81 100644 --- a/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/ObserverTest.kt +++ b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/ObserverTest.kt @@ -22,40 +22,33 @@ import com.google.common.util.concurrent.MoreExecutors import java.util.concurrent.Executor import java.util.concurrent.atomic.AtomicInteger import org.junit.Assert.assertThrows -import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith -import org.mockito.Mock -import org.mockito.junit.MockitoJUnit -import org.mockito.junit.MockitoRule -import org.mockito.kotlin.any +import org.mockito.kotlin.mock import org.mockito.kotlin.never import org.mockito.kotlin.reset import org.mockito.kotlin.verify @RunWith(AndroidJUnit4::class) class ObserverTest { - @get:Rule val mockitoRule: MockitoRule = MockitoJUnit.rule() - - @Mock private lateinit var observer1: Observer - - @Mock private lateinit var observer2: Observer - - @Mock private lateinit var executor: Executor + private val observer1 = mock<Observer>() + private val observer2 = mock<Observer>() + private val executor1: Executor = MoreExecutors.directExecutor() + private val executor2: Executor = MoreExecutors.newDirectExecutorService() private val observable = DataObservable() @Test fun addObserver_sameExecutor() { - observable.addObserver(observer1, executor) - observable.addObserver(observer1, executor) + observable.addObserver(observer1, executor1) + observable.addObserver(observer1, executor1) } @Test fun addObserver_differentExecutor() { - observable.addObserver(observer1, executor) + observable.addObserver(observer1, executor1) assertThrows(IllegalStateException::class.java) { - observable.addObserver(observer1, MoreExecutors.directExecutor()) + observable.addObserver(observer1, executor2) } } @@ -63,7 +56,7 @@ class ObserverTest { fun addObserver_weaklyReferenced() { val counter = AtomicInteger() var observer: Observer? = Observer { counter.incrementAndGet() } - observable.addObserver(observer!!, MoreExecutors.directExecutor()) + observable.addObserver(observer!!, executor1) observable.notifyChange(ChangeReason.UPDATE) assertThat(counter.get()).isEqualTo(1) @@ -79,31 +72,27 @@ class ObserverTest { @Test fun addObserver_notifyObservers_removeObserver() { - observable.addObserver(observer1, MoreExecutors.directExecutor()) - observable.addObserver(observer2, executor) + observable.addObserver(observer1, executor1) + observable.addObserver(observer2, executor2) observable.notifyChange(ChangeReason.DELETE) verify(observer1).onChanged(ChangeReason.DELETE) - verify(observer2, never()).onChanged(any()) - verify(executor).execute(any()) + verify(observer2).onChanged(ChangeReason.DELETE) - reset(observer1, executor) + reset(observer1, observer2) observable.removeObserver(observer2) observable.notifyChange(ChangeReason.UPDATE) verify(observer1).onChanged(ChangeReason.UPDATE) - verify(executor, never()).execute(any()) + verify(observer2, never()).onChanged(ChangeReason.UPDATE) } @Test fun notifyChange_addObserverWithinCallback() { // ConcurrentModificationException is raised if it is not implemented correctly - val observer = Observer { observable.addObserver(observer1, executor) } - observable.addObserver( - observer, - MoreExecutors.directExecutor() - ) + val observer = Observer { observable.addObserver(observer1, executor1) } + observable.addObserver(observer, executor1) observable.notifyChange(ChangeReason.UPDATE) observable.removeObserver(observer) } diff --git a/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/SharedPreferencesStorageTest.kt b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/SharedPreferencesStorageTest.kt new file mode 100644 index 000000000000..fec7d758b893 --- /dev/null +++ b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/SharedPreferencesStorageTest.kt @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib.datastore + +import android.app.Application +import android.content.Context +import android.content.SharedPreferences +import android.os.Build +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.platform.app.InstrumentationRegistry +import com.google.common.truth.Truth.assertThat +import com.google.common.util.concurrent.MoreExecutors +import java.io.ByteArrayOutputStream +import java.io.File +import java.util.concurrent.Executor +import kotlin.random.Random +import org.junit.After +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.kotlin.any +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.mock +import org.mockito.kotlin.spy +import org.mockito.kotlin.verify + +/** Tests of [SharedPreferencesStorage]. */ +@RunWith(AndroidJUnit4::class) +class SharedPreferencesStorageTest { + private val random = Random.Default + private val application: Application = ApplicationProvider.getApplicationContext() + private val map = + mapOf( + "boolean" to true, + "float" to random.nextFloat(), + "int" to random.nextInt(), + "long" to random.nextLong(), + "string" to "string", + "set" to setOf("string"), + ) + + @After + fun tearDown() { + application.getSharedPreferences(NAME, MODE).edit().clear().applySync() + } + + @Test + fun constructors() { + val storage1 = SharedPreferencesStorage(application, NAME, MODE) + val storage2 = + SharedPreferencesStorage( + application, + NAME, + application.getSharedPreferences(NAME, MODE), + ) + assertThat(storage1.sharedPreferences).isSameInstanceAs(storage2.sharedPreferences) + } + + @Test + fun observer() { + val observer = mock<KeyedObserver<Any?>>() + val keyedObserver = mock<KeyedObserver<Any>>() + val storage = SharedPreferencesStorage(application, NAME, MODE) + val executor: Executor = MoreExecutors.directExecutor() + storage.addObserver(observer, executor) + storage.addObserver("key", keyedObserver, executor) + + storage.sharedPreferences.edit().putString("key", "string").applySync() + verify(observer).onKeyChanged("key", ChangeReason.UPDATE) + verify(keyedObserver).onKeyChanged("key", ChangeReason.UPDATE) + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + storage.sharedPreferences.edit().clear().applySync() + verify(observer).onKeyChanged(null, ChangeReason.DELETE) + verify(keyedObserver).onKeyChanged("key", ChangeReason.DELETE) + } + } + + @Test + fun prepareBackup_commitFailed() { + val editor = mock<SharedPreferences.Editor> { on { commit() } doReturn false } + val storage = + spy(SharedPreferencesStorage(application, NAME, MODE)) { + onGeneric { mergeSharedPreferences(any(), any(), any()) } doReturn editor + } + storage.prepareBackup(File("")) + } + + @Test + fun backupAndRestore() { + fun test(codec: BackupCodec) { + val storage = SharedPreferencesStorage(application, NAME, MODE, codec) + storage.mergeSharedPreferences(storage.sharedPreferences, map, "op").commit() + assertThat(storage.sharedPreferences.all).isEqualTo(map) + + val outputStream = ByteArrayOutputStream() + assertThat(storage.toBackupRestoreEntity().backup(BackupContext(mock()), outputStream)) + .isEqualTo(EntityBackupResult.UPDATE) + val payload = outputStream.toByteArray() + + storage.sharedPreferences.edit().clear().commit() + assertThat(storage.sharedPreferences.all).isEmpty() + + BackupRestoreFileArchiver(application, listOf(storage), "archiver") + .restoreEntity(newBackupDataInputStream(storage.storageFilePath, payload)) + assertThat(storage.sharedPreferences.all).isEqualTo(map) + } + + for (codec in allCodecs()) test(codec) + } + + @Test + fun mergeSharedPreferences_filter() { + val storage = + SharedPreferencesStorage(application, NAME, MODE) { key, value -> + key == "float" || value is String + } + storage.mergeSharedPreferences(storage.sharedPreferences, map, "op").apply() + assertThat(storage.sharedPreferences.all) + .containsExactly("float", map["float"], "string", map["string"]) + } + + @Test + fun mergeSharedPreferences_invalidSet() { + val storage = SharedPreferencesStorage(application, NAME, MODE, verbose = true) + storage + .mergeSharedPreferences( + storage.sharedPreferences, + mapOf<String, Any>("set" to setOf(Any())), + "op" + ) + .apply() + assertThat(storage.sharedPreferences.all).isEmpty() + } + + @Test + fun mergeSharedPreferences_unknownType() { + val storage = SharedPreferencesStorage(application, NAME, MODE) + storage + .mergeSharedPreferences(storage.sharedPreferences, map + ("key" to Any()), "op") + .apply() + assertThat(storage.sharedPreferences.all).isEqualTo(map) + } + + @Test + fun mergeSharedPreferences() { + val storage = SharedPreferencesStorage(application, NAME, MODE, verbose = true) + storage.mergeSharedPreferences(storage.sharedPreferences, map, "op").apply() + assertThat(storage.sharedPreferences.all).isEqualTo(map) + } + + private fun SharedPreferences.Editor.applySync() { + apply() + InstrumentationRegistry.getInstrumentation().waitForIdleSync() + } + + companion object { + private const val NAME = "pref" + private const val MODE = Context.MODE_PRIVATE + } +} diff --git a/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/TestUtils.kt b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/TestUtils.kt new file mode 100644 index 000000000000..823d222b2305 --- /dev/null +++ b/packages/SettingsLib/DataStore/tests/src/com/android/settingslib/datastore/TestUtils.kt @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settingslib.datastore + +import android.app.backup.BackupDataInput +import android.app.backup.BackupDataInputStream +import android.os.Build +import java.io.ByteArrayInputStream +import org.mockito.kotlin.any +import org.mockito.kotlin.doAnswer +import org.mockito.kotlin.mock + +internal const val MAX_DATA_SIZE = 1 shl 12 + +internal fun allCodecs() = + arrayOf<BackupCodec>( + BackupNoOpCodec(), + ) + zipCodecs() + +internal fun zipCodecs() = + arrayOf<BackupCodec>( + BackupZipCodec.DEFAULT_COMPRESSION, + BackupZipCodec.BEST_COMPRESSION, + BackupZipCodec.BEST_SPEED, + ) + +internal fun <T : Any> Class<T>.newInstance(arg: Any, type: Class<*> = arg.javaClass): T = + getDeclaredConstructor(type).apply { isAccessible = true }.newInstance(arg) + +internal fun newBackupDataInputStream( + key: String, + data: ByteArray, + e: Exception? = null, +): BackupDataInputStream { + // ShadowBackupDataOutput does not write data to file, so mock for reading data + val inputStream = ByteArrayInputStream(data) + val backupDataInput = + mock<BackupDataInput> { + on { readEntityData(any(), any(), any()) } doAnswer + { + if (e != null) throw e + val buf = it.arguments[0] as ByteArray + val offset = it.arguments[1] as Int + val size = it.arguments[2] as Int + inputStream.read(buf, offset, size) + } + } + return BackupDataInputStream::class + .java + .newInstance(backupDataInput, BackupDataInput::class.java) + .apply { + setKey(key) + setDataSize(data.size) + } +} + +internal fun BackupDataInputStream.setKey(value: Any) { + val field = javaClass.getDeclaredField("key") + field.isAccessible = true + field.set(this, value) +} + +internal fun BackupDataInputStream.setDataSize(dataSize: Int) { + val field = javaClass.getDeclaredField("dataSize") + field.isAccessible = true + field.setInt(this, dataSize) +} + +internal fun isRobolectric() = Build.FINGERPRINT.contains("robolectric") diff --git a/packages/SettingsLib/aconfig/OWNERS b/packages/SettingsLib/aconfig/OWNERS new file mode 100644 index 000000000000..ba02d20b1ae4 --- /dev/null +++ b/packages/SettingsLib/aconfig/OWNERS @@ -0,0 +1,2 @@ +# go/android-fwk-media-solutions for info on areas of ownership. +per-file settingslib_media_flag_declarations.aconfig = file:platform/frameworks/av:/media/janitors/media_solutions_OWNERS diff --git a/packages/SettingsLib/aconfig/settingslib_media_flag_declarations.aconfig b/packages/SettingsLib/aconfig/settingslib_media_flag_declarations.aconfig index 4d70aec9fa5c..7aae1a6b4a59 100644 --- a/packages/SettingsLib/aconfig/settingslib_media_flag_declarations.aconfig +++ b/packages/SettingsLib/aconfig/settingslib_media_flag_declarations.aconfig @@ -21,3 +21,13 @@ flag { description: "Enable Output Switcher when no media is playing." bug: "284227163" } + +flag { + name: "remove_unnecessary_route_scanning" + namespace: "media_solutions" + description: "Avoid active scan requests on UI components that only display route status information." + bug: "332515672" + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java index 3e2987208444..eae58adb5381 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java @@ -150,7 +150,16 @@ public abstract class InfoMediaManager { mUserHandle = userHandle; } - /** Creates an instance of InfoMediaManager. */ + /** + * Creates an instance of InfoMediaManager. + * + * @param context The {@link Context}. + * @param packageName The package name of the app for which to control routing, or null if the + * caller is interested in system-level routing only (for example, headsets, built-in + * speakers, as opposed to app-specific routing (for example, casting to another device). + * @param userHandle The {@link UserHandle} of the user on which the app to control is running, + * or null if the caller does not need app-specific routing (see {@code packageName}). + */ public static InfoMediaManager createInstance( Context context, @Nullable String packageName, @@ -185,11 +194,7 @@ public abstract class InfoMediaManager { } public void startScan() { - mMediaDevices.clear(); - registerRouter(); startScanOnRouter(); - updateRouteListingPreference(); - refreshDevices(); } private void updateRouteListingPreference() { @@ -203,7 +208,6 @@ public abstract class InfoMediaManager { public final void stopScan() { stopScanOnRouter(); - unregisterRouter(); } protected abstract void stopScanOnRouter(); @@ -290,14 +294,37 @@ public abstract class InfoMediaManager { return null; } - protected final void registerCallback(MediaDeviceCallback callback) { + /** + * Registers the specified {@code callback} to receive state updates about routing information. + * + * <p>As long as there is a registered {@link MediaDeviceCallback}, {@link InfoMediaManager} + * will receive state updates from the platform. + * + * <p>Call {@link #unregisterCallback(MediaDeviceCallback)} once you no longer need platform + * updates. + */ + public final void registerCallback(@NonNull MediaDeviceCallback callback) { + boolean wasEmpty = mCallbacks.isEmpty(); if (!mCallbacks.contains(callback)) { mCallbacks.add(callback); + if (wasEmpty) { + mMediaDevices.clear(); + registerRouter(); + updateRouteListingPreference(); + refreshDevices(); + } } } - protected final void unregisterCallback(MediaDeviceCallback callback) { - mCallbacks.remove(callback); + /** + * Unregisters the specified {@code callback}. + * + * @see #registerCallback(MediaDeviceCallback) + */ + public final void unregisterCallback(@NonNull MediaDeviceCallback callback) { + if (mCallbacks.remove(callback) && mCallbacks.isEmpty()) { + unregisterRouter(); + } } private void dispatchDeviceListAdded(@NonNull List<MediaDevice> devices) { diff --git a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java index 0c2414c6a7a8..a08a3dc4f7cc 100644 --- a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java +++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java @@ -17,7 +17,6 @@ package com.android.settingslib.media; import static android.media.MediaRoute2ProviderService.REASON_UNKNOWN_ERROR; -import android.app.Notification; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.content.ComponentName; @@ -106,14 +105,23 @@ public class LocalMediaManager implements BluetoothCallback { * Register to start receiving callbacks for MediaDevice events. */ public void registerCallback(DeviceCallback callback) { - mCallbacks.add(callback); + boolean wasEmpty = mCallbacks.isEmpty(); + if (!mCallbacks.contains(callback)) { + mCallbacks.add(callback); + if (wasEmpty) { + mInfoMediaManager.registerCallback(mMediaDeviceCallback); + } + } } /** * Unregister to stop receiving callbacks for MediaDevice events */ public void unregisterCallback(DeviceCallback callback) { - mCallbacks.remove(callback); + if (mCallbacks.remove(callback) && mCallbacks.isEmpty()) { + mInfoMediaManager.unregisterCallback(mMediaDeviceCallback); + unRegisterDeviceAttributeChangeCallback(); + } } /** @@ -125,7 +133,7 @@ public class LocalMediaManager implements BluetoothCallback { * * It will use {@link BluetoothAdapter#getDefaultAdapter()] for setting the bluetooth adapter. */ - public LocalMediaManager(Context context, String packageName, Notification notification) { + public LocalMediaManager(Context context, String packageName) { mContext = context; mPackageName = packageName; mLocalBluetoothManager = @@ -228,10 +236,6 @@ public class LocalMediaManager implements BluetoothCallback { * Start scan connected MediaDevice */ public void startScan() { - synchronized (mMediaDevicesLock) { - mMediaDevices.clear(); - } - mInfoMediaManager.registerCallback(mMediaDeviceCallback); mInfoMediaManager.startScan(); } @@ -281,9 +285,7 @@ public class LocalMediaManager implements BluetoothCallback { * Stop scan MediaDevice */ public void stopScan() { - mInfoMediaManager.unregisterCallback(mMediaDeviceCallback); mInfoMediaManager.stopScan(); - unRegisterDeviceAttributeChangeCallback(); } /** diff --git a/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/LocalMediaRepository.kt b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/LocalMediaRepository.kt index 724dd51b8fe4..869fb7f4043c 100644 --- a/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/LocalMediaRepository.kt +++ b/packages/SettingsLib/src/com/android/settingslib/volume/data/repository/LocalMediaRepository.kt @@ -17,6 +17,7 @@ package com.android.settingslib.volume.data.repository import com.android.settingslib.media.LocalMediaManager import com.android.settingslib.media.MediaDevice +import com.android.settingslib.media.flags.Flags import com.android.settingslib.volume.shared.AudioManagerEventsReceiver import com.android.settingslib.volume.shared.model.AudioManagerEvent import kotlinx.coroutines.CoroutineScope @@ -69,10 +70,14 @@ class LocalMediaRepositoryImpl( } } localMediaManager.registerCallback(callback) - localMediaManager.startScan() + if (!Flags.removeUnnecessaryRouteScanning()) { + localMediaManager.startScan() + } awaitClose { - localMediaManager.stopScan() + if (!Flags.removeUnnecessaryRouteScanning()) { + localMediaManager.stopScan() + } localMediaManager.unregisterCallback(callback) } } diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java index a4b87da9f7f6..69faddf48f19 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java @@ -35,6 +35,7 @@ import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -751,35 +752,31 @@ public class InfoMediaManagerTest { @Test public void onTransferred_getAvailableRoutes_shouldAddMediaDevice() { - final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>(); - final RoutingSessionInfo sessionInfo = mock(RoutingSessionInfo.class); - routingSessionInfos.add(sessionInfo); - final List<String> selectedRoutes = new ArrayList<>(); - selectedRoutes.add(TEST_ID); - when(sessionInfo.getSelectedRoutes()).thenReturn(selectedRoutes); - mShadowRouter2Manager.setRoutingSessions(routingSessionInfos); + mInfoMediaManager.mRouterManager = mRouterManager; + // Since test is running in Robolectric, return a fake session to avoid NPE. + when(mRouterManager.getRoutingSessions(anyString())) + .thenReturn(List.of(TEST_SYSTEM_ROUTING_SESSION)); + when(mRouterManager.getSelectedRoutes(any())) + .thenReturn(List.of(TEST_SELECTED_SYSTEM_ROUTE)); - final MediaRoute2Info info = mock(MediaRoute2Info.class); mInfoMediaManager.registerCallback(mCallback); - when(info.getDeduplicationIds()).thenReturn(Set.of()); - when(info.getId()).thenReturn(TEST_ID); - when(info.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME); - - final List<MediaRoute2Info> routes = new ArrayList<>(); - routes.add(info); - mShadowRouter2Manager.setTransferableRoutes(routes); + MediaDevice mediaDevice = mInfoMediaManager.getCurrentConnectedDevice(); + assertThat(mediaDevice).isNotNull(); + assertThat(mediaDevice.getId()).isEqualTo(TEST_SYSTEM_ROUTE_ID); - final MediaDevice mediaDevice = mInfoMediaManager.findMediaDevice(TEST_ID); - assertThat(mediaDevice).isNull(); + when(mRouterManager.getRoutingSessions(anyString())) + .thenReturn(List.of(TEST_SYSTEM_ROUTING_SESSION, TEST_REMOTE_ROUTING_SESSION)); + when(mRouterManager.getSelectedRoutes(any())).thenReturn(List.of(TEST_REMOTE_ROUTE)); - mInfoMediaManager.mMediaRouterCallback.onTransferred(sessionInfo, sessionInfo); + mInfoMediaManager.mMediaRouterCallback.onTransferred( + TEST_SYSTEM_ROUTING_SESSION, TEST_REMOTE_ROUTING_SESSION); final MediaDevice infoDevice = mInfoMediaManager.mMediaDevices.get(0); - assertThat(infoDevice.getId()).isEqualTo(TEST_ID); + assertThat(infoDevice).isNotNull(); + assertThat(infoDevice.getId()).isEqualTo(TEST_REMOTE_ROUTE.getId()); assertThat(mInfoMediaManager.getCurrentConnectedDevice()).isEqualTo(infoDevice); - assertThat(mInfoMediaManager.mMediaDevices).hasSize(routes.size()); - verify(mCallback).onConnectedDeviceChanged(TEST_ID); + verify(mCallback).onConnectedDeviceChanged(TEST_REMOTE_ROUTE.getId()); } @Test @@ -795,7 +792,8 @@ public class InfoMediaManagerTest { mInfoMediaManager.mMediaRouterCallback.onSessionUpdated(TEST_SYSTEM_ROUTING_SESSION); - verify(mCallback).onDeviceListAdded(any()); + // Expecting 1st call after registerCallback() and 2nd call after onSessionUpdated(). + verify(mCallback, times(2)).onDeviceListAdded(any()); } @Test diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java index 693b7d0faa79..ddb5419509d7 100644 --- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java +++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/LocalMediaManagerTest.java @@ -21,6 +21,8 @@ import static android.media.MediaRoute2ProviderService.REASON_UNKNOWN_ERROR; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.clearInvocations; +import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.spy; @@ -58,6 +60,7 @@ import org.robolectric.annotation.Config; import org.robolectric.shadow.api.Shadow; import java.util.ArrayList; +import java.util.Collections; import java.util.List; @RunWith(RobolectricTestRunner.class) @@ -117,6 +120,13 @@ public class LocalMediaManagerTest { mInfoMediaManager = mock( InfoMediaManager.class, withSettings().useConstructor(mContext, TEST_PACKAGE_NAME, mLocalBluetoothManager)); + doReturn( + List.of( + new RoutingSessionInfo.Builder(TEST_SESSION_ID, TEST_PACKAGE_NAME) + .addSelectedRoute(TEST_DEVICE_ID_1) + .build())) + .when(mInfoMediaManager) + .getRoutingSessionsForPackage(); mInfoMediaDevice1 = spy(new InfoMediaDevice(mContext, mRouteInfo1)); mInfoMediaDevice2 = new InfoMediaDevice(mContext, mRouteInfo2); @@ -124,15 +134,16 @@ public class LocalMediaManagerTest { new LocalMediaManager( mContext, mLocalBluetoothManager, mInfoMediaManager, TEST_PACKAGE_NAME); mLocalMediaManager.mAudioManager = mAudioManager; + mLocalMediaManager.registerCallback(mCallback); + clearInvocations(mCallback); } @Test - public void startScan_mediaDevicesListShouldBeClear() { + public void onDeviceListAdded_shouldClearDeviceList() { final MediaDevice device = mock(MediaDevice.class); mLocalMediaManager.mMediaDevices.add(device); - assertThat(mLocalMediaManager.mMediaDevices).hasSize(1); - mLocalMediaManager.startScan(); + mLocalMediaManager.mMediaDeviceCallback.onDeviceListAdded(Collections.emptyList()); assertThat(mLocalMediaManager.mMediaDevices).isEmpty(); } @@ -147,7 +158,6 @@ public class LocalMediaManagerTest { when(device.getId()).thenReturn(TEST_DEVICE_ID_1); when(currentDevice.getId()).thenReturn(TEST_CURRENT_DEVICE_ID); - mLocalMediaManager.registerCallback(mCallback); assertThat(mLocalMediaManager.connectDevice(device)).isTrue(); verify(mInfoMediaManager).connectToDevice(device); } @@ -158,7 +168,6 @@ public class LocalMediaManagerTest { mLocalMediaManager.mMediaDevices.add(mInfoMediaDevice2); mLocalMediaManager.mCurrentConnectedDevice = mInfoMediaDevice1; - mLocalMediaManager.registerCallback(mCallback); assertThat(mLocalMediaManager.connectDevice(mInfoMediaDevice2)).isTrue(); assertThat(mInfoMediaDevice2.getState()).isEqualTo(LocalMediaManager.MediaDeviceState @@ -171,7 +180,6 @@ public class LocalMediaManagerTest { mLocalMediaManager.mMediaDevices.add(mInfoMediaDevice2); mLocalMediaManager.mCurrentConnectedDevice = mInfoMediaDevice1; - mLocalMediaManager.registerCallback(mCallback); assertThat(mLocalMediaManager.connectDevice(mInfoMediaDevice1)).isFalse(); assertThat(mInfoMediaDevice1.getState()).isNotEqualTo(LocalMediaManager.MediaDeviceState @@ -189,7 +197,6 @@ public class LocalMediaManagerTest { when(cachedDevice.isConnected()).thenReturn(false); when(cachedDevice.isBusy()).thenReturn(false); - mLocalMediaManager.registerCallback(mCallback); assertThat(mLocalMediaManager.connectDevice(device)).isTrue(); verify(cachedDevice).connect(); @@ -252,7 +259,6 @@ public class LocalMediaManagerTest { when(device2.getId()).thenReturn(TEST_DEVICE_ID_2); assertThat(mLocalMediaManager.mMediaDevices).isEmpty(); - mLocalMediaManager.registerCallback(mCallback); mLocalMediaManager.mMediaDeviceCallback.onDeviceListAdded(devices); assertThat(mLocalMediaManager.mMediaDevices).hasSize(2); @@ -274,7 +280,6 @@ public class LocalMediaManagerTest { when(device3.getId()).thenReturn(TEST_DEVICE_ID_3); assertThat(mLocalMediaManager.mMediaDevices).hasSize(1); - mLocalMediaManager.registerCallback(mCallback); mLocalMediaManager.mMediaDeviceCallback.onDeviceListAdded(devices); assertThat(mLocalMediaManager.mMediaDevices).hasSize(2); @@ -292,7 +297,6 @@ public class LocalMediaManagerTest { mLocalMediaManager.mMediaDevices.add(device2); assertThat(mLocalMediaManager.mMediaDevices).hasSize(2); - mLocalMediaManager.registerCallback(mCallback); mLocalMediaManager.mMediaDeviceCallback .onDeviceListRemoved(mLocalMediaManager.mMediaDevices); @@ -302,19 +306,21 @@ public class LocalMediaManagerTest { @Test public void onDeviceListRemoved_phoneDeviceNotLastDeviceAfterRemoveDeviceList_removeList() { - final List<MediaDevice> devices = new ArrayList<>(); final MediaDevice device1 = mock(MediaDevice.class); final MediaDevice device2 = mock(MediaDevice.class); final MediaDevice device3 = mock(MediaDevice.class); - devices.add(device1); - devices.add(device3); + + mLocalMediaManager.mMediaDevices.clear(); mLocalMediaManager.mMediaDevices.add(device1); mLocalMediaManager.mMediaDevices.add(device2); mLocalMediaManager.mMediaDevices.add(device3); assertThat(mLocalMediaManager.mMediaDevices).hasSize(3); - mLocalMediaManager.registerCallback(mCallback); - mLocalMediaManager.mMediaDeviceCallback.onDeviceListRemoved(devices); + + final List<MediaDevice> devicesToRemove = new ArrayList<>(); + devicesToRemove.add(device1); + devicesToRemove.add(device3); + mLocalMediaManager.mMediaDeviceCallback.onDeviceListRemoved(devicesToRemove); assertThat(mLocalMediaManager.mMediaDevices).hasSize(1); verify(mCallback).onDeviceListUpdate(any()); @@ -332,7 +338,6 @@ public class LocalMediaManagerTest { when(device1.getId()).thenReturn(TEST_DEVICE_ID_1); when(device2.getId()).thenReturn(TEST_DEVICE_ID_2); - mLocalMediaManager.registerCallback(mCallback); mLocalMediaManager.mMediaDeviceCallback.onConnectedDeviceChanged(TEST_DEVICE_ID_2); assertThat(mLocalMediaManager.getCurrentConnectedDevice()).isEqualTo(device2); @@ -352,7 +357,6 @@ public class LocalMediaManagerTest { when(device1.getId()).thenReturn(TEST_DEVICE_ID_1); when(device2.getId()).thenReturn(TEST_DEVICE_ID_2); - mLocalMediaManager.registerCallback(mCallback); mLocalMediaManager.mMediaDeviceCallback.onConnectedDeviceChanged(TEST_DEVICE_ID_1); verify(mCallback, never()).onDeviceAttributesChanged(); @@ -366,7 +370,6 @@ public class LocalMediaManagerTest { assertThat(mInfoMediaDevice1.getState()).isEqualTo(LocalMediaManager.MediaDeviceState .STATE_DISCONNECTED); - mLocalMediaManager.registerCallback(mCallback); mLocalMediaManager.mMediaDeviceCallback.onConnectedDeviceChanged(TEST_DEVICE_ID_1); assertThat(mInfoMediaDevice1.getState()).isEqualTo(LocalMediaManager.MediaDeviceState @@ -375,7 +378,6 @@ public class LocalMediaManagerTest { @Test public void onConnectedDeviceChanged_nullConnectedDevice_noException() { - mLocalMediaManager.registerCallback(mCallback); mLocalMediaManager.mMediaDeviceCallback.onConnectedDeviceChanged(TEST_DEVICE_ID_2); } @@ -392,7 +394,6 @@ public class LocalMediaManagerTest { when(cachedDevice.isConnected()).thenReturn(false); when(cachedDevice.isBusy()).thenReturn(false); - mLocalMediaManager.registerCallback(mCallback); mLocalMediaManager.connectDevice(device); mLocalMediaManager.mDeviceAttributeChangeCallback.onDeviceAttributesChanged(); @@ -410,7 +411,6 @@ public class LocalMediaManagerTest { .STATE_CONNECTING); assertThat(mInfoMediaDevice2.getState()).isEqualTo(LocalMediaManager.MediaDeviceState .STATE_CONNECTED); - mLocalMediaManager.registerCallback(mCallback); mLocalMediaManager.mMediaDeviceCallback.onRequestFailed(REASON_UNKNOWN_ERROR); assertThat(mInfoMediaDevice1.getState()).isEqualTo(LocalMediaManager.MediaDeviceState @@ -421,8 +421,6 @@ public class LocalMediaManagerTest { @Test public void onDeviceAttributesChanged_shouldBeCalled() { - mLocalMediaManager.registerCallback(mCallback); - mLocalMediaManager.mDeviceAttributeChangeCallback.onDeviceAttributesChanged(); verify(mCallback).onDeviceAttributesChanged(); @@ -475,7 +473,6 @@ public class LocalMediaManagerTest { when(device1.getDeviceType()).thenReturn(MediaDevice.MediaDeviceType.TYPE_PHONE_DEVICE); assertThat(mLocalMediaManager.mMediaDevices).hasSize(0); - mLocalMediaManager.registerCallback(mCallback); mLocalMediaManager.mMediaDeviceCallback.onDeviceListAdded(devices); assertThat(mLocalMediaManager.mMediaDevices).hasSize(2); @@ -497,7 +494,6 @@ public class LocalMediaManagerTest { when(cachedDevice.isConnected()).thenReturn(false); when(cachedDevice.isBusy()).thenReturn(false); - mLocalMediaManager.registerCallback(mCallback); mLocalMediaManager.connectDevice(device); verify(cachedDevice).connect(); @@ -512,8 +508,6 @@ public class LocalMediaManagerTest { @Test public void onRequestFailed_shouldDispatchOnRequestFailed() { - mLocalMediaManager.registerCallback(mCallback); - mLocalMediaManager.mMediaDeviceCallback.onRequestFailed(1); verify(mCallback).onRequestFailed(1); @@ -532,7 +526,6 @@ public class LocalMediaManagerTest { mShadowBluetoothAdapter = null; assertThat(mLocalMediaManager.mMediaDevices).hasSize(1); - mLocalMediaManager.registerCallback(mCallback); mLocalMediaManager.mMediaDeviceCallback.onDeviceListAdded(devices); assertThat(mLocalMediaManager.mMediaDevices).hasSize(2); diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java index ad3eb92b0519..e77cf2fa6543 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/DeviceConfigService.java @@ -531,13 +531,22 @@ public final class DeviceConfigService extends Binder { pw.println(" put NAMESPACE KEY VALUE [default]"); pw.println(" Change the contents of KEY to VALUE for the given NAMESPACE."); pw.println(" {default} to set as the default value."); + pw.println(" override NAMESPACE KEY VALUE"); + pw.println(" Set flag NAMESPACE/KEY to the given VALUE, and ignores " + + "server-updates for"); + pw.println(" this flag. This can still be called even if there is no underlying " + + "value set."); pw.println(" delete NAMESPACE KEY"); pw.println(" Delete the entry for KEY for the given NAMESPACE."); + pw.println(" clear_override NAMESPACE KEY"); + pw.println(" Clear local sticky flag override for KEY in the given NAMESPACE."); pw.println(" list_namespaces [--public]"); pw.println(" Prints the name of all (or just the public) namespaces."); pw.println(" list [NAMESPACE]"); pw.println(" Print all keys and values defined, optionally for the given " + "NAMESPACE."); + pw.println(" list_local_overrides"); + pw.println(" Print all flags that have been overridden."); pw.println(" reset RESET_MODE [NAMESPACE]"); pw.println(" Reset all flag values, optionally for a NAMESPACE, according to " + "RESET_MODE."); @@ -547,8 +556,9 @@ public final class DeviceConfigService extends Binder { + "flags are reset"); pw.println(" set_sync_disabled_for_tests SYNC_DISABLED_MODE"); pw.println(" Modifies bulk property setting behavior for tests. When in one of the" - + " disabled modes this ensures that config isn't overwritten."); - pw.println(" SYNC_DISABLED_MODE is one of:"); + + " disabled modes"); + pw.println(" this ensures that config isn't overwritten. SYNC_DISABLED_MODE is " + + "one of:"); pw.println(" none: Sync is not disabled. A reboot may be required to restart" + " syncing."); pw.println(" persistent: Sync is disabled, this state will survive a reboot."); diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp index 40db52eec81b..c88c3731aa10 100644 --- a/packages/SystemUI/Android.bp +++ b/packages/SystemUI/Android.bp @@ -117,6 +117,7 @@ android_library { "SystemUILogLib", "SystemUIPluginLib", "SystemUISharedLib", + "SystemUI-shared-utils", "SystemUI-statsd", "SettingsLib", "com_android_systemui_flags_lib", @@ -263,6 +264,7 @@ android_library { "SystemUISharedLib", "SystemUICustomizationLib", "SystemUICustomizationTestUtils", + "SystemUI-shared-utils", "SystemUI-statsd", "SettingsLib", "com_android_systemui_flags_lib", diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/values-cs/strings.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/values-cs/strings.xml index e5dd693505cf..15eb928c1ced 100644 --- a/packages/SystemUI/accessibility/accessibilitymenu/res/values-cs/strings.xml +++ b/packages/SystemUI/accessibility/accessibilitymenu/res/values-cs/strings.xml @@ -21,7 +21,7 @@ <string name="previous_button_content_description" msgid="840869171117765966">"Zpět na předchozí obrazovku"</string> <string name="next_button_content_description" msgid="6810058269847364406">"Přejít na další obrazovku"</string> <string name="accessibility_menu_description" msgid="4458354794093858297">"Nabídka usnadnění přístupu zobrazuje na obrazovce velkou nabídku k ovládání zařízení. Můžete zamknout zařízení, upravit hlasitost a jas, pořídit snímek obrazovky apod."</string> - <string name="accessibility_menu_summary" msgid="340071398148208130">"Ovládat zařízení pomocí velké nabídky"</string> + <string name="accessibility_menu_summary" msgid="340071398148208130">"Ovládání zařízení pomocí velké nabídky"</string> <string name="accessibility_menu_settings_name" msgid="1716888058785672611">"Nastavení nabídky usnadnění přístupu"</string> <string name="accessibility_menu_large_buttons_title" msgid="8978499601044961736">"Velká tlačítka"</string> <string name="accessibility_menu_large_buttons_summary" msgid="236873938502785311">"Zvětšit tlačítka v nabídce přístupnosti"</string> diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt index 9bd6f817cff3..84b1a4b77f07 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/shade/ui/composable/ShadeScene.kt @@ -62,6 +62,7 @@ import com.android.compose.animation.scene.TransitionState import com.android.compose.animation.scene.UserAction import com.android.compose.animation.scene.UserActionResult import com.android.compose.animation.scene.animateSceneFloatAsState +import com.android.compose.modifiers.padding import com.android.compose.modifiers.thenIf import com.android.systemui.battery.BatteryMeterViewController import com.android.systemui.dagger.SysUISingleton @@ -289,6 +290,18 @@ private fun SceneScope.SplitShade( remember(lifecycleOwner, viewModel) { viewModel.getFooterActionsViewModel(lifecycleOwner) } val tileSquishiness by animateSceneFloatAsState(value = 1f, key = QuickSettings.SharedValues.TilesSquishiness) + val unfoldTranslationXForStartSide by + viewModel + .unfoldTranslationX( + isOnStartSide = true, + ) + .collectAsState(0f) + val unfoldTranslationXForEndSide by + viewModel + .unfoldTranslationX( + isOnStartSide = false, + ) + .collectAsState(0f) val navBarBottomHeight = WindowInsets.navigationBars.asPaddingValues().calculateBottomPadding() val density = LocalDensity.current @@ -337,10 +350,18 @@ private fun SceneScope.SplitShade( modifier = Modifier.padding(horizontal = Shade.Dimensions.HorizontalPadding) .then(brightnessMirrorShowingModifier) + .padding( + horizontal = { unfoldTranslationXForStartSide.roundToInt() }, + ) ) Row(modifier = Modifier.fillMaxWidth().weight(1f)) { - Box(modifier = Modifier.weight(1f)) { + Box( + modifier = + Modifier.weight(1f).graphicsLayer { + translationX = unfoldTranslationXForStartSide + }, + ) { BrightnessMirror( viewModel = viewModel.brightnessMirrorViewModel, qsSceneAdapter = viewModel.qsSceneAdapter, @@ -407,7 +428,7 @@ private fun SceneScope.SplitShade( Modifier.weight(1f) .fillMaxHeight() .padding(bottom = navBarBottomHeight) - .then(brightnessMirrorShowingModifier), + .then(brightnessMirrorShowingModifier) ) } } 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 e15d315f9a0a..0893b9d4c580 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 @@ -69,9 +69,19 @@ class ButtonComponent( role = Role.Button contentDescription = label }, - color = MaterialTheme.colorScheme.tertiaryContainer, + color = + if (viewModel.isActive) { + MaterialTheme.colorScheme.tertiaryContainer + } else { + MaterialTheme.colorScheme.surface + }, shape = RoundedCornerShape(28.dp), - contentColor = MaterialTheme.colorScheme.onTertiaryContainer, + contentColor = + if (viewModel.isActive) { + MaterialTheme.colorScheme.onTertiaryContainer + } else { + MaterialTheme.colorScheme.onSurface + }, onClick = onClick, ) { Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { 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 b2351c492fc1..4f3a6c84b13d 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 @@ -40,14 +40,14 @@ import androidx.compose.ui.semantics.role import androidx.compose.ui.semantics.semantics import androidx.compose.ui.unit.dp import com.android.systemui.common.ui.compose.Icon -import com.android.systemui.volume.panel.component.button.ui.viewmodel.ToggleButtonViewModel +import com.android.systemui.volume.panel.component.button.ui.viewmodel.ButtonViewModel import com.android.systemui.volume.panel.ui.composable.ComposeVolumePanelUiComponent import com.android.systemui.volume.panel.ui.composable.VolumePanelComposeScope import kotlinx.coroutines.flow.StateFlow /** [ComposeVolumePanelUiComponent] implementing a toggleable button from a bottom row. */ class ToggleButtonComponent( - private val viewModelFlow: StateFlow<ToggleButtonViewModel?>, + private val viewModelFlow: StateFlow<ButtonViewModel?>, private val onCheckedChange: (isChecked: Boolean) -> Unit ) : ComposeVolumePanelUiComponent { @@ -64,7 +64,7 @@ class ToggleButtonComponent( ) { BottomComponentButtonSurface { val colors = - if (viewModel.isChecked) { + if (viewModel.isActive) { ButtonDefaults.buttonColors( containerColor = MaterialTheme.colorScheme.tertiaryContainer, contentColor = MaterialTheme.colorScheme.onTertiaryContainer, @@ -81,7 +81,7 @@ class ToggleButtonComponent( role = Role.Switch contentDescription = label }, - onClick = { onCheckedChange(!viewModel.isChecked) }, + onClick = { onCheckedChange(!viewModel.isActive) }, shape = RoundedCornerShape(28.dp), colors = colors ) { diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/ui/composable/SpatialAudioPopup.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/ui/composable/SpatialAudioPopup.kt index f377fa6276a0..12d2bc2e274b 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/ui/composable/SpatialAudioPopup.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/component/spatialaudio/ui/composable/SpatialAudioPopup.kt @@ -52,7 +52,7 @@ constructor( VolumePanelUiEvent.VOLUME_PANEL_SPATIAL_AUDIO_POP_UP_SHOWN, 0, null, - viewModel.spatialAudioButtons.value.indexOfFirst { it.button.isChecked } + viewModel.spatialAudioButtons.value.indexOfFirst { it.button.isActive } ) volumePanelPopup.show(expandable, { Title() }, { Content(it) }) } @@ -85,7 +85,7 @@ constructor( for (buttonViewModel in enabledModelStates) { val label = buttonViewModel.button.label.toString() item( - isSelected = buttonViewModel.button.isChecked, + isSelected = buttonViewModel.button.isActive, onItemSelected = { viewModel.setEnabled(buttonViewModel.model) }, contentDescription = label, icon = { Icon(icon = buttonViewModel.button.icon) }, diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelRoot.kt b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelRoot.kt index 910cd5ec107b..1bf541a92070 100644 --- a/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelRoot.kt +++ b/packages/SystemUI/compose/features/src/com/android/systemui/volume/panel/ui/composable/VolumePanelRoot.kt @@ -16,7 +16,7 @@ package com.android.systemui.volume.panel.ui.composable -import androidx.compose.foundation.clickable +import androidx.compose.foundation.gestures.detectTapGestures import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box @@ -38,6 +38,7 @@ import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.res.dimensionResource @@ -75,21 +76,13 @@ fun VolumePanelRoot( modifier = modifier .fillMaxSize() - .clickable(onClick = onDismiss) + .volumePanelClick(onDismiss) .volumePanelPaddings(isPortrait = isPortrait), contentAlignment = Alignment.BottomCenter, ) { val radius = dimensionResource(R.dimen.volume_panel_corner_radius) Surface( - modifier = - Modifier.clickable( - interactionSource = null, - indication = null, - onClick = { - // prevent windowCloseOnTouchOutside from dismissing when tapped - // on the panel itself. - }, - ), + modifier = Modifier.volumePanelClick {}, shape = RoundedCornerShape(topStart = radius, topEnd = radius), color = MaterialTheme.colorScheme.surfaceContainer, ) { @@ -185,3 +178,13 @@ private fun Modifier.volumePanelPaddings(isPortrait: Boolean): Modifier { ) } } + +/** + * For some reason adding clickable modifier onto the VolumePanel affects the traversal order: + * b/331155283. + * + * TODO(b/334870995) revert this to Modifier.clickable + */ +@Composable +private fun Modifier.volumePanelClick(onClick: () -> Unit) = + pointerInput(onClick) { detectTapGestures { onClick() } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt index 85774c67bccb..60b48f28fc23 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt @@ -571,7 +571,7 @@ class UdfpsControllerOverlayTest : SysuiTestCase() { // THEN the view layout is never updated verify(windowManager, never()).updateViewLayout(any(), any()) - // CLEANUPL we hide to end the job that listens for the finishedGoingToSleep signal + // CLEANUP we hide to end the job that listens for the finishedGoingToSleep signal controllerOverlay.hide() } } @@ -595,7 +595,7 @@ class UdfpsControllerOverlayTest : SysuiTestCase() { controllerOverlay.updateOverlayParams(overlayParams) // THEN the view layout is updated - verify(windowManager, never()).updateViewLayout(any(), any()) + verify(windowManager).updateViewLayout(any(), any()) } } } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java deleted file mode 100644 index 4e18a47b45f6..000000000000 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java +++ /dev/null @@ -1,559 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.dreams; - -import static android.view.WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS; - -import static com.google.common.truth.Truth.assertThat; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.clearInvocations; -import static org.mockito.Mockito.doAnswer; -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.inOrder; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.content.ComponentName; -import android.content.Intent; -import android.os.IBinder; -import android.os.RemoteException; -import android.service.dreams.IDreamOverlay; -import android.service.dreams.IDreamOverlayCallback; -import android.service.dreams.IDreamOverlayClient; -import android.service.dreams.IDreamOverlayClientCallback; -import android.view.View; -import android.view.ViewGroup; -import android.view.WindowManager; -import android.view.WindowManagerImpl; - -import androidx.lifecycle.Lifecycle; -import androidx.lifecycle.LifecycleRegistry; -import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.filters.SmallTest; - -import com.android.internal.logging.UiEventLogger; -import com.android.keyguard.KeyguardUpdateMonitor; -import com.android.systemui.SysuiTestCase; -import com.android.systemui.ambient.touch.TouchMonitor; -import com.android.systemui.ambient.touch.dagger.AmbientTouchComponent; -import com.android.systemui.complication.ComplicationLayoutEngine; -import com.android.systemui.dreams.complication.HideComplicationTouchHandler; -import com.android.systemui.dreams.complication.dagger.ComplicationComponent; -import com.android.systemui.dreams.dagger.DreamOverlayComponent; -import com.android.systemui.touch.TouchInsetManager; -import com.android.systemui.util.concurrency.FakeExecutor; -import com.android.systemui.util.time.FakeSystemClock; -import com.android.systemui.utils.leaks.LeakCheckedTest; - -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Captor; -import org.mockito.InOrder; -import org.mockito.Mock; -import org.mockito.Mockito; -import org.mockito.MockitoAnnotations; - -@SmallTest -@RunWith(AndroidJUnit4.class) -public class DreamOverlayServiceTest extends SysuiTestCase { - private static final ComponentName LOW_LIGHT_COMPONENT = new ComponentName("package", - "lowlight"); - - private static final ComponentName HOME_CONTROL_PANEL_DREAM_COMPONENT = - new ComponentName("package", "homeControlPanel"); - private static final String DREAM_COMPONENT = "package/dream"; - private static final String WINDOW_NAME = "test"; - private final FakeSystemClock mFakeSystemClock = new FakeSystemClock(); - private final FakeExecutor mMainExecutor = new FakeExecutor(mFakeSystemClock); - - @Mock - DreamOverlayLifecycleOwner mLifecycleOwner; - - @Mock - LifecycleRegistry mLifecycleRegistry; - - @Rule - public final LeakCheckedTest.SysuiLeakCheck mLeakCheck = new LeakCheckedTest.SysuiLeakCheck(); - - WindowManager.LayoutParams mWindowParams; - - @Mock - IDreamOverlayCallback mDreamOverlayCallback; - - @Mock - WindowManagerImpl mWindowManager; - - @Mock - com.android.systemui.complication.dagger.ComplicationComponent.Factory - mComplicationComponentFactory; - - @Mock - com.android.systemui.complication.dagger.ComplicationComponent mComplicationComponent; - - @Mock - ComplicationLayoutEngine mComplicationVisibilityController; - - @Mock - ComplicationComponent.Factory mDreamComplicationComponentFactory; - - @Mock - ComplicationComponent mDreamComplicationComponent; - - @Mock - HideComplicationTouchHandler mHideComplicationTouchHandler; - - @Mock - DreamOverlayComponent.Factory mDreamOverlayComponentFactory; - - @Mock - DreamOverlayComponent mDreamOverlayComponent; - - @Mock - AmbientTouchComponent.Factory mAmbientTouchComponentFactory; - - @Mock - AmbientTouchComponent mAmbientTouchComponent; - - @Mock - DreamOverlayContainerView mDreamOverlayContainerView; - - @Mock - DreamOverlayContainerViewController mDreamOverlayContainerViewController; - - @Mock - KeyguardUpdateMonitor mKeyguardUpdateMonitor; - - @Mock - TouchMonitor mTouchMonitor; - - @Mock - DreamOverlayStateController mStateController; - - @Mock - ViewGroup mDreamOverlayContainerViewParent; - - @Mock - TouchInsetManager mTouchInsetManager; - - @Mock - UiEventLogger mUiEventLogger; - - @Mock - DreamOverlayCallbackController mDreamOverlayCallbackController; - - @Captor - ArgumentCaptor<View> mViewCaptor; - - DreamOverlayService mService; - - @Before - public void setup() { - MockitoAnnotations.initMocks(this); - - when(mDreamOverlayComponent.getDreamOverlayContainerViewController()) - .thenReturn(mDreamOverlayContainerViewController); - when(mLifecycleOwner.getRegistry()) - .thenReturn(mLifecycleRegistry); - when(mComplicationComponentFactory - .create(any(), any(), any(), any())) - .thenReturn(mComplicationComponent); - when(mComplicationComponent.getVisibilityController()) - .thenReturn(mComplicationVisibilityController); - when(mDreamComplicationComponent.getHideComplicationTouchHandler()) - .thenReturn(mHideComplicationTouchHandler); - when(mDreamComplicationComponentFactory - .create(any(), any())) - .thenReturn(mDreamComplicationComponent); - when(mDreamOverlayComponentFactory - .create(any(), any(), any())) - .thenReturn(mDreamOverlayComponent); - when(mAmbientTouchComponentFactory.create(any(), any())).thenReturn(mAmbientTouchComponent); - when(mAmbientTouchComponent.getTouchMonitor()) - .thenReturn(mTouchMonitor); - when(mDreamOverlayContainerViewController.getContainerView()) - .thenReturn(mDreamOverlayContainerView); - - mWindowParams = new WindowManager.LayoutParams(); - mService = new DreamOverlayService( - mContext, - mLifecycleOwner, - mMainExecutor, - mWindowManager, - mComplicationComponentFactory, - mDreamComplicationComponentFactory, - mDreamOverlayComponentFactory, - mAmbientTouchComponentFactory, - mStateController, - mKeyguardUpdateMonitor, - mUiEventLogger, - mTouchInsetManager, - LOW_LIGHT_COMPONENT, - HOME_CONTROL_PANEL_DREAM_COMPONENT, - mDreamOverlayCallbackController, - WINDOW_NAME); - } - - public IDreamOverlayClient getClient() throws RemoteException { - final IBinder proxy = mService.onBind(new Intent()); - final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy); - final IDreamOverlayClientCallback callback = - Mockito.mock(IDreamOverlayClientCallback.class); - overlay.getClient(callback); - final ArgumentCaptor<IDreamOverlayClient> clientCaptor = - ArgumentCaptor.forClass(IDreamOverlayClient.class); - verify(callback).onDreamOverlayClient(clientCaptor.capture()); - - return clientCaptor.getValue(); - } - - @Test - public void testOnStartMetricsLogged() throws Exception { - final IDreamOverlayClient client = getClient(); - - // Inform the overlay service of dream starting. - client.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT, - false /*shouldShowComplication*/); - mMainExecutor.runAllReady(); - - verify(mUiEventLogger).log(DreamOverlayService.DreamOverlayEvent.DREAM_OVERLAY_ENTER_START); - verify(mUiEventLogger).log( - DreamOverlayService.DreamOverlayEvent.DREAM_OVERLAY_COMPLETE_START); - } - - @Test - public void testOverlayContainerViewAddedToWindow() throws Exception { - final IDreamOverlayClient client = getClient(); - - // Inform the overlay service of dream starting. - client.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT, - false /*shouldShowComplication*/); - mMainExecutor.runAllReady(); - - verify(mWindowManager).addView(any(), any()); - } - - // Validates that {@link DreamOverlayService} properly handles the case where the dream's - // window is no longer valid by the time start is called. - @Test - public void testInvalidWindowAddStart() throws Exception { - final IDreamOverlayClient client = getClient(); - - doThrow(new WindowManager.BadTokenException()).when(mWindowManager).addView(any(), any()); - // Inform the overlay service of dream starting. - client.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT, - false /*shouldShowComplication*/); - mMainExecutor.runAllReady(); - - verify(mWindowManager).addView(any(), any()); - - verify(mStateController).setOverlayActive(false); - verify(mStateController).setLowLightActive(false); - verify(mStateController).setEntryAnimationsFinished(false); - - verify(mStateController, never()).setOverlayActive(true); - verify(mUiEventLogger, never()).log( - DreamOverlayService.DreamOverlayEvent.DREAM_OVERLAY_COMPLETE_START); - - verify(mDreamOverlayCallbackController, never()).onStartDream(); - } - - @Test - public void testDreamOverlayContainerViewControllerInitialized() throws Exception { - final IDreamOverlayClient client = getClient(); - - // Inform the overlay service of dream starting. - client.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT, - false /*shouldShowComplication*/); - mMainExecutor.runAllReady(); - - verify(mDreamOverlayContainerViewController).init(); - } - - @Test - public void testDreamOverlayContainerViewRemovedFromOldParentWhenInitialized() - throws Exception { - when(mDreamOverlayContainerView.getParent()) - .thenReturn(mDreamOverlayContainerViewParent) - .thenReturn(null); - - final IDreamOverlayClient client = getClient(); - - // Inform the overlay service of dream starting. - client.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT, - false /*shouldShowComplication*/); - mMainExecutor.runAllReady(); - - verify(mDreamOverlayContainerViewParent).removeView(mDreamOverlayContainerView); - } - - @Test - public void testShouldShowComplicationsSetByStartDream() throws RemoteException { - final IDreamOverlayClient client = getClient(); - - // Inform the overlay service of dream starting. - client.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT, - true /*shouldShowComplication*/); - mMainExecutor.runAllReady(); - - assertThat(mService.shouldShowComplications()).isTrue(); - } - - @Test - public void testLowLightSetByStartDream() throws RemoteException { - final IDreamOverlayClient client = getClient(); - - // Inform the overlay service of dream starting. - client.startDream(mWindowParams, mDreamOverlayCallback, - LOW_LIGHT_COMPONENT.flattenToString(), false /*shouldShowComplication*/); - mMainExecutor.runAllReady(); - - assertThat(mService.getDreamComponent()).isEqualTo(LOW_LIGHT_COMPONENT); - verify(mStateController).setLowLightActive(true); - } - - @Test - public void testHomeControlPanelSetsByStartDream() throws RemoteException { - final IDreamOverlayClient client = getClient(); - - // Inform the overlay service of dream starting. - client.startDream(mWindowParams, mDreamOverlayCallback, - HOME_CONTROL_PANEL_DREAM_COMPONENT.flattenToString(), - false /*shouldShowComplication*/); - mMainExecutor.runAllReady(); - assertThat(mService.getDreamComponent()).isEqualTo(HOME_CONTROL_PANEL_DREAM_COMPONENT); - verify(mStateController).setHomeControlPanelActive(true); - } - - @Test - public void testOnEndDream() throws RemoteException { - final IDreamOverlayClient client = getClient(); - - // Inform the overlay service of dream starting. - client.startDream(mWindowParams, mDreamOverlayCallback, - LOW_LIGHT_COMPONENT.flattenToString(), false /*shouldShowComplication*/); - mMainExecutor.runAllReady(); - - // Verify view added. - verify(mWindowManager).addView(mViewCaptor.capture(), any()); - - // Service destroyed. - mService.onEndDream(); - mMainExecutor.runAllReady(); - - // Verify view removed. - verify(mWindowManager).removeView(mViewCaptor.getValue()); - - // Verify state correctly set. - verify(mStateController).setOverlayActive(false); - verify(mStateController).setLowLightActive(false); - verify(mStateController).setEntryAnimationsFinished(false); - } - - @Test - public void testImmediateEndDream() throws Exception { - final IDreamOverlayClient client = getClient(); - - // Start the dream, but don't execute any Runnables put on the executor yet. We delay - // executing Runnables as the timing isn't guaranteed and we want to verify that the overlay - // starts and finishes in the proper order even if Runnables are delayed. - client.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT, - false /*shouldShowComplication*/); - // Immediately end the dream. - client.endDream(); - // Run any scheduled Runnables. - mMainExecutor.runAllReady(); - - // The overlay starts then finishes. - InOrder inOrder = inOrder(mWindowManager); - inOrder.verify(mWindowManager).addView(mViewCaptor.capture(), any()); - inOrder.verify(mWindowManager).removeView(mViewCaptor.getValue()); - } - - @Test - public void testEndDreamDuringStartDream() throws Exception { - final IDreamOverlayClient client = getClient(); - - // Schedule the endDream call in the middle of the startDream implementation, as any - // ordering is possible. - doAnswer(invocation -> { - client.endDream(); - return null; - }).when(mStateController).setOverlayActive(true); - - // Start the dream. - client.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT, - false /*shouldShowComplication*/); - mMainExecutor.runAllReady(); - - // The overlay starts then finishes. - InOrder inOrder = inOrder(mWindowManager); - inOrder.verify(mWindowManager).addView(mViewCaptor.capture(), any()); - inOrder.verify(mWindowManager).removeView(mViewCaptor.getValue()); - } - - @Test - public void testDestroy() throws RemoteException { - final IDreamOverlayClient client = getClient(); - - // Inform the overlay service of dream starting. - client.startDream(mWindowParams, mDreamOverlayCallback, - LOW_LIGHT_COMPONENT.flattenToString(), false /*shouldShowComplication*/); - mMainExecutor.runAllReady(); - - // Verify view added. - verify(mWindowManager).addView(mViewCaptor.capture(), any()); - - // Service destroyed. - mService.onDestroy(); - mMainExecutor.runAllReady(); - - // Verify view removed. - verify(mWindowManager).removeView(mViewCaptor.getValue()); - - // Verify state correctly set. - verify(mKeyguardUpdateMonitor).removeCallback(any()); - verify(mLifecycleRegistry).setCurrentState(Lifecycle.State.DESTROYED); - verify(mStateController).setOverlayActive(false); - verify(mStateController).setLowLightActive(false); - verify(mStateController).setEntryAnimationsFinished(false); - } - - @Test - public void testDoNotRemoveViewOnDestroyIfOverlayNotStarted() { - // Service destroyed without ever starting dream. - mService.onDestroy(); - mMainExecutor.runAllReady(); - - // Verify no view is removed. - verify(mWindowManager, never()).removeView(any()); - - // Verify state still correctly set. - verify(mKeyguardUpdateMonitor).removeCallback(any()); - verify(mLifecycleRegistry).setCurrentState(Lifecycle.State.DESTROYED); - verify(mStateController).setOverlayActive(false); - verify(mStateController).setLowLightActive(false); - } - - @Test - public void testDecorViewNotAddedToWindowAfterDestroy() throws Exception { - final IDreamOverlayClient client = getClient(); - - // Destroy the service. - mService.onDestroy(); - mMainExecutor.runAllReady(); - - // Inform the overlay service of dream starting. - client.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT, - false /*shouldShowComplication*/); - mMainExecutor.runAllReady(); - - verify(mWindowManager, never()).addView(any(), any()); - } - - @Test - public void testNeverRemoveDecorViewIfNotAdded() { - // Service destroyed before dream started. - mService.onDestroy(); - mMainExecutor.runAllReady(); - - verify(mWindowManager, never()).removeView(any()); - } - - @Test - public void testResetCurrentOverlayWhenConnectedToNewDream() throws RemoteException { - final IDreamOverlayClient client = getClient(); - - // Inform the overlay service of dream starting. Do not show dream complications. - client.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT, - false /*shouldShowComplication*/); - mMainExecutor.runAllReady(); - - // Verify that a new window is added. - verify(mWindowManager).addView(mViewCaptor.capture(), any()); - final View windowDecorView = mViewCaptor.getValue(); - - // Assert that the overlay is not showing complications. - assertThat(mService.shouldShowComplications()).isFalse(); - - clearInvocations(mDreamOverlayComponent); - clearInvocations(mAmbientTouchComponent); - clearInvocations(mWindowManager); - - // New dream starting with dream complications showing. Note that when a new dream is - // binding to the dream overlay service, it receives the same instance of IBinder as the - // first one. - client.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT, - true /*shouldShowComplication*/); - mMainExecutor.runAllReady(); - - // Assert that the overlay is showing complications. - assertThat(mService.shouldShowComplications()).isTrue(); - - // Verify that the old overlay window has been removed, and a new one created. - verify(mWindowManager).removeView(windowDecorView); - verify(mWindowManager).addView(any(), any()); - - // Verify that new instances of overlay container view controller and overlay touch monitor - // are created. - verify(mDreamOverlayComponent).getDreamOverlayContainerViewController(); - verify(mAmbientTouchComponent).getTouchMonitor(); - } - - @Test - public void testWakeUp() throws RemoteException { - final IDreamOverlayClient client = getClient(); - - // Inform the overlay service of dream starting. - client.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT, - true /*shouldShowComplication*/); - mMainExecutor.runAllReady(); - - mService.onWakeUp(); - verify(mDreamOverlayContainerViewController).wakeUp(); - verify(mDreamOverlayCallbackController).onWakeUp(); - } - - @Test - public void testWakeUpBeforeStartDoesNothing() { - mService.onWakeUp(); - verify(mDreamOverlayContainerViewController, never()).wakeUp(); - } - - @Test - public void testSystemFlagShowForAllUsersSetOnWindow() throws RemoteException { - final IDreamOverlayClient client = getClient(); - - // Inform the overlay service of dream starting. Do not show dream complications. - client.startDream(mWindowParams, mDreamOverlayCallback, DREAM_COMPONENT, - false /*shouldShowComplication*/); - mMainExecutor.runAllReady(); - - final ArgumentCaptor<WindowManager.LayoutParams> paramsCaptor = - ArgumentCaptor.forClass(WindowManager.LayoutParams.class); - - // Verify that a new window is added. - verify(mWindowManager).addView(any(), paramsCaptor.capture()); - - assertThat((paramsCaptor.getValue().privateFlags & SYSTEM_FLAG_SHOW_FOR_ALL_USERS) - == SYSTEM_FLAG_SHOW_FOR_ALL_USERS).isTrue(); - } -} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt new file mode 100644 index 000000000000..b18a8ecee946 --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/dreams/DreamOverlayServiceTest.kt @@ -0,0 +1,573 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS 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.dreams + +import android.content.ComponentName +import android.content.Intent +import android.os.RemoteException +import android.service.dreams.IDreamOverlay +import android.service.dreams.IDreamOverlayCallback +import android.service.dreams.IDreamOverlayClient +import android.service.dreams.IDreamOverlayClientCallback +import android.view.View +import android.view.ViewGroup +import android.view.WindowManager +import android.view.WindowManagerImpl +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.LifecycleRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.internal.logging.UiEventLogger +import com.android.keyguard.KeyguardUpdateMonitor +import com.android.systemui.SysuiTestCase +import com.android.systemui.ambient.touch.TouchMonitor +import com.android.systemui.ambient.touch.dagger.AmbientTouchComponent +import com.android.systemui.complication.ComplicationHostViewController +import com.android.systemui.complication.ComplicationLayoutEngine +import com.android.systemui.complication.dagger.ComplicationComponent +import com.android.systemui.dreams.complication.HideComplicationTouchHandler +import com.android.systemui.dreams.dagger.DreamOverlayComponent +import com.android.systemui.touch.TouchInsetManager +import com.android.systemui.util.concurrency.FakeExecutor +import com.android.systemui.util.mockito.any +import com.android.systemui.util.mockito.whenever +import com.android.systemui.util.time.FakeSystemClock +import com.google.common.truth.Truth +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 +import org.mockito.MockitoAnnotations +import org.mockito.invocation.InvocationOnMock + +@SmallTest +@RunWith(AndroidJUnit4::class) +class DreamOverlayServiceTest : SysuiTestCase() { + private val mFakeSystemClock = FakeSystemClock() + private val mMainExecutor = FakeExecutor(mFakeSystemClock) + + @Mock lateinit var mLifecycleOwner: DreamOverlayLifecycleOwner + + @Mock lateinit var mLifecycleRegistry: LifecycleRegistry + + lateinit var mWindowParams: WindowManager.LayoutParams + + @Mock lateinit var mDreamOverlayCallback: IDreamOverlayCallback + + @Mock lateinit var mWindowManager: WindowManagerImpl + + @Mock lateinit var mComplicationComponentFactory: ComplicationComponent.Factory + + @Mock lateinit var mComplicationComponent: ComplicationComponent + + @Mock lateinit var mComplicationHostViewController: ComplicationHostViewController + + @Mock lateinit var mComplicationVisibilityController: ComplicationLayoutEngine + + @Mock + lateinit var mDreamComplicationComponentFactory: + com.android.systemui.dreams.complication.dagger.ComplicationComponent.Factory + + @Mock + lateinit var mDreamComplicationComponent: + com.android.systemui.dreams.complication.dagger.ComplicationComponent + + @Mock lateinit var mHideComplicationTouchHandler: HideComplicationTouchHandler + + @Mock lateinit var mDreamOverlayComponentFactory: DreamOverlayComponent.Factory + + @Mock lateinit var mDreamOverlayComponent: DreamOverlayComponent + + @Mock lateinit var mAmbientTouchComponentFactory: AmbientTouchComponent.Factory + + @Mock lateinit var mAmbientTouchComponent: AmbientTouchComponent + + @Mock lateinit var mDreamOverlayContainerView: DreamOverlayContainerView + + @Mock lateinit var mDreamOverlayContainerViewController: DreamOverlayContainerViewController + + @Mock lateinit var mKeyguardUpdateMonitor: KeyguardUpdateMonitor + + @Mock lateinit var mTouchMonitor: TouchMonitor + + @Mock lateinit var mStateController: DreamOverlayStateController + + @Mock lateinit var mDreamOverlayContainerViewParent: ViewGroup + + @Mock lateinit var mTouchInsetManager: TouchInsetManager + + @Mock lateinit var mUiEventLogger: UiEventLogger + + @Mock lateinit var mDreamOverlayCallbackController: DreamOverlayCallbackController + + @Captor var mViewCaptor: ArgumentCaptor<View>? = null + var mService: DreamOverlayService? = null + @Before + fun setup() { + MockitoAnnotations.initMocks(this) + whenever(mDreamOverlayComponent.getDreamOverlayContainerViewController()) + .thenReturn(mDreamOverlayContainerViewController) + whenever(mComplicationComponent.getComplicationHostViewController()) + .thenReturn(mComplicationHostViewController) + whenever(mLifecycleOwner.registry).thenReturn(mLifecycleRegistry) + whenever(mComplicationComponentFactory.create(any(), any(), any(), any())) + .thenReturn(mComplicationComponent) + whenever(mComplicationComponent.getVisibilityController()) + .thenReturn(mComplicationVisibilityController) + whenever(mDreamComplicationComponent.getHideComplicationTouchHandler()) + .thenReturn(mHideComplicationTouchHandler) + whenever(mDreamComplicationComponentFactory.create(any(), any())) + .thenReturn(mDreamComplicationComponent) + whenever(mDreamOverlayComponentFactory.create(any(), any(), any())) + .thenReturn(mDreamOverlayComponent) + whenever(mAmbientTouchComponentFactory.create(any(), any())) + .thenReturn(mAmbientTouchComponent) + whenever(mAmbientTouchComponent.getTouchMonitor()).thenReturn(mTouchMonitor) + whenever(mDreamOverlayContainerViewController.containerView) + .thenReturn(mDreamOverlayContainerView) + mWindowParams = WindowManager.LayoutParams() + mService = + DreamOverlayService( + mContext, + mLifecycleOwner, + mMainExecutor, + mWindowManager, + mComplicationComponentFactory, + mDreamComplicationComponentFactory, + mDreamOverlayComponentFactory, + mAmbientTouchComponentFactory, + mStateController, + mKeyguardUpdateMonitor, + mUiEventLogger, + mTouchInsetManager, + LOW_LIGHT_COMPONENT, + HOME_CONTROL_PANEL_DREAM_COMPONENT, + mDreamOverlayCallbackController, + WINDOW_NAME + ) + } + + @get:Throws(RemoteException::class) + val client: IDreamOverlayClient + get() { + val proxy = mService!!.onBind(Intent()) + val overlay = IDreamOverlay.Stub.asInterface(proxy) + val callback = Mockito.mock(IDreamOverlayClientCallback::class.java) + overlay.getClient(callback) + val clientCaptor = ArgumentCaptor.forClass(IDreamOverlayClient::class.java) + Mockito.verify(callback).onDreamOverlayClient(clientCaptor.capture()) + return clientCaptor.value + } + + @Test + fun testOnStartMetricsLogged() { + val client = client + + // Inform the overlay service of dream starting. + client.startDream( + mWindowParams, + mDreamOverlayCallback, + DREAM_COMPONENT, + false /*shouldShowComplication*/ + ) + mMainExecutor.runAllReady() + Mockito.verify(mUiEventLogger) + .log(DreamOverlayService.DreamOverlayEvent.DREAM_OVERLAY_ENTER_START) + Mockito.verify(mUiEventLogger) + .log(DreamOverlayService.DreamOverlayEvent.DREAM_OVERLAY_COMPLETE_START) + } + + @Test + fun testOverlayContainerViewAddedToWindow() { + val client = client + + // Inform the overlay service of dream starting. + client.startDream( + mWindowParams, + mDreamOverlayCallback, + DREAM_COMPONENT, + false /*shouldShowComplication*/ + ) + mMainExecutor.runAllReady() + Mockito.verify(mWindowManager).addView(any(), any()) + } + + // Validates that {@link DreamOverlayService} properly handles the case where the dream's + // window is no longer valid by the time start is called. + @Test + fun testInvalidWindowAddStart() { + val client = client + Mockito.doThrow(WindowManager.BadTokenException()) + .`when`(mWindowManager) + .addView(any(), any()) + // Inform the overlay service of dream starting. + client.startDream( + mWindowParams, + mDreamOverlayCallback, + DREAM_COMPONENT, + false /*shouldShowComplication*/ + ) + mMainExecutor.runAllReady() + Mockito.verify(mWindowManager).addView(any(), any()) + Mockito.verify(mStateController).setOverlayActive(false) + Mockito.verify(mStateController).setLowLightActive(false) + Mockito.verify(mStateController).setEntryAnimationsFinished(false) + Mockito.verify(mStateController, Mockito.never()).setOverlayActive(true) + Mockito.verify(mUiEventLogger, Mockito.never()) + .log(DreamOverlayService.DreamOverlayEvent.DREAM_OVERLAY_COMPLETE_START) + Mockito.verify(mDreamOverlayCallbackController, Mockito.never()).onStartDream() + } + + @Test + fun testDreamOverlayContainerViewControllerInitialized() { + val client = client + + // Inform the overlay service of dream starting. + client.startDream( + mWindowParams, + mDreamOverlayCallback, + DREAM_COMPONENT, + false /*shouldShowComplication*/ + ) + mMainExecutor.runAllReady() + Mockito.verify(mDreamOverlayContainerViewController).init() + } + + @Test + fun testDreamOverlayContainerViewRemovedFromOldParentWhenInitialized() { + whenever(mDreamOverlayContainerView.parent) + .thenReturn(mDreamOverlayContainerViewParent) + .thenReturn(null) + val client = client + + // Inform the overlay service of dream starting. + client.startDream( + mWindowParams, + mDreamOverlayCallback, + DREAM_COMPONENT, + false /*shouldShowComplication*/ + ) + mMainExecutor.runAllReady() + Mockito.verify(mDreamOverlayContainerViewParent).removeView(mDreamOverlayContainerView) + } + + @Test + fun testShouldShowComplicationsSetByStartDream() { + val client = client + + // Inform the overlay service of dream starting. + client.startDream( + mWindowParams, + mDreamOverlayCallback, + DREAM_COMPONENT, + true /*shouldShowComplication*/ + ) + mMainExecutor.runAllReady() + Truth.assertThat(mService!!.shouldShowComplications()).isTrue() + } + + @Test + fun testLowLightSetByStartDream() { + val client = client + + // Inform the overlay service of dream starting. + client.startDream( + mWindowParams, + mDreamOverlayCallback, + LOW_LIGHT_COMPONENT.flattenToString(), + false /*shouldShowComplication*/ + ) + mMainExecutor.runAllReady() + Truth.assertThat(mService!!.dreamComponent).isEqualTo(LOW_LIGHT_COMPONENT) + Mockito.verify(mStateController).setLowLightActive(true) + } + + @Test + fun testHomeControlPanelSetsByStartDream() { + val client = client + + // Inform the overlay service of dream starting. + client.startDream( + mWindowParams, + mDreamOverlayCallback, + HOME_CONTROL_PANEL_DREAM_COMPONENT.flattenToString(), + false /*shouldShowComplication*/ + ) + mMainExecutor.runAllReady() + Truth.assertThat(mService!!.dreamComponent).isEqualTo(HOME_CONTROL_PANEL_DREAM_COMPONENT) + Mockito.verify(mStateController).setHomeControlPanelActive(true) + } + + @Test + fun testOnEndDream() { + val client = client + + // Inform the overlay service of dream starting. + client.startDream( + mWindowParams, + mDreamOverlayCallback, + LOW_LIGHT_COMPONENT.flattenToString(), + false /*shouldShowComplication*/ + ) + mMainExecutor.runAllReady() + + // Verify view added. + Mockito.verify(mWindowManager).addView(mViewCaptor!!.capture(), any()) + + // Service destroyed. + mService!!.onEndDream() + mMainExecutor.runAllReady() + + // Verify view removed. + Mockito.verify(mWindowManager).removeView(mViewCaptor!!.value) + + // Verify state correctly set. + Mockito.verify(mStateController).setOverlayActive(false) + Mockito.verify(mStateController).setLowLightActive(false) + Mockito.verify(mStateController).setEntryAnimationsFinished(false) + } + + @Test + fun testImmediateEndDream() { + val client = client + + // Start the dream, but don't execute any Runnables put on the executor yet. We delay + // executing Runnables as the timing isn't guaranteed and we want to verify that the overlay + // starts and finishes in the proper order even if Runnables are delayed. + client.startDream( + mWindowParams, + mDreamOverlayCallback, + DREAM_COMPONENT, + false /*shouldShowComplication*/ + ) + // Immediately end the dream. + client.endDream() + // Run any scheduled Runnables. + mMainExecutor.runAllReady() + + // The overlay starts then finishes. + val inOrder = Mockito.inOrder(mWindowManager) + inOrder.verify(mWindowManager).addView(mViewCaptor!!.capture(), any()) + inOrder.verify(mWindowManager).removeView(mViewCaptor!!.value) + } + + @Test + fun testEndDreamDuringStartDream() { + val client = client + + // Schedule the endDream call in the middle of the startDream implementation, as any + // ordering is possible. + Mockito.doAnswer { invocation: InvocationOnMock? -> + client.endDream() + null + } + .`when`(mStateController) + .setOverlayActive(true) + + // Start the dream. + client.startDream( + mWindowParams, + mDreamOverlayCallback, + DREAM_COMPONENT, + false /*shouldShowComplication*/ + ) + mMainExecutor.runAllReady() + + // The overlay starts then finishes. + val inOrder = Mockito.inOrder(mWindowManager) + inOrder.verify(mWindowManager).addView(mViewCaptor!!.capture(), any()) + inOrder.verify(mWindowManager).removeView(mViewCaptor!!.value) + } + + @Test + fun testDestroy() { + val client = client + + // Inform the overlay service of dream starting. + client.startDream( + mWindowParams, + mDreamOverlayCallback, + LOW_LIGHT_COMPONENT.flattenToString(), + false /*shouldShowComplication*/ + ) + mMainExecutor.runAllReady() + + // Verify view added. + Mockito.verify(mWindowManager).addView(mViewCaptor!!.capture(), any()) + + // Service destroyed. + mService!!.onDestroy() + mMainExecutor.runAllReady() + + // Verify view removed. + Mockito.verify(mWindowManager).removeView(mViewCaptor!!.value) + + // Verify state correctly set. + Mockito.verify(mKeyguardUpdateMonitor).removeCallback(any()) + Mockito.verify(mLifecycleRegistry).currentState = Lifecycle.State.DESTROYED + Mockito.verify(mStateController).setOverlayActive(false) + Mockito.verify(mStateController).setLowLightActive(false) + Mockito.verify(mStateController).setEntryAnimationsFinished(false) + } + + @Test + fun testDoNotRemoveViewOnDestroyIfOverlayNotStarted() { + // Service destroyed without ever starting dream. + mService!!.onDestroy() + mMainExecutor.runAllReady() + + // Verify no view is removed. + Mockito.verify(mWindowManager, Mockito.never()).removeView(any()) + + // Verify state still correctly set. + Mockito.verify(mKeyguardUpdateMonitor).removeCallback(any()) + Mockito.verify(mLifecycleRegistry).currentState = Lifecycle.State.DESTROYED + Mockito.verify(mStateController).setOverlayActive(false) + Mockito.verify(mStateController).setLowLightActive(false) + } + + @Test + fun testDecorViewNotAddedToWindowAfterDestroy() { + val client = client + + // Destroy the service. + mService!!.onDestroy() + mMainExecutor.runAllReady() + + // Inform the overlay service of dream starting. + client.startDream( + mWindowParams, + mDreamOverlayCallback, + DREAM_COMPONENT, + false /*shouldShowComplication*/ + ) + mMainExecutor.runAllReady() + Mockito.verify(mWindowManager, Mockito.never()).addView(any(), any()) + } + + @Test + fun testNeverRemoveDecorViewIfNotAdded() { + // Service destroyed before dream started. + mService!!.onDestroy() + mMainExecutor.runAllReady() + Mockito.verify(mWindowManager, Mockito.never()).removeView(any()) + } + + @Test + fun testResetCurrentOverlayWhenConnectedToNewDream() { + val client = client + + // Inform the overlay service of dream starting. Do not show dream complications. + client.startDream( + mWindowParams, + mDreamOverlayCallback, + DREAM_COMPONENT, + false /*shouldShowComplication*/ + ) + mMainExecutor.runAllReady() + + // Verify that a new window is added. + Mockito.verify(mWindowManager).addView(mViewCaptor!!.capture(), any()) + val windowDecorView = mViewCaptor!!.value + + // Assert that the overlay is not showing complications. + Truth.assertThat(mService!!.shouldShowComplications()).isFalse() + Mockito.clearInvocations(mDreamOverlayComponent) + Mockito.clearInvocations(mAmbientTouchComponent) + Mockito.clearInvocations(mWindowManager) + + // New dream starting with dream complications showing. Note that when a new dream is + // binding to the dream overlay service, it receives the same instance of IBinder as the + // first one. + client.startDream( + mWindowParams, + mDreamOverlayCallback, + DREAM_COMPONENT, + true /*shouldShowComplication*/ + ) + mMainExecutor.runAllReady() + + // Assert that the overlay is showing complications. + Truth.assertThat(mService!!.shouldShowComplications()).isTrue() + + // Verify that the old overlay window has been removed, and a new one created. + Mockito.verify(mWindowManager).removeView(windowDecorView) + Mockito.verify(mWindowManager).addView(any(), any()) + + // Verify that new instances of overlay container view controller and overlay touch monitor + // are created. + Mockito.verify(mDreamOverlayComponent).getDreamOverlayContainerViewController() + Mockito.verify(mAmbientTouchComponent).getTouchMonitor() + } + + @Test + fun testWakeUp() { + val client = client + + // Inform the overlay service of dream starting. + client.startDream( + mWindowParams, + mDreamOverlayCallback, + DREAM_COMPONENT, + true /*shouldShowComplication*/ + ) + mMainExecutor.runAllReady() + mService!!.onWakeUp() + Mockito.verify(mDreamOverlayContainerViewController).wakeUp() + Mockito.verify(mDreamOverlayCallbackController).onWakeUp() + } + + @Test + fun testWakeUpBeforeStartDoesNothing() { + mService!!.onWakeUp() + Mockito.verify(mDreamOverlayContainerViewController, Mockito.never()).wakeUp() + } + + @Test + fun testSystemFlagShowForAllUsersSetOnWindow() { + val client = client + + // Inform the overlay service of dream starting. Do not show dream complications. + client.startDream( + mWindowParams, + mDreamOverlayCallback, + DREAM_COMPONENT, + false /*shouldShowComplication*/ + ) + mMainExecutor.runAllReady() + val paramsCaptor = ArgumentCaptor.forClass(WindowManager.LayoutParams::class.java) + + // Verify that a new window is added. + Mockito.verify(mWindowManager).addView(any(), paramsCaptor.capture()) + Truth.assertThat( + paramsCaptor.value.privateFlags and + WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS == + WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS + ) + .isTrue() + } + + companion object { + private val LOW_LIGHT_COMPONENT = ComponentName("package", "lowlight") + private val HOME_CONTROL_PANEL_DREAM_COMPONENT = + ComponentName("package", "homeControlPanel") + private const val DREAM_COMPONENT = "package/dream" + private const val WINDOW_NAME = "test" + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt index 470d342402e1..65fd1010ec38 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/scene/SceneFrameworkIntegrationTest.kt @@ -85,6 +85,7 @@ import com.android.systemui.statusbar.pipeline.mobile.data.repository.fakeMobile import com.android.systemui.statusbar.policy.domain.interactor.deviceProvisioningInteractor import com.android.systemui.telephony.data.repository.fakeTelephonyRepository import com.android.systemui.testKosmos +import com.android.systemui.unfold.domain.interactor.unfoldTransitionInteractor import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever @@ -228,6 +229,7 @@ class SceneFrameworkIntegrationTest : SysuiTestCase() { footerActionsController = kosmos.footerActionsController, footerActionsViewModelFactory = kosmos.footerActionsViewModelFactory, sceneInteractor = sceneInteractor, + unfoldTransitionInteractor = kosmos.unfoldTransitionInteractor, ) val displayTracker = FakeDisplayTracker(context) diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt index ab95e2c2e449..2397de69c063 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt @@ -24,6 +24,7 @@ import com.android.compose.animation.scene.SwipeDirection import com.android.systemui.SysuiTestCase import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository import com.android.systemui.authentication.shared.model.AuthenticationMethodModel +import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository import com.android.systemui.coroutines.collectLastValue import com.android.systemui.deviceentry.data.repository.fakeDeviceEntryRepository import com.android.systemui.deviceentry.domain.interactor.deviceEntryInteractor @@ -44,10 +45,14 @@ import com.android.systemui.shade.domain.startable.shadeStartable import com.android.systemui.shade.shared.model.ShadeMode import com.android.systemui.statusbar.notification.stack.ui.viewmodel.notificationsPlaceholderViewModel import com.android.systemui.testKosmos +import com.android.systemui.unfold.domain.interactor.unfoldTransitionInteractor +import com.android.systemui.unfold.fakeUnfoldTransitionProgressProvider import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat +import java.util.Locale import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.combine import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Before @@ -91,6 +96,7 @@ class ShadeSceneViewModelTest : SysuiTestCase() { footerActionsViewModelFactory = kosmos.footerActionsViewModelFactory, footerActionsController = kosmos.footerActionsController, sceneInteractor = kosmos.sceneInteractor, + unfoldTransitionInteractor = kosmos.unfoldTransitionInteractor, ) } @@ -254,4 +260,59 @@ class ShadeSceneViewModelTest : SysuiTestCase() { shadeRepository.setShadeMode(ShadeMode.Split) assertThat(shadeMode).isEqualTo(ShadeMode.Split) } + + @Test + fun unfoldTransitionProgress() = + testScope.runTest { + val maxTranslation = prepareConfiguration() + val translations by + collectLastValue( + combine( + underTest.unfoldTranslationX(isOnStartSide = true), + underTest.unfoldTranslationX(isOnStartSide = false), + ) { start, end -> + Translations( + start = start, + end = end, + ) + } + ) + + val unfoldProvider = kosmos.fakeUnfoldTransitionProgressProvider + unfoldProvider.onTransitionStarted() + assertThat(translations?.start).isEqualTo(0f) + assertThat(translations?.end).isEqualTo(-0f) + + repeat(10) { repetition -> + val transitionProgress = 0.1f * (repetition + 1) + unfoldProvider.onTransitionProgress(transitionProgress) + assertThat(translations?.start).isEqualTo((1 - transitionProgress) * maxTranslation) + assertThat(translations?.end).isEqualTo(-(1 - transitionProgress) * maxTranslation) + } + + unfoldProvider.onTransitionFinishing() + assertThat(translations?.start).isEqualTo(0f) + assertThat(translations?.end).isEqualTo(-0f) + + unfoldProvider.onTransitionFinished() + assertThat(translations?.start).isEqualTo(0f) + assertThat(translations?.end).isEqualTo(-0f) + } + + private fun prepareConfiguration(): Int { + val configuration = context.resources.configuration + configuration.setLayoutDirection(Locale.US) + kosmos.fakeConfigurationRepository.onConfigurationChange(configuration) + val maxTranslation = 10 + kosmos.fakeConfigurationRepository.setDimensionPixelSize( + R.dimen.notification_side_paddings, + maxTranslation + ) + return maxTranslation + } + + private data class Translations( + val start: Float, + val end: Float, + ) } diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/domain/interactor/UnfoldTransitionInteractorTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/domain/interactor/UnfoldTransitionInteractorTest.kt new file mode 100644 index 000000000000..12f08a343886 --- /dev/null +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/unfold/domain/interactor/UnfoldTransitionInteractorTest.kt @@ -0,0 +1,184 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ +package com.android.systemui.unfold.domain.interactor + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository +import com.android.systemui.coroutines.collectLastValue +import com.android.systemui.kosmos.testScope +import com.android.systemui.res.R +import com.android.systemui.testKosmos +import com.android.systemui.unfold.fakeUnfoldTransitionProgressProvider +import com.google.common.truth.Truth.assertThat +import java.util.Locale +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.async +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.test.runCurrent +import kotlinx.coroutines.test.runTest +import org.junit.Test +import org.junit.runner.RunWith + +@OptIn(ExperimentalCoroutinesApi::class) +@SmallTest +@RunWith(AndroidJUnit4::class) +class UnfoldTransitionInteractorTest : SysuiTestCase() { + + private val kosmos = testKosmos() + private val testScope = kosmos.testScope + private val unfoldTransitionProgressProvider = kosmos.fakeUnfoldTransitionProgressProvider + + private val underTest: UnfoldTransitionInteractor = kosmos.unfoldTransitionInteractor + + @Test + fun waitForTransitionFinish_noEvents_doesNotComplete() = + testScope.runTest { + val deferred = async { underTest.waitForTransitionFinish() } + + runCurrent() + + assertThat(deferred.isCompleted).isFalse() + deferred.cancel() + } + + @Test + fun waitForTransitionFinish_finishEvent_completes() = + testScope.runTest { + val deferred = async { underTest.waitForTransitionFinish() } + + runCurrent() + unfoldTransitionProgressProvider.onTransitionFinished() + runCurrent() + + assertThat(deferred.isCompleted).isTrue() + deferred.cancel() + } + + @Test + fun waitForTransitionFinish_otherEvent_doesNotComplete() = + testScope.runTest { + val deferred = async { underTest.waitForTransitionFinish() } + + runCurrent() + unfoldTransitionProgressProvider.onTransitionStarted() + runCurrent() + + assertThat(deferred.isCompleted).isFalse() + deferred.cancel() + } + + @Test + fun unfoldTranslationX_leftToRight() = + testScope.runTest { + val maxTranslation = prepareConfiguration(isLeftToRight = true) + val translations by + collectLastValue( + combine( + underTest.unfoldTranslationX(isOnStartSide = true), + underTest.unfoldTranslationX(isOnStartSide = false), + ) { start, end -> + Translations( + start = start, + end = end, + ) + } + ) + runCurrent() + + unfoldTransitionProgressProvider.onTransitionStarted() + assertThat(translations?.start).isEqualTo(0f) + assertThat(translations?.end).isEqualTo(-0f) + + repeat(10) { repetition -> + val transitionProgress = 1 - 0.1f * (repetition + 1) + unfoldTransitionProgressProvider.onTransitionProgress(transitionProgress) + assertThat(translations?.start).isEqualTo((1 - transitionProgress) * maxTranslation) + assertThat(translations?.end).isEqualTo(-(1 - transitionProgress) * maxTranslation) + } + + unfoldTransitionProgressProvider.onTransitionFinishing() + assertThat(translations?.start).isEqualTo(maxTranslation) + assertThat(translations?.end).isEqualTo(-maxTranslation) + + unfoldTransitionProgressProvider.onTransitionFinished() + assertThat(translations?.start).isEqualTo(0f) + assertThat(translations?.end).isEqualTo(-0f) + } + + @Test + fun unfoldTranslationX_rightToLeft() = + testScope.runTest { + val maxTranslation = prepareConfiguration(isLeftToRight = false) + val translations by + collectLastValue( + combine( + underTest.unfoldTranslationX(isOnStartSide = true), + underTest.unfoldTranslationX(isOnStartSide = false), + ) { start, end -> + Translations( + start = start, + end = end, + ) + } + ) + runCurrent() + + unfoldTransitionProgressProvider.onTransitionStarted() + assertThat(translations?.start).isEqualTo(-0f) + assertThat(translations?.end).isEqualTo(0f) + + repeat(10) { repetition -> + val transitionProgress = 1 - 0.1f * (repetition + 1) + unfoldTransitionProgressProvider.onTransitionProgress(transitionProgress) + assertThat(translations?.start) + .isEqualTo(-(1 - transitionProgress) * maxTranslation) + assertThat(translations?.end).isEqualTo((1 - transitionProgress) * maxTranslation) + } + + unfoldTransitionProgressProvider.onTransitionFinishing() + assertThat(translations?.start).isEqualTo(-maxTranslation) + assertThat(translations?.end).isEqualTo(maxTranslation) + + unfoldTransitionProgressProvider.onTransitionFinished() + assertThat(translations?.start).isEqualTo(-0f) + assertThat(translations?.end).isEqualTo(0f) + } + + private fun prepareConfiguration( + isLeftToRight: Boolean, + ): Int { + val configuration = context.resources.configuration + if (isLeftToRight) { + configuration.setLayoutDirection(Locale.US) + } else { + configuration.setLayoutDirection(Locale("he", "il")) + } + kosmos.fakeConfigurationRepository.onConfigurationChange(configuration) + val maxTranslation = 10 + kosmos.fakeConfigurationRepository.setDimensionPixelSize( + R.dimen.notification_side_paddings, + maxTranslation + ) + return maxTranslation + } + + private data class Translations( + val start: Float, + val end: Float, + ) +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/captioning/ui/viewmodel/CaptioningViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/captioning/ui/viewmodel/CaptioningViewModelTest.kt index fdeded8422d6..4cf924ad1609 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/captioning/ui/viewmodel/CaptioningViewModelTest.kt +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/volume/panel/component/captioning/ui/viewmodel/CaptioningViewModelTest.kt @@ -64,7 +64,7 @@ class CaptioningViewModelTest : SysuiTestCase() { val buttonViewModel by collectLastValue(underTest.buttonViewModel) runCurrent() - assertThat(buttonViewModel!!.isChecked).isFalse() + assertThat(buttonViewModel!!.isActive).isFalse() } } } @@ -78,7 +78,7 @@ class CaptioningViewModelTest : SysuiTestCase() { val buttonViewModel by collectLastValue(underTest.buttonViewModel) runCurrent() - assertThat(buttonViewModel!!.isChecked).isTrue() + assertThat(buttonViewModel!!.isActive).isTrue() } } } diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt index 8e2bd9b2562b..79bf5f19997b 100644 --- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt +++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/clocks/ClockProviderPlugin.kt @@ -267,6 +267,9 @@ data class ClockConfig( /** True if the clock will react to tone changes in the seed color. */ val isReactiveToTone: Boolean = true, + + /** True if the clock is large frame clock, which will use weather in compose. */ + val useCustomClockScene: Boolean = false, ) /** Render configuration options for a clock face. Modifies the way SystemUI behaves. */ @@ -283,6 +286,9 @@ data class ClockFaceConfig( * animation will be used (e.g. a simple translation). */ val hasCustomPositionUpdatedAnimation: Boolean = false, + + /** True if the clock is large frame clock, which will use weatherBlueprint in compose. */ + val useCustomClockScene: Boolean = false, ) /** Structure for keeping clock-specific settings */ diff --git a/packages/SystemUI/res-keyguard/values-es-rUS/strings.xml b/packages/SystemUI/res-keyguard/values-es-rUS/strings.xml index 45e2a9dc0ff5..bfb2ed0f421f 100644 --- a/packages/SystemUI/res-keyguard/values-es-rUS/strings.xml +++ b/packages/SystemUI/res-keyguard/values-es-rUS/strings.xml @@ -83,7 +83,7 @@ <string name="kg_primary_auth_locked_out_pin" msgid="5492230176361601475">"Demasiados intentos con PIN incorrecto"</string> <string name="kg_primary_auth_locked_out_pattern" msgid="8266214607346180952">"Demasiados intentos con patrón incorrecto"</string> <string name="kg_primary_auth_locked_out_password" msgid="6170245108400198659">"Demasiados intentos con contraseña incorrecta"</string> - <string name="kg_too_many_failed_attempts_countdown" msgid="2038195171919795529">"{count,plural, =1{Vuelve a intentarlo en # segundo.}many{Vuelve a intentarlo en # segundos.}other{Vuelve a intentarlo en # segundos.}}"</string> + <string name="kg_too_many_failed_attempts_countdown" msgid="2038195171919795529">"{count,plural, =1{Vuelve a intentarlo en # segundo}many{Vuelve a intentarlo en # segundos}other{Vuelve a intentarlo en # segundos}}"</string> <string name="kg_sim_pin_instructions" msgid="1942424305184242951">"Ingresa el PIN de la tarjeta SIM."</string> <string name="kg_sim_pin_instructions_multi" msgid="3639863309953109649">"Ingresa el PIN de la tarjeta SIM de \"<xliff:g id="CARRIER">%1$s</xliff:g>\"."</string> <string name="kg_sim_lock_esim_instructions" msgid="5577169988158738030">"<xliff:g id="PREVIOUS_MSG">%1$s</xliff:g> Inhabilita la tarjeta eSIM para usar el dispositivo sin servicio móvil."</string> diff --git a/packages/SystemUI/res-keyguard/values-hi/strings.xml b/packages/SystemUI/res-keyguard/values-hi/strings.xml index a762b49c320b..2b019039a01f 100644 --- a/packages/SystemUI/res-keyguard/values-hi/strings.xml +++ b/packages/SystemUI/res-keyguard/values-hi/strings.xml @@ -109,8 +109,8 @@ <string name="kg_prompt_reason_restart_pin" msgid="2672166323886110512">"डिवाइस रीस्टार्ट करने पर, पिन डालना ज़रूरी है"</string> <string name="kg_prompt_reason_restart_password" msgid="3967993994418885887">"डिवाइस रीस्टार्ट करने पर, पासवर्ड डालना ज़रूरी है"</string> <string name="kg_prompt_reason_timeout_pattern" msgid="5514969660010197363">"ज़्यादा सुरक्षा के लिए, इसके बजाय पैटर्न का इस्तेमाल करें"</string> - <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"ज़्यादा सुरक्षा के लिए, इसके बजाय पिन का इस्तेमाल करें"</string> - <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"ज़्यादा सुरक्षा के लिए, इसके बजाय पासवर्ड का इस्तेमाल करें"</string> + <string name="kg_prompt_reason_timeout_pin" msgid="4227962059353859376">"ज़्यादा सुरक्षा के लिए, पिन का इस्तेमाल करें"</string> + <string name="kg_prompt_reason_timeout_password" msgid="8810879144143933690">"ज़्यादा सुरक्षा के लिए, पासवर्ड का इस्तेमाल करें"</string> <string name="kg_prompt_added_security_pin" msgid="5487992065995475528">"अतिरिक्त सुरक्षा के लिए पिन ज़रूरी है"</string> <string name="kg_prompt_added_security_pattern" msgid="1017068086102168544">"अतिरिक्त सुरक्षा के लिए पैटर्न ज़रूरी है"</string> <string name="kg_prompt_added_security_password" msgid="6053156069765029006">"अतिरिक्त सुरक्षा के लिए पासवर्ड ज़रूरी है"</string> diff --git a/packages/SystemUI/res-keyguard/values-ja/strings.xml b/packages/SystemUI/res-keyguard/values-ja/strings.xml index 71969c00cc7c..9752eca46a97 100644 --- a/packages/SystemUI/res-keyguard/values-ja/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ja/strings.xml @@ -80,9 +80,9 @@ <string name="kg_face_locked_out" msgid="2751559491287575">"顔認証でロックを解除できません。何度もログインに失敗したためログインできません。"</string> <string name="kg_fp_locked_out" msgid="6228277682396768830">"指紋でロックを解除できません。何度もログインに失敗したためログインできません。"</string> <string name="kg_trust_agent_disabled" msgid="5400691179958727891">"信頼エージェントは利用できません"</string> - <string name="kg_primary_auth_locked_out_pin" msgid="5492230176361601475">"間違った PIN による試行回数が上限を超えました"</string> - <string name="kg_primary_auth_locked_out_pattern" msgid="8266214607346180952">"間違ったパターンによる試行回数が上限を超えました"</string> - <string name="kg_primary_auth_locked_out_password" msgid="6170245108400198659">"間違ったパスワードによる試行回数が上限を超えました"</string> + <string name="kg_primary_auth_locked_out_pin" msgid="5492230176361601475">"間違った PIN による試行回数が上限に達しました"</string> + <string name="kg_primary_auth_locked_out_pattern" msgid="8266214607346180952">"間違ったパターンによる試行回数が上限に達しました"</string> + <string name="kg_primary_auth_locked_out_password" msgid="6170245108400198659">"間違ったパスワードによる試行回数が上限に達しました"</string> <string name="kg_too_many_failed_attempts_countdown" msgid="2038195171919795529">"{count,plural, =1{# 秒後にもう一度お試しください。}other{# 秒後にもう一度お試しください。}}"</string> <string name="kg_sim_pin_instructions" msgid="1942424305184242951">"SIM PIN を入力してください。"</string> <string name="kg_sim_pin_instructions_multi" msgid="3639863309953109649">"「<xliff:g id="CARRIER">%1$s</xliff:g>」の SIM PIN を入力してください。"</string> diff --git a/packages/SystemUI/res-keyguard/values-km/strings.xml b/packages/SystemUI/res-keyguard/values-km/strings.xml index b816748ed5db..733e29c75de7 100644 --- a/packages/SystemUI/res-keyguard/values-km/strings.xml +++ b/packages/SystemUI/res-keyguard/values-km/strings.xml @@ -80,9 +80,9 @@ <string name="kg_face_locked_out" msgid="2751559491287575">"មិនអាចដោះសោដោយប្រើមុខទេ។ ព្យាយាមច្រើនដងពេក។"</string> <string name="kg_fp_locked_out" msgid="6228277682396768830">"មិនអាចដោះសោដោយប្រើស្នាមម្រាមដៃទេ។ ព្យាយាមច្រើនដងពេក។"</string> <string name="kg_trust_agent_disabled" msgid="5400691179958727891">"ភ្នាក់ងារទុកចិត្តមិនទំនេរទេ"</string> - <string name="kg_primary_auth_locked_out_pin" msgid="5492230176361601475">"ព្យាយាមច្រើនដងពេកដោយប្រើកូដ PIN មិនត្រឹមត្រូវ"</string> - <string name="kg_primary_auth_locked_out_pattern" msgid="8266214607346180952">"ព្យាយាមច្រើនដងពេកដោយប្រើលំនាំមិនត្រឹមត្រូវ"</string> - <string name="kg_primary_auth_locked_out_password" msgid="6170245108400198659">"ព្យាយាមច្រើនដងពេកដោយប្រើពាក្យសម្ងាត់មិនត្រឹមត្រូវ"</string> + <string name="kg_primary_auth_locked_out_pin" msgid="5492230176361601475">"ព្យាយាមដោយប្រើកូដ PIN មិនត្រឹមត្រូវច្រើនដងពេក"</string> + <string name="kg_primary_auth_locked_out_pattern" msgid="8266214607346180952">"ព្យាយាមដោយប្រើលំនាំមិនត្រឹមត្រូវច្រើនដងពេក"</string> + <string name="kg_primary_auth_locked_out_password" msgid="6170245108400198659">"ព្យាយាមដោយប្រើពាក្យសម្ងាត់មិនត្រឹមត្រូវច្រើនដងពេក"</string> <string name="kg_too_many_failed_attempts_countdown" msgid="2038195171919795529">"{count,plural, =1{ព្យាយាមម្តងទៀតក្នុងរយៈពេល # វិនាទីទៀត។}other{ព្យាយាមម្តងទៀតក្នុងរយៈពេល # វិនាទីទៀត។}}"</string> <string name="kg_sim_pin_instructions" msgid="1942424305184242951">"បញ្ចូលកូដ PIN របស់ស៊ីម។"</string> <string name="kg_sim_pin_instructions_multi" msgid="3639863309953109649">"បញ្ចូលកូដ PIN របស់ស៊ីមសម្រាប់ \"<xliff:g id="CARRIER">%1$s</xliff:g>\"។"</string> diff --git a/packages/SystemUI/res-keyguard/values-ky/strings.xml b/packages/SystemUI/res-keyguard/values-ky/strings.xml index 6d2f0e508acf..de53f9fa0098 100644 --- a/packages/SystemUI/res-keyguard/values-ky/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ky/strings.xml @@ -80,9 +80,9 @@ <string name="kg_face_locked_out" msgid="2751559491287575">"Жүз менен кулпусу ачылбай жатат. Өтө көп жолу аракет кылдыңыз."</string> <string name="kg_fp_locked_out" msgid="6228277682396768830">"Манжа изи менен кулпусу ачылбай жатат. Өтө көп жолу аракет кылдыңыз."</string> <string name="kg_trust_agent_disabled" msgid="5400691179958727891">"Ишеним агенти жеткиликсиз"</string> - <string name="kg_primary_auth_locked_out_pin" msgid="5492230176361601475">"Туура эмес PIN код менен өтө көп аракет"</string> - <string name="kg_primary_auth_locked_out_pattern" msgid="8266214607346180952">"Туура эмес графикалык ачкыч менен өтө көп аракет"</string> - <string name="kg_primary_auth_locked_out_password" msgid="6170245108400198659">"Туура эмес сырсөз менен өтө көп аракет"</string> + <string name="kg_primary_auth_locked_out_pin" msgid="5492230176361601475">"PIN код өтө көп жолу туура эмес киргизилди."</string> + <string name="kg_primary_auth_locked_out_pattern" msgid="8266214607346180952">"Графикалык ачкыч өтө көп жолу туура эмес тартылды."</string> + <string name="kg_primary_auth_locked_out_password" msgid="6170245108400198659">"Сырсөздү өтө көп жолу туура эмес киргиздиңиз."</string> <string name="kg_too_many_failed_attempts_countdown" msgid="2038195171919795529">"{count,plural, =1{# секунддан кийин кайталаңыз.}other{# секунддан кийин кайталаңыз.}}"</string> <string name="kg_sim_pin_instructions" msgid="1942424305184242951">"SIM-картанын PIN кодун киргизиңиз."</string> <string name="kg_sim_pin_instructions_multi" msgid="3639863309953109649">"\"<xliff:g id="CARRIER">%1$s</xliff:g>\" SIM-картасынын PIN кодун киргизиңиз."</string> diff --git a/packages/SystemUI/res-keyguard/values-ms/strings.xml b/packages/SystemUI/res-keyguard/values-ms/strings.xml index ecf843dd966c..0112d5d3d56d 100644 --- a/packages/SystemUI/res-keyguard/values-ms/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ms/strings.xml @@ -83,7 +83,7 @@ <string name="kg_primary_auth_locked_out_pin" msgid="5492230176361601475">"Terlalu banyak percubaan dengan PIN yang salah"</string> <string name="kg_primary_auth_locked_out_pattern" msgid="8266214607346180952">"Terlalu banyak percubaan dengan corak yang salah"</string> <string name="kg_primary_auth_locked_out_password" msgid="6170245108400198659">"Terlalu banyak percubaan dengan kata laluan yang salah"</string> - <string name="kg_too_many_failed_attempts_countdown" msgid="2038195171919795529">"{count,plural, =1{Cuba lagi dalam # saat.}other{Cuba lagi dalam # saat.}}"</string> + <string name="kg_too_many_failed_attempts_countdown" msgid="2038195171919795529">"{count,plural, =1{Cuba lagi selepas # saat.}other{Cuba lagi selepas # saat.}}"</string> <string name="kg_sim_pin_instructions" msgid="1942424305184242951">"Masukkan PIN SIM."</string> <string name="kg_sim_pin_instructions_multi" msgid="3639863309953109649">"Masukkan PIN SIM untuk \"<xliff:g id="CARRIER">%1$s</xliff:g>\"."</string> <string name="kg_sim_lock_esim_instructions" msgid="5577169988158738030">"<xliff:g id="PREVIOUS_MSG">%1$s</xliff:g> Lumpuhkan eSIM untuk menggunakan peranti tanpa perkhidmatan mudah alih."</string> diff --git a/packages/SystemUI/res-keyguard/values-ru/strings.xml b/packages/SystemUI/res-keyguard/values-ru/strings.xml index aa2d08042565..fd90d08caf9e 100644 --- a/packages/SystemUI/res-keyguard/values-ru/strings.xml +++ b/packages/SystemUI/res-keyguard/values-ru/strings.xml @@ -81,7 +81,7 @@ <string name="kg_fp_locked_out" msgid="6228277682396768830">"Превышен лимит попыток разблокировки отпечатком."</string> <string name="kg_trust_agent_disabled" msgid="5400691179958727891">"Агент доверия недоступен."</string> <string name="kg_primary_auth_locked_out_pin" msgid="5492230176361601475">"Слишком много неудачных попыток ввести PIN-код."</string> - <string name="kg_primary_auth_locked_out_pattern" msgid="8266214607346180952">"Слишком много неудачных попыток ввести граф. ключ."</string> + <string name="kg_primary_auth_locked_out_pattern" msgid="8266214607346180952">"Слишком много неудачных попыток ввести графический ключ."</string> <string name="kg_primary_auth_locked_out_password" msgid="6170245108400198659">"Слишком много неудачных попыток ввести пароль."</string> <string name="kg_too_many_failed_attempts_countdown" msgid="2038195171919795529">"{count,plural, =1{Повторите попытку через # секунду.}one{Повторите попытку через # секунду.}few{Повторите попытку через # секунды.}many{Повторите попытку через # секунд.}other{Повторите попытку через # секунды.}}"</string> <string name="kg_sim_pin_instructions" msgid="1942424305184242951">"Введите PIN-код SIM-карты."</string> diff --git a/packages/SystemUI/res-product/values-ar/strings.xml b/packages/SystemUI/res-product/values-ar/strings.xml index 4d4d8d06ac5e..0ddb911f44aa 100644 --- a/packages/SystemUI/res-product/values-ar/strings.xml +++ b/packages/SystemUI/res-product/values-ar/strings.xml @@ -34,10 +34,10 @@ <string name="kg_failed_attempts_almost_at_erase_user" product="default" msgid="8110939900089863103">"أخطأت في محاولة فتح قفل الهاتف <xliff:g id="NUMBER_0">%1$d</xliff:g> مرة. بعد <xliff:g id="NUMBER_1">%2$d</xliff:g> محاولة غير ناجحة أخرى، ستتم إزالة الملف الشخصي لهذا المستخدم، ومن ثم يتم حذف جميع بياناته."</string> <string name="kg_failed_attempts_now_erasing_user" product="tablet" msgid="8509811676952707883">"أخطأت في محاولة فتح قفل الجهاز اللوحي <xliff:g id="NUMBER">%d</xliff:g> مرة. ستتم إزالة الملف الشخصي لهذا المستخدم، ومن ثم يتم حذف جميع بياناته."</string> <string name="kg_failed_attempts_now_erasing_user" product="default" msgid="3051962486994265014">"أخطأت في محاولة فتح قفل الهاتف <xliff:g id="NUMBER">%d</xliff:g> مرة. ستتم إزالة الملف الشخصي لهذا المستخدم، ومن ثم يتم حذف جميع بياناته."</string> - <string name="kg_failed_attempts_almost_at_erase_profile" product="tablet" msgid="1049523640263353830">"أخطأت في محاولة فتح قفل الجهاز اللوحي <xliff:g id="NUMBER_0">%1$d</xliff:g> مرة. بعد <xliff:g id="NUMBER_1">%2$d</xliff:g> محاولة غير ناجحة أخرى، ستتم إزالة الملف الشخصي للعمل، ومن ثم يتم حذف جميع بيانات الملف الشخصي."</string> - <string name="kg_failed_attempts_almost_at_erase_profile" product="default" msgid="3280816298678433681">"أخطأت في محاولة فتح قفل الهاتف <xliff:g id="NUMBER_0">%1$d</xliff:g> مرة. بعد <xliff:g id="NUMBER_1">%2$d</xliff:g> محاولة غير ناجحة أخرى، ستتم إزالة الملف الشخصي للعمل، ومن ثم يتم حذف جميع بيانات الملف الشخصي."</string> - <string name="kg_failed_attempts_now_erasing_profile" product="tablet" msgid="4417100487251371559">"أخطأت في محاولة فتح قفل الجهاز اللوحي <xliff:g id="NUMBER">%d</xliff:g> مرة. ستتم إزالة الملف الشخصي للعمل، ومن ثم يتم حذف جميع بياناته."</string> - <string name="kg_failed_attempts_now_erasing_profile" product="default" msgid="4682221342671290678">"أخطأت في محاولة فتح قفل الهاتف <xliff:g id="NUMBER">%d</xliff:g> مرة. ستتم إزالة الملف الشخصي للعمل، ومن ثم يتم حذف جميع بياناته."</string> + <string name="kg_failed_attempts_almost_at_erase_profile" product="tablet" msgid="1049523640263353830">"أخطأت في محاولة فتح قفل الجهاز اللوحي <xliff:g id="NUMBER_0">%1$d</xliff:g> مرة. بعد <xliff:g id="NUMBER_1">%2$d</xliff:g> محاولة غير ناجحة أخرى، ستتم إزالة ملف العمل، ومن ثم يتم حذف جميع بيانات الملف الشخصي."</string> + <string name="kg_failed_attempts_almost_at_erase_profile" product="default" msgid="3280816298678433681">"أخطأت في محاولة فتح قفل الهاتف <xliff:g id="NUMBER_0">%1$d</xliff:g> مرة. بعد <xliff:g id="NUMBER_1">%2$d</xliff:g> محاولة غير ناجحة أخرى، ستتم إزالة ملف العمل، ومن ثم يتم حذف جميع بيانات الملف الشخصي."</string> + <string name="kg_failed_attempts_now_erasing_profile" product="tablet" msgid="4417100487251371559">"أخطأت في محاولة فتح قفل الجهاز اللوحي <xliff:g id="NUMBER">%d</xliff:g> مرة. ستتم إزالة ملف العمل، ومن ثم يتم حذف جميع بياناته."</string> + <string name="kg_failed_attempts_now_erasing_profile" product="default" msgid="4682221342671290678">"أخطأت في محاولة فتح قفل الهاتف <xliff:g id="NUMBER">%d</xliff:g> مرة. ستتم إزالة ملف العمل، ومن ثم يتم حذف جميع بياناته."</string> <string name="kg_failed_attempts_almost_at_login" product="tablet" msgid="1860049973474855672">"رسمت نقش فتح القفل بشكل غير صحيح <xliff:g id="NUMBER_0">%1$d</xliff:g> مرة. بعد إجراء <xliff:g id="NUMBER_1">%2$d</xliff:g> محاولة غير ناجحة أخرى، ستُطالَب بفتح قفل الجهاز اللوحي باستخدام معلومات حساب بريد إلكتروني.\n\n يُرجى إعادة المحاولة خلال <xliff:g id="NUMBER_2">%3$d</xliff:g> ثانية."</string> <string name="kg_failed_attempts_almost_at_login" product="default" msgid="44112553371516141">"رسمت نقش فتح القفل بشكل غير صحيح <xliff:g id="NUMBER_0">%1$d</xliff:g> مرة. بعد إجراء <xliff:g id="NUMBER_1">%2$d</xliff:g> محاولة غير ناجحة أخرى، ستُطالَب بفتح قفل الهاتف باستخدام حساب بريد إلكتروني.\n\n يُرجى إعادة المحاولة خلال <xliff:g id="NUMBER_2">%3$d</xliff:g> ثانية."</string> <string name="thermal_shutdown_title" product="default" msgid="8039593017174903505">"تم إطفاء الهاتف بسبب ارتفاع درجة حرارته"</string> diff --git a/packages/SystemUI/res/values-af/strings.xml b/packages/SystemUI/res/values-af/strings.xml index ccea0fcc11db..0e1bed8a0a21 100644 --- a/packages/SystemUI/res/values-af/strings.xml +++ b/packages/SystemUI/res/values-af/strings.xml @@ -270,12 +270,20 @@ <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Sien alles"</string> <string name="turn_on_bluetooth" msgid="5681370462180289071">"Gebruik Bluetooth"</string> <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Gekoppel"</string> + <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) --> + <skip /> <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Gestoor"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ontkoppel"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktiveer"</string> <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="8267380591344023327">"Kenmerke soos Kitsdeel, Kry My Toestel en toestelligging gebruik Bluetooth"</string> - <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth sal môre om 05:00 aanskakel"</string> + <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) --> + <skip /> + <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) --> + <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> diff --git a/packages/SystemUI/res/values-am/strings.xml b/packages/SystemUI/res/values-am/strings.xml index 9e528e0c9ac0..3709a9009f09 100644 --- a/packages/SystemUI/res/values-am/strings.xml +++ b/packages/SystemUI/res/values-am/strings.xml @@ -270,12 +270,20 @@ <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"ሁሉንም ይመልከቱ"</string> <string name="turn_on_bluetooth" msgid="5681370462180289071">"ብሉቱዝን ይጠቀሙ"</string> <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"ተገናኝቷል"</string> + <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) --> + <skip /> <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_info_disabled" msgid="8267380591344023327">"እንደ ፈጣን ማጋራት፣ የእኔን መሣሪያ አግኝ እና የመሣሪያ አካባቢ ያሉ ባህሪያት ብሉቱዝን ይጠቀማሉ"</string> - <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"ብሉቱዝ ነገ ጠዋቱ 5 ሰዓት ላይ ይበራል"</string> + <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) --> + <skip /> + <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) --> + <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> diff --git a/packages/SystemUI/res/values-ar/strings.xml b/packages/SystemUI/res/values-ar/strings.xml index 759f957c543a..0a7250ab56b5 100644 --- a/packages/SystemUI/res/values-ar/strings.xml +++ b/packages/SystemUI/res/values-ar/strings.xml @@ -75,7 +75,7 @@ <string name="global_action_smart_lock_disabled" msgid="6286551337177954859">"تم إيقاف ميزة \"إبقاء الجهاز مفتوحًا\"."</string> <string name="remote_input_image_insertion_text" msgid="4850791636452521123">"أرسَل صورة"</string> <string name="screenshot_saving_title" msgid="2298349784913287333">"جارٍ حفظ لقطة الشاشة..."</string> - <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"جارٍ حفظ لقطة الشاشة في الملف الشخصي للعمل…"</string> + <string name="screenshot_saving_work_profile_title" msgid="5332829607308450880">"جارٍ حفظ لقطة الشاشة في ملف العمل…"</string> <string name="screenshot_saved_title" msgid="8893267638659083153">"تم حفظ لقطة الشاشة."</string> <string name="screenshot_failed_title" msgid="3259148215671936891">"تعذّر حفظ لقطة الشاشة"</string> <string name="screenshot_failed_external_display_indication" msgid="6555673132061101936">"الشاشة الخارجية"</string> @@ -90,13 +90,13 @@ <string name="screenshot_share_description" msgid="2861628935812656612">"مشاركة لقطة الشاشة"</string> <string name="screenshot_scroll_label" msgid="2930198809899329367">"التقاط المزيد من المحتوى"</string> <string name="screenshot_dismiss_description" msgid="4702341245899508786">"إغلاق لقطة الشاشة"</string> - <string name="screenshot_dismiss_work_profile" msgid="3101530842987697045">"تجاهل رسالة الملف الشخصي للعمل"</string> + <string name="screenshot_dismiss_work_profile" msgid="3101530842987697045">"تجاهل رسالة ملف العمل"</string> <string name="screenshot_preview_description" msgid="7606510140714080474">"معاينة لقطة الشاشة"</string> <string name="screenshot_top_boundary_pct" msgid="2520148599096479332">"الحد العلوي <xliff:g id="PERCENT">%1$d</xliff:g> في المئة"</string> <string name="screenshot_bottom_boundary_pct" msgid="3880821519814946478">"الحد السفلى <xliff:g id="PERCENT">%1$d</xliff:g> في المئة"</string> <string name="screenshot_left_boundary_pct" msgid="8502323556112287469">"الحد الأيسر <xliff:g id="PERCENT">%1$d</xliff:g> في المئة"</string> <string name="screenshot_right_boundary_pct" msgid="1201150713021779321">"الحد الأيمن <xliff:g id="PERCENT">%1$d</xliff:g> في المئة"</string> - <string name="screenshot_work_profile_notification" msgid="203041724052970693">"تم حفظ لقطة الشاشة في \"<xliff:g id="APP">%1$s</xliff:g>\" في الملف الشخصي للعمل."</string> + <string name="screenshot_work_profile_notification" msgid="203041724052970693">"تم حفظ لقطة الشاشة في \"<xliff:g id="APP">%1$s</xliff:g>\" في ملف العمل."</string> <string name="screenshot_default_files_app_name" msgid="8721579578575161912">"الملفات"</string> <string name="screenshot_detected_template" msgid="7940376642921719915">"رصَد تطبيق \"<xliff:g id="APPNAME">%1$s</xliff:g>\" لقطة الشاشة هذه."</string> <string name="screenshot_detected_multiple_template" msgid="7644827792093819241">"رصَد تطبيق \"<xliff:g id="APPNAME">%1$s</xliff:g>\" والتطبيقات المفتوحة الأخرى لقطة الشاشة هذه."</string> @@ -270,12 +270,20 @@ <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"عرض الكل"</string> <string name="turn_on_bluetooth" msgid="5681370462180289071">"استخدام البلوتوث"</string> <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"متّصل"</string> + <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) --> + <skip /> <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_info_disabled" msgid="8267380591344023327">"يُستخدَم البلوتوث في ميزات مثل Quick Share و\"العثور على جهازي\" والموقع الجغرافي للجهاز"</string> - <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"سيتم تفعيل البلوتوث غدًا الساعة 5 صباحًا"</string> + <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) --> + <skip /> + <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) --> + <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> @@ -515,9 +523,9 @@ <string name="quick_settings_disclosure_named_management" msgid="3476472755775165827">"هذا الجهاز يخص <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>."</string> <string name="quick_settings_disclosure_management_vpns" msgid="929181757984262902">"ينتمي هذا الجهاز إلى مؤسستك، ويتّصل بالإنترنت من خلال خدمات الشبكة الافتراضية الخاصة (VPN)."</string> <string name="quick_settings_disclosure_named_management_vpns" msgid="3312645578322079185">"ينتمي هذا الجهاز إلى <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g>، ويتّصل بالإنترنت من خلال خدمات الشبكة الافتراضية الخاصة (VPN)."</string> - <string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"يمكن لمؤسستك مراقبة حركة بيانات الشبكة في الملف الشخصي للعمل"</string> + <string name="quick_settings_disclosure_managed_profile_monitoring" msgid="1423899084754272514">"يمكن لمؤسستك مراقبة حركة بيانات الشبكة في ملف العمل"</string> <string name="quick_settings_disclosure_named_managed_profile_monitoring" msgid="8321469176706219860">"يمكن لـ <xliff:g id="ORGANIZATION_NAME">%1$s</xliff:g> مراقبة حركة بيانات الشبكة في ملفك الشخصي للعمل"</string> - <string name="quick_settings_disclosure_managed_profile_network_activity" msgid="2636594621387832827">"تكون أنشطة شبكة الملف الشخصي للعمل مرئية لمشرف تكنولوجيا المعلومات."</string> + <string name="quick_settings_disclosure_managed_profile_network_activity" msgid="2636594621387832827">"تكون أنشطة شبكة ملف العمل مرئية لمشرف تكنولوجيا المعلومات."</string> <string name="quick_settings_disclosure_monitoring" msgid="8548019955631378680">"قد تكون الشبكة خاضعة للمراقبة"</string> <string name="quick_settings_disclosure_vpns" msgid="3586175303518266301">"هذا الجهاز متّصل بالإنترنت من خلال خدمات الشبكات الافتراضية الخاصة (VPN)."</string> <string name="quick_settings_disclosure_managed_profile_named_vpn" msgid="153393105176944100">"تطبيقات العمل الخاصة بك متّصلة بالإنترنت من خلال <xliff:g id="VPN_APP">%1$s</xliff:g>."</string> @@ -636,7 +644,7 @@ <string name="wallet_lockscreen_settings_label" msgid="3539105300870383570">"إعدادات شاشة القفل"</string> <string name="qr_code_scanner_title" msgid="1938155688725760702">"ماسح ضوئي لرمز الاستجابة السريعة"</string> <string name="qr_code_scanner_updating_secondary_label" msgid="8344598017007876352">"جارٍ تعديل الحالة"</string> - <string name="status_bar_work" msgid="5238641949837091056">"الملف الشخصي للعمل"</string> + <string name="status_bar_work" msgid="5238641949837091056">"ملف العمل"</string> <string name="status_bar_airplane" msgid="4848702508684541009">"وضع الطيران"</string> <string name="zen_alarm_warning" msgid="7844303238486849503">"لن تسمع المنبّه القادم في <xliff:g id="WHEN">%1$s</xliff:g>"</string> <string name="alarm_template" msgid="2234991538018805736">"في <xliff:g id="WHEN">%1$s</xliff:g>"</string> @@ -646,7 +654,7 @@ <string name="accessibility_status_bar_satellite_poor_connection" msgid="5231478574952724160">"قمر صناعي، الاتصال ضعيف"</string> <string name="accessibility_status_bar_satellite_good_connection" msgid="308079391708578704">"قمر صناعي، الاتصال جيد"</string> <string name="accessibility_status_bar_satellite_available" msgid="6514855015496916829">"قمر صناعي، الاتصال متوفّر"</string> - <string name="accessibility_managed_profile" msgid="4703836746209377356">"الملف الشخصي للعمل"</string> + <string name="accessibility_managed_profile" msgid="4703836746209377356">"ملف العمل"</string> <string name="tuner_warning_title" msgid="7721976098452135267">"متعة للبعض وليس للجميع"</string> <string name="tuner_warning" msgid="1861736288458481650">"توفر لك أداة ضبط واجهة مستخدم النظام طرقًا إضافية لتعديل واجهة مستخدم Android وتخصيصها. ويمكن أن تطرأ تغييرات على هذه الميزات التجريبية أو يمكن أن تتعطل هذه الميزات أو تختفي في الإصدارات المستقبلية. عليك متابعة الاستخدام مع توخي الحذر."</string> <string name="tuner_persistent_warning" msgid="230466285569307806">"يمكن أن تطرأ تغييرات على هذه الميزات التجريبية أو يمكن أن تتعطل هذه الميزات أو تختفي في الإصدارات المستقبلية. عليك متابعة الاستخدام مع توخي الحذر."</string> @@ -1262,8 +1270,8 @@ <string name="video_camera" msgid="7654002575156149298">"كاميرا فيديو"</string> <string name="call_from_work_profile_title" msgid="5418253516453177114">"لا يمكن الاتصال من تطبيق شخصي"</string> <string name="call_from_work_profile_text" msgid="2856337395968118274">"تسمح لك مؤسستك بإجراء المكالمات من تطبيقات العمل فقط."</string> - <string name="call_from_work_profile_action" msgid="2937701298133010724">"التبديل إلى الملف الشخصي للعمل"</string> - <string name="install_dialer_on_work_profile_action" msgid="2014659711597862506">"تثبيت تطبيق الهاتف في الملف الشخصي للعمل"</string> + <string name="call_from_work_profile_action" msgid="2937701298133010724">"التبديل إلى ملف العمل"</string> + <string name="install_dialer_on_work_profile_action" msgid="2014659711597862506">"تثبيت تطبيق الهاتف في ملف العمل"</string> <string name="call_from_work_profile_close" msgid="5830072964434474143">"إلغاء"</string> <string name="lock_screen_settings" msgid="6152703934761402399">"تخصيص شاشة القفل"</string> <string name="keyguard_unlock_to_customize_ls" msgid="2068542308086253819">"الفتح لتخصيص شاشة القفل"</string> diff --git a/packages/SystemUI/res/values-as/strings.xml b/packages/SystemUI/res/values-as/strings.xml index 1f859accbf55..3311588fcf89 100644 --- a/packages/SystemUI/res/values-as/strings.xml +++ b/packages/SystemUI/res/values-as/strings.xml @@ -270,12 +270,20 @@ <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"আটাইবোৰ চাওক"</string> <string name="turn_on_bluetooth" msgid="5681370462180289071">"ব্লুটুথ ব্যৱহাৰ কৰক"</string> <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"সংযুক্ত আছে"</string> + <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) --> + <skip /> <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_info_disabled" msgid="8267380591344023327">"Quick Share, Find My Device আৰু ডিভাইচৰ অৱস্থানৰ দৰে সুবিধাই ব্লুটুথ ব্যৱহাৰ কৰে"</string> - <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"কাইলৈ পুৱা ৫ বজাত ব্লুটুথ অন হ’ব"</string> + <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) --> + <skip /> + <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) --> + <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> diff --git a/packages/SystemUI/res/values-az/strings.xml b/packages/SystemUI/res/values-az/strings.xml index dc095c9169a3..741cc41ab1dd 100644 --- a/packages/SystemUI/res/values-az/strings.xml +++ b/packages/SystemUI/res/values-az/strings.xml @@ -270,12 +270,20 @@ <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Hamısına baxın"</string> <string name="turn_on_bluetooth" msgid="5681370462180289071">"Bluetooth aç"</string> <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Qoşulub"</string> + <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) --> + <skip /> <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Yadda saxlandı"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"əlaqəni kəsin"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktivləşdirin"</string> <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Sabah avtomatik aktiv edin"</string> - <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Cəld Paylaşım, Cihazın Tapılması və cihaz məkanı kimi funksiyalar Bluetooth istifadə edir"</string> - <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth sabah 05:00-da aktiv olacaq"</string> + <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) --> + <skip /> + <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) --> + <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> diff --git a/packages/SystemUI/res/values-b+sr+Latn/strings.xml b/packages/SystemUI/res/values-b+sr+Latn/strings.xml index 2f86ca4b3b8f..583ffaff2b9d 100644 --- a/packages/SystemUI/res/values-b+sr+Latn/strings.xml +++ b/packages/SystemUI/res/values-b+sr+Latn/strings.xml @@ -270,12 +270,20 @@ <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Prikaži sve"</string> <string name="turn_on_bluetooth" msgid="5681370462180289071">"Koristi Bluetooth"</string> <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Povezano"</string> + <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) --> + <skip /> <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Sačuvano"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"prekinite vezu"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktivirajte"</string> <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatski ponovo uključi sutra"</string> - <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Funkcije kao što su Quick Share, Pronađi moj uređaj i lokacija uređaja koriste Bluetooth"</string> - <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth će se uključiti sutra u 5:00"</string> + <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) --> + <skip /> + <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) --> + <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> diff --git a/packages/SystemUI/res/values-be/strings.xml b/packages/SystemUI/res/values-be/strings.xml index bc03d8c25213..50b407ff17e3 100644 --- a/packages/SystemUI/res/values-be/strings.xml +++ b/packages/SystemUI/res/values-be/strings.xml @@ -270,12 +270,20 @@ <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Паглядзець усе"</string> <string name="turn_on_bluetooth" msgid="5681370462180289071">"Выкарыстоўваць Bluetooth"</string> <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Падключана"</string> + <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) --> + <skip /> <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_info_disabled" msgid="8267380591344023327">"Bluetooth выкарыстоўваецца для вызначэння месцазнаходжання прылады, а таксама такімі функцыямі, як Хуткае абагульванне і Знайсці прыладу"</string> - <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth будзе ўключаны заўтра ў 5 гадзін раніцы"</string> + <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) --> + <skip /> + <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) --> + <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> @@ -350,8 +358,7 @@ <string name="qs_record_issue_label" msgid="8166290137285529059">"Запіс праблемы"</string> <string name="qs_record_issue_start" msgid="2979831312582567056">"Пачынайце"</string> <string name="qs_record_issue_stop" msgid="3531747965741982657">"Спыніцеся"</string> - <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) --> - <skip /> + <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Справаздача"</string> <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> diff --git a/packages/SystemUI/res/values-bg/strings.xml b/packages/SystemUI/res/values-bg/strings.xml index d80b8894dd18..3a283296dd9c 100644 --- a/packages/SystemUI/res/values-bg/strings.xml +++ b/packages/SystemUI/res/values-bg/strings.xml @@ -270,12 +270,20 @@ <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Преглед на всички"</string> <string name="turn_on_bluetooth" msgid="5681370462180289071">"Използване на Bluetooth"</string> <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Установена е връзка"</string> + <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) --> + <skip /> <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_info_disabled" msgid="8267380591344023327">"Bluetooth се използва от различни функции, като например „Бързо споделяне“, „Намиране на устройството ми“ и местоположението на устройството"</string> - <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth ще се включи утре в 5:00 ч."</string> + <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) --> + <skip /> + <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) --> + <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> @@ -350,8 +358,7 @@ <string name="qs_record_issue_label" msgid="8166290137285529059">"Записване на проблем"</string> <string name="qs_record_issue_start" msgid="2979831312582567056">"Стартиране"</string> <string name="qs_record_issue_stop" msgid="3531747965741982657">"Спиране"</string> - <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) --> - <skip /> + <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Сигнал за грешка"</string> <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> diff --git a/packages/SystemUI/res/values-bn/strings.xml b/packages/SystemUI/res/values-bn/strings.xml index 36b57ee2975b..ec704b8b8c32 100644 --- a/packages/SystemUI/res/values-bn/strings.xml +++ b/packages/SystemUI/res/values-bn/strings.xml @@ -270,12 +270,20 @@ <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"সব দেখুন"</string> <string name="turn_on_bluetooth" msgid="5681370462180289071">"ব্লুটুথ ব্যবহার করুন"</string> <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"কানেক্ট করা আছে"</string> + <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) --> + <skip /> <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_info_disabled" msgid="8267380591344023327">"\'দ্রুত শেয়ার\', \'Find My Device\' ও \'ডিভাইস লোকেশনের\' মতো ফিচার ব্লুটুথ ব্যবহার করে"</string> - <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"আগামীকাল ভোর ৫টায় ব্লুটুথ চালু হবে"</string> + <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) --> + <skip /> + <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) --> + <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> @@ -350,8 +358,7 @@ <string name="qs_record_issue_label" msgid="8166290137285529059">"রেকর্ডিংয়ে সমস্যা"</string> <string name="qs_record_issue_start" msgid="2979831312582567056">"শুরু করুন"</string> <string name="qs_record_issue_stop" msgid="3531747965741982657">"বন্ধ করুন"</string> - <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) --> - <skip /> + <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"সমস্যার রিপোর্ট"</string> <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> diff --git a/packages/SystemUI/res/values-bs/strings.xml b/packages/SystemUI/res/values-bs/strings.xml index 91acfa5b3a2b..fd77f144f128 100644 --- a/packages/SystemUI/res/values-bs/strings.xml +++ b/packages/SystemUI/res/values-bs/strings.xml @@ -270,12 +270,20 @@ <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Prikaži sve"</string> <string name="turn_on_bluetooth" msgid="5681370462180289071">"Koristi Bluetooth"</string> <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Povezano"</string> + <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) --> + <skip /> <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Sačuvano"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"prekid veze"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktiviranje"</string> <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatski uključi ponovo sutra"</string> - <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Funkcije kao što su Quick Share, Pronađi moj uređaj i lokacija uređaja koriste Bluetooth"</string> - <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth će se uključiti sutra u 5:00"</string> + <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) --> + <skip /> + <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) --> + <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> @@ -350,7 +358,7 @@ <string name="qs_record_issue_label" msgid="8166290137285529059">"Snimite problem"</string> <string name="qs_record_issue_start" msgid="2979831312582567056">"Pokrenite"</string> <string name="qs_record_issue_stop" msgid="3531747965741982657">"Zaustavite"</string> - <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Izvješće o pogrešci"</string> + <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Izvještaj o grešci"</string> <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> diff --git a/packages/SystemUI/res/values-ca/strings.xml b/packages/SystemUI/res/values-ca/strings.xml index 14456238cd63..aeb0ef0045a8 100644 --- a/packages/SystemUI/res/values-ca/strings.xml +++ b/packages/SystemUI/res/values-ca/strings.xml @@ -270,12 +270,20 @@ <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Mostra-ho tot"</string> <string name="turn_on_bluetooth" msgid="5681370462180289071">"Utilitza\'l"</string> <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Connectat"</string> + <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) --> + <skip /> <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Desat"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"desconnecta"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"activa"</string> <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="8267380591344023327">"Funcions com ara Quick Share, Troba el meu dispositiu i la ubicació del dispositiu utilitzen el Bluetooth"</string> - <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"El Bluetooth s\'activarà demà a les 5:00 h"</string> + <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) --> + <skip /> + <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) --> + <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> @@ -350,8 +358,7 @@ <string name="qs_record_issue_label" msgid="8166290137285529059">"Registra el problema"</string> <string name="qs_record_issue_start" msgid="2979831312582567056">"Inicia"</string> <string name="qs_record_issue_stop" msgid="3531747965741982657">"Atura"</string> - <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) --> - <skip /> + <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Informe d\'errors"</string> <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> @@ -604,7 +611,7 @@ <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Toca per silenciar."</string> <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"Control de soroll"</string> <string name="volume_panel_spatial_audio_title" msgid="3367048857932040660">"Àudio espacial"</string> - <string name="volume_panel_spatial_audio_off" msgid="4177490084606772989">"Desactiva"</string> + <string name="volume_panel_spatial_audio_off" msgid="4177490084606772989">"Desactivat"</string> <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Fix"</string> <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Seguiment del cap"</string> <string name="volume_ringer_change" msgid="3574969197796055532">"Toca per canviar el mode de timbre"</string> @@ -1303,5 +1310,5 @@ <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> - <string name="home_controls_dream_description" msgid="4644150952104035789">"Utilitza controls de la llar com a salvapantalles"</string> + <string name="home_controls_dream_description" msgid="4644150952104035789">"Utilitza controls de la llar com a estalvi de pantalla"</string> </resources> diff --git a/packages/SystemUI/res/values-cs/strings.xml b/packages/SystemUI/res/values-cs/strings.xml index 7b766abc30b5..891f836504cf 100644 --- a/packages/SystemUI/res/values-cs/strings.xml +++ b/packages/SystemUI/res/values-cs/strings.xml @@ -270,12 +270,20 @@ <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Zobrazit vše"</string> <string name="turn_on_bluetooth" msgid="5681370462180289071">"Použít Bluetooth"</string> <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Připojeno"</string> + <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) --> + <skip /> <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Uloženo"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"odpojit"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktivovat"</string> <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Zítra znovu automaticky zapnout"</string> - <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Funkce jako Quick Share, Najdi moje zařízení a vyhledávání zařízení používají Bluetooth"</string> - <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth se zapne zítra v 5:00"</string> + <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) --> + <skip /> + <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) --> + <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> @@ -350,8 +358,7 @@ <string name="qs_record_issue_label" msgid="8166290137285529059">"Zaznamenat problém"</string> <string name="qs_record_issue_start" msgid="2979831312582567056">"Spustit"</string> <string name="qs_record_issue_stop" msgid="3531747965741982657">"Ukončit"</string> - <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) --> - <skip /> + <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Zpráva o chybě"</string> <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> diff --git a/packages/SystemUI/res/values-da/strings.xml b/packages/SystemUI/res/values-da/strings.xml index 2100e275ed2a..5c721a14e0a7 100644 --- a/packages/SystemUI/res/values-da/strings.xml +++ b/packages/SystemUI/res/values-da/strings.xml @@ -270,12 +270,20 @@ <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Se alt"</string> <string name="turn_on_bluetooth" msgid="5681370462180289071">"Brug Bluetooth"</string> <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Der er oprettet forbindelse"</string> + <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) --> + <skip /> <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Gemt"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"afbryd forbindelse"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktivér"</string> <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="8267380591344023327">"Funktioner som f.eks. Quick Share, Find min enhed og enhedslokation anvender Bluetooth"</string> - <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth aktiveres i morgen kl. 5.00"</string> + <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) --> + <skip /> + <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) --> + <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> @@ -350,8 +358,7 @@ <string name="qs_record_issue_label" msgid="8166290137285529059">"Optag problem"</string> <string name="qs_record_issue_start" msgid="2979831312582567056">"Start"</string> <string name="qs_record_issue_stop" msgid="3531747965741982657">"Stop"</string> - <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) --> - <skip /> + <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Fejlrapport"</string> <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> diff --git a/packages/SystemUI/res/values-de/strings.xml b/packages/SystemUI/res/values-de/strings.xml index 6c4f4b5f7c91..510dba96c418 100644 --- a/packages/SystemUI/res/values-de/strings.xml +++ b/packages/SystemUI/res/values-de/strings.xml @@ -198,7 +198,7 @@ <string name="face_re_enroll_notification_title" msgid="1850838867718410520">"Entsperrung per Gesichtserkennung neu einrichten"</string> <string name="face_re_enroll_notification_name" msgid="7384545252206120659">"Entsperrung per Gesichtserkennung"</string> <string name="face_re_enroll_dialog_title" msgid="6392173708176069994">"Entsperrung per Gesichtserkennung einrichten"</string> - <string name="face_re_enroll_dialog_content" msgid="7353502359464038511">"Wenn du die Entsperrung per Gesichtserkennung neu einrichtest, wird dein aktuelles Gesichtsmodell gelöscht.\n\nDu musst diese Funktion neu einrichten, damit du dein Smartphone weiterhin mit deinem Gesicht entsperren kannst."</string> + <string name="face_re_enroll_dialog_content" msgid="7353502359464038511">"Wenn du die Entsperrung per Gesichtserkennung neu einrichtest, wird dein aktuelles Gesichtsmodell gelöscht.\n\nDu musst diese Funktion neu einrichten, damit du dein Smartphone weiterhin mithilfe der Gesichtserkennung entsperren kannst."</string> <string name="face_reenroll_failure_dialog_content" msgid="7073947334397236935">"Die Entsperrung per Gesichtserkennung konnte nicht eingerichtet werden. Gehe zu den Einstellungen und versuche es noch einmal."</string> <string name="fingerprint_dialog_touch_sensor" msgid="2817887108047658975">"Berühre den Fingerabdrucksensor"</string> <string name="fingerprint_dialog_authenticated_confirmation" msgid="1603899612957562862">"Tippe zum Fortfahren auf das Symbol „Entsperren“"</string> @@ -270,12 +270,20 @@ <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Alle anzeigen"</string> <string name="turn_on_bluetooth" msgid="5681370462180289071">"Bluetooth verwenden"</string> <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Verbunden"</string> + <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) --> + <skip /> <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Gespeichert"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"Verknüpfung aufheben"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktivieren"</string> <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Morgen automatisch wieder aktivieren"</string> - <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Für Funktionen wie Quick Share, „Mein Gerät finden“ und den Gerätestandort wird Bluetooth verwendet"</string> - <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth wird morgen um 5:00 Uhr aktiviert"</string> + <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) --> + <skip /> + <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) --> + <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> @@ -350,8 +358,7 @@ <string name="qs_record_issue_label" msgid="8166290137285529059">"Problem aufnehmen"</string> <string name="qs_record_issue_start" msgid="2979831312582567056">"Aufnahme starten"</string> <string name="qs_record_issue_stop" msgid="3531747965741982657">"Aufnahme beenden"</string> - <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) --> - <skip /> + <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Fehlerbericht"</string> <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> diff --git a/packages/SystemUI/res/values-el/strings.xml b/packages/SystemUI/res/values-el/strings.xml index 4c964de521fa..a8815363bb9c 100644 --- a/packages/SystemUI/res/values-el/strings.xml +++ b/packages/SystemUI/res/values-el/strings.xml @@ -270,12 +270,20 @@ <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Εμφάνιση όλων"</string> <string name="turn_on_bluetooth" msgid="5681370462180289071">"Χρήση Bluetooth"</string> <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Συνδέθηκε"</string> + <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) --> + <skip /> <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_info_disabled" msgid="8267380591344023327">"Λειτουργίες όπως το Quick Share, η Εύρεση συσκευής και η τοποθεσία της συσκευής χρησιμοποιούν Bluetooth"</string> - <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Το Bluetooth θα ενεργοποιηθεί αύριο στις 5 π.μ."</string> + <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) --> + <skip /> + <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) --> + <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> @@ -350,8 +358,7 @@ <string name="qs_record_issue_label" msgid="8166290137285529059">"Εγγραφή προβλήματος"</string> <string name="qs_record_issue_start" msgid="2979831312582567056">"Έναρξη"</string> <string name="qs_record_issue_stop" msgid="3531747965741982657">"Διακοπή"</string> - <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) --> - <skip /> + <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Αναφορά σφάλματος"</string> <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> diff --git a/packages/SystemUI/res/values-en-rAU/strings.xml b/packages/SystemUI/res/values-en-rAU/strings.xml index fc27f16f7a88..d2af8a6977dc 100644 --- a/packages/SystemUI/res/values-en-rAU/strings.xml +++ b/packages/SystemUI/res/values-en-rAU/strings.xml @@ -270,12 +270,20 @@ <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"See all"</string> <string name="turn_on_bluetooth" msgid="5681370462180289071">"Use Bluetooth"</string> <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Connected"</string> + <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) --> + <skip /> <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Saved"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"disconnect"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"activate"</string> <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatically turn on again tomorrow"</string> - <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Features like Quick Share, Find My Device and device location use Bluetooth"</string> - <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth will turn on tomorrow at 5.00 a.m."</string> + <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) --> + <skip /> + <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) --> + <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> @@ -350,8 +358,7 @@ <string name="qs_record_issue_label" msgid="8166290137285529059">"Record issue"</string> <string name="qs_record_issue_start" msgid="2979831312582567056">"Start"</string> <string name="qs_record_issue_stop" msgid="3531747965741982657">"Stop"</string> - <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) --> - <skip /> + <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Bug report"</string> <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"What part of your device experience was affected?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Select issue type"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Screen record"</string> diff --git a/packages/SystemUI/res/values-en-rCA/strings.xml b/packages/SystemUI/res/values-en-rCA/strings.xml index 088f75184889..836eefa6378c 100644 --- a/packages/SystemUI/res/values-en-rCA/strings.xml +++ b/packages/SystemUI/res/values-en-rCA/strings.xml @@ -270,12 +270,15 @@ <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"See all"</string> <string name="turn_on_bluetooth" msgid="5681370462180289071">"Use Bluetooth"</string> <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Connected"</string> + <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Audio Sharing"</string> <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Saved"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"disconnect"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"activate"</string> <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatically turn on again tomorrow"</string> - <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Features like Quick Share, Find My Device, and device location use Bluetooth"</string> - <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth will turn on tomorrow at 5 AM"</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_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> diff --git a/packages/SystemUI/res/values-en-rGB/strings.xml b/packages/SystemUI/res/values-en-rGB/strings.xml index fc27f16f7a88..d2af8a6977dc 100644 --- a/packages/SystemUI/res/values-en-rGB/strings.xml +++ b/packages/SystemUI/res/values-en-rGB/strings.xml @@ -270,12 +270,20 @@ <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"See all"</string> <string name="turn_on_bluetooth" msgid="5681370462180289071">"Use Bluetooth"</string> <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Connected"</string> + <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) --> + <skip /> <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Saved"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"disconnect"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"activate"</string> <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatically turn on again tomorrow"</string> - <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Features like Quick Share, Find My Device and device location use Bluetooth"</string> - <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth will turn on tomorrow at 5.00 a.m."</string> + <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) --> + <skip /> + <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) --> + <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> @@ -350,8 +358,7 @@ <string name="qs_record_issue_label" msgid="8166290137285529059">"Record issue"</string> <string name="qs_record_issue_start" msgid="2979831312582567056">"Start"</string> <string name="qs_record_issue_stop" msgid="3531747965741982657">"Stop"</string> - <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) --> - <skip /> + <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Bug report"</string> <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"What part of your device experience was affected?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Select issue type"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Screen record"</string> diff --git a/packages/SystemUI/res/values-en-rIN/strings.xml b/packages/SystemUI/res/values-en-rIN/strings.xml index fc27f16f7a88..d2af8a6977dc 100644 --- a/packages/SystemUI/res/values-en-rIN/strings.xml +++ b/packages/SystemUI/res/values-en-rIN/strings.xml @@ -270,12 +270,20 @@ <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"See all"</string> <string name="turn_on_bluetooth" msgid="5681370462180289071">"Use Bluetooth"</string> <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Connected"</string> + <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) --> + <skip /> <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Saved"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"disconnect"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"activate"</string> <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatically turn on again tomorrow"</string> - <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Features like Quick Share, Find My Device and device location use Bluetooth"</string> - <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth will turn on tomorrow at 5.00 a.m."</string> + <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) --> + <skip /> + <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) --> + <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> @@ -350,8 +358,7 @@ <string name="qs_record_issue_label" msgid="8166290137285529059">"Record issue"</string> <string name="qs_record_issue_start" msgid="2979831312582567056">"Start"</string> <string name="qs_record_issue_stop" msgid="3531747965741982657">"Stop"</string> - <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) --> - <skip /> + <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Bug report"</string> <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"What part of your device experience was affected?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Select issue type"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Screen record"</string> diff --git a/packages/SystemUI/res/values-en-rXC/strings.xml b/packages/SystemUI/res/values-en-rXC/strings.xml index 958c6456cd6f..77ef52e6e5a8 100644 --- a/packages/SystemUI/res/values-en-rXC/strings.xml +++ b/packages/SystemUI/res/values-en-rXC/strings.xml @@ -270,12 +270,15 @@ <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"See all"</string> <string name="turn_on_bluetooth" msgid="5681370462180289071">"Use Bluetooth"</string> <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Connected"</string> + <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Audio Sharing"</string> <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Saved"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"disconnect"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"activate"</string> <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatically turn on again tomorrow"</string> - <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Features like Quick Share, Find My Device, and device location use Bluetooth"</string> - <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth will turn on tomorrow at 5 AM"</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_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> diff --git a/packages/SystemUI/res/values-es-rUS/strings.xml b/packages/SystemUI/res/values-es-rUS/strings.xml index b625dc5cfd09..c33213aae5cf 100644 --- a/packages/SystemUI/res/values-es-rUS/strings.xml +++ b/packages/SystemUI/res/values-es-rUS/strings.xml @@ -270,12 +270,20 @@ <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Ver todos"</string> <string name="turn_on_bluetooth" msgid="5681370462180289071">"Usar Bluetooth"</string> <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Conectado"</string> + <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) --> + <skip /> <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Guardado"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"desconectar"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"activar"</string> <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="8267380591344023327">"Las funciones como Quick Share, Encontrar mi dispositivo y la ubicación del dispositivo usan Bluetooth"</string> - <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Se activará el Bluetooth mañana a las 5 a.m."</string> + <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) --> + <skip /> + <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) --> + <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> @@ -350,8 +358,7 @@ <string name="qs_record_issue_label" msgid="8166290137285529059">"Grabar error"</string> <string name="qs_record_issue_start" msgid="2979831312582567056">"Iniciar"</string> <string name="qs_record_issue_stop" msgid="3531747965741982657">"Detener"</string> - <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) --> - <skip /> + <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Informe de errores"</string> <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> @@ -1303,5 +1310,5 @@ <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 a controles de la casa como prot. de pant."</string> + <string name="home_controls_dream_description" msgid="4644150952104035789">"Accede rápidamente a controles de la casa como prot. de pantalla"</string> </resources> diff --git a/packages/SystemUI/res/values-es/strings.xml b/packages/SystemUI/res/values-es/strings.xml index 583a18116222..1dc93fde85be 100644 --- a/packages/SystemUI/res/values-es/strings.xml +++ b/packages/SystemUI/res/values-es/strings.xml @@ -270,12 +270,20 @@ <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Ver todos"</string> <string name="turn_on_bluetooth" msgid="5681370462180289071">"Usar Bluetooth"</string> <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Conectado"</string> + <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) --> + <skip /> <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Guardado"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"desconectar"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"activar"</string> <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="8267380591344023327">"Las funciones como Quick Share y Encontrar mi dispositivo, y la ubicación del dispositivo usan Bluetooth"</string> - <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"El Bluetooth se activará mañana a las 5:00"</string> + <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) --> + <skip /> + <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) --> + <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> @@ -350,8 +358,7 @@ <string name="qs_record_issue_label" msgid="8166290137285529059">"Problema de grabación"</string> <string name="qs_record_issue_start" msgid="2979831312582567056">"Iniciar"</string> <string name="qs_record_issue_stop" msgid="3531747965741982657">"Detener"</string> - <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) --> - <skip /> + <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Informe errores"</string> <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> @@ -479,7 +486,7 @@ <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"Cuando compartes, grabas o envías una aplicación, <xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> puede acceder a todo lo que se muestre o se reproduzca en ella. Debes tener cuidado con elementos como contraseñas, detalles de pagos, mensajes, fotos, audio y vídeo."</string> <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"Empezar"</string> <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"<xliff:g id="APP_NAME">%1$s</xliff:g> ha inhabilitado esta opción"</string> - <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"¿Empezar a enviar contenido?"</string> + <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"¿Empezar a enviar?"</string> <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"Cuando envías contenido, Android puede acceder a todo lo que se muestre en la pantalla o se reproduzca en tu dispositivo. Debes tener cuidado con elementos como contraseñas, detalles de pagos, mensajes, fotos, audio y vídeo."</string> <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"Cuando envías una aplicación, Android puede acceder a todo lo que se muestre o se reproduzca en ella. Debes tener cuidado con elementos como contraseñas, detalles de pagos, mensajes, fotos, audio y vídeo."</string> <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"Empezar a enviar"</string> diff --git a/packages/SystemUI/res/values-et/strings.xml b/packages/SystemUI/res/values-et/strings.xml index 190c002fb033..fea407db4682 100644 --- a/packages/SystemUI/res/values-et/strings.xml +++ b/packages/SystemUI/res/values-et/strings.xml @@ -270,12 +270,20 @@ <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Kuva kõik"</string> <string name="turn_on_bluetooth" msgid="5681370462180289071">"Kasuta Bluetoothi"</string> <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Ühendatud"</string> + <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) --> + <skip /> <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Salvestatud"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"katkesta ühendus"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktiveeri"</string> <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="8267380591344023327">"Funktsioonid, nagu Kiirjagamine, Leia mu seade ja seadme asukoht, kasutavad Bluetoothi"</string> - <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth lülitatakse sisse homme kell viis hommikul"</string> + <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) --> + <skip /> + <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) --> + <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> @@ -350,8 +358,7 @@ <string name="qs_record_issue_label" msgid="8166290137285529059">"Probleemi salvestamine"</string> <string name="qs_record_issue_start" msgid="2979831312582567056">"Alusta"</string> <string name="qs_record_issue_stop" msgid="3531747965741982657">"Peata"</string> - <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) --> - <skip /> + <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Veaaruanne"</string> <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> diff --git a/packages/SystemUI/res/values-eu/strings.xml b/packages/SystemUI/res/values-eu/strings.xml index b60df6f99b60..41de1d434c88 100644 --- a/packages/SystemUI/res/values-eu/strings.xml +++ b/packages/SystemUI/res/values-eu/strings.xml @@ -270,12 +270,20 @@ <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Ikusi guztiak"</string> <string name="turn_on_bluetooth" msgid="5681370462180289071">"Erabili Bluetootha"</string> <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Konektatuta"</string> + <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) --> + <skip /> <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Gordeta"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"deskonektatu"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktibatu"</string> <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Aktibatu automatikoki berriro bihar"</string> - <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Quick Share, Bilatu nire gailua, gailuaren kokapena eta beste eginbide batzuek Bluetootha darabilte"</string> - <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetootha bihar 05:00etan aktibatuko da"</string> + <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) --> + <skip /> + <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) --> + <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> @@ -350,8 +358,7 @@ <string name="qs_record_issue_label" msgid="8166290137285529059">"Arazo bat dago grabaketarekin"</string> <string name="qs_record_issue_start" msgid="2979831312582567056">"Hasi"</string> <string name="qs_record_issue_stop" msgid="3531747965741982657">"Gelditu"</string> - <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) --> - <skip /> + <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Akatsen txostena"</string> <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> @@ -735,7 +742,7 @@ <string name="keyboard_key_numpad_template" msgid="7316338238459991821">"Zenbaki-teklatuko <xliff:g id="NAME">%1$s</xliff:g>"</string> <string name="notif_inline_reply_remove_attachment_description" msgid="7954075334095405429">"Kendu eranskina"</string> <string name="keyboard_shortcut_group_system" msgid="1583416273777875970">"Sistema"</string> - <string name="keyboard_shortcut_group_system_home" msgid="7465138628692109907">"Hasierako pantaila"</string> + <string name="keyboard_shortcut_group_system_home" msgid="7465138628692109907">"Orri nagusia"</string> <string name="keyboard_shortcut_group_system_recents" msgid="8628108256824616927">"Azkenaldikoak"</string> <string name="keyboard_shortcut_group_system_back" msgid="1055709713218453863">"Atzera"</string> <string name="keyboard_shortcut_group_system_notifications" msgid="3615971650562485878">"Jakinarazpenak"</string> @@ -1302,6 +1309,6 @@ <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"<xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>) aplikazioak erabili du duela gutxi"</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 gailuak kontrolatzeko aukerak"</string> - <string name="home_controls_dream_description" msgid="4644150952104035789">"Atzitu etxeko gailuak kontrolatzeko aukerak pantaila-babesletik"</string> + <string name="home_controls_dream_label" msgid="6567105701292324257">"Etxeko gailuen kontrola"</string> + <string name="home_controls_dream_description" msgid="4644150952104035789">"Kontrolatu etxeko gailuak pantaila-babesletik"</string> </resources> diff --git a/packages/SystemUI/res/values-fa/strings.xml b/packages/SystemUI/res/values-fa/strings.xml index 0c53fa01d903..8e72450809a4 100644 --- a/packages/SystemUI/res/values-fa/strings.xml +++ b/packages/SystemUI/res/values-fa/strings.xml @@ -270,12 +270,20 @@ <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"دیدن همه"</string> <string name="turn_on_bluetooth" msgid="5681370462180289071">"استفاده از بلوتوث"</string> <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"متصل"</string> + <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) --> + <skip /> <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_info_disabled" msgid="8267380591344023327">"ویژگیهایی مثل «همرسانی سریع»، «پیدا کردن دستگاهم»، و مکان دستگاه از بلوتوث استفاده میکنند"</string> - <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"بلوتوث فردا ۵ ق.ظ روشن خواهد شد"</string> + <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) --> + <skip /> + <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) --> + <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> @@ -1301,6 +1309,6 @@ <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"اخیراً <xliff:g id="APP_NAME">%1$s</xliff:g> از آن استفاده کرده است (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</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> - <string name="home_controls_dream_description" msgid="4644150952104035789">"دسترسی سریع به کنترلهای لوازم خانگی بهعنوان محافظ صفحهنمایش"</string> + <string name="home_controls_dream_label" msgid="6567105701292324257">"کنترل خانه هوشمند"</string> + <string name="home_controls_dream_description" msgid="4644150952104035789">"به کنترل خانه هوشمند بهعنوان محافظ صفحهنمایش دسترسی سریع دارید"</string> </resources> diff --git a/packages/SystemUI/res/values-fi/strings.xml b/packages/SystemUI/res/values-fi/strings.xml index 3c90847483cc..2935d2e47ce9 100644 --- a/packages/SystemUI/res/values-fi/strings.xml +++ b/packages/SystemUI/res/values-fi/strings.xml @@ -270,12 +270,20 @@ <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Näytä kaikki"</string> <string name="turn_on_bluetooth" msgid="5681370462180289071">"Käytä Bluetoothia"</string> <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Yhdistetty"</string> + <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) --> + <skip /> <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Tallennettu"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"katkaise yhteys"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktivoi"</string> <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="8267380591344023327">"Ominaisuudet (esim. Quick Share ja Paikanna laite) ja laitteen sijainti käyttävät Bluetoothia"</string> - <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth käynnistetään huomenna klo 5.00"</string> + <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) --> + <skip /> + <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) --> + <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> @@ -350,8 +358,7 @@ <string name="qs_record_issue_label" msgid="8166290137285529059">"Tallenna ongelma"</string> <string name="qs_record_issue_start" msgid="2979831312582567056">"Aloita"</string> <string name="qs_record_issue_stop" msgid="3531747965741982657">"Lopeta"</string> - <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) --> - <skip /> + <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Virheraportti"</string> <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> diff --git a/packages/SystemUI/res/values-fr-rCA/strings.xml b/packages/SystemUI/res/values-fr-rCA/strings.xml index cb56ba93b2bd..bd53d8509b86 100644 --- a/packages/SystemUI/res/values-fr-rCA/strings.xml +++ b/packages/SystemUI/res/values-fr-rCA/strings.xml @@ -270,12 +270,20 @@ <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Tout afficher"</string> <string name="turn_on_bluetooth" msgid="5681370462180289071">"Utiliser le Bluetooth"</string> <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Connecté"</string> + <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) --> + <skip /> <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Enregistré"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"Déconnecter"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"Activer"</string> <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Activer le Bluetooth automatiquement demain"</string> - <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Les fonctionnalités comme le Partage rapide, Localiser mon appareil et la position de l\'appareil utilisent le Bluetooth"</string> - <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Le Bluetooth s\'activera demain à 5 h"</string> + <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) --> + <skip /> + <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) --> + <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> @@ -350,8 +358,7 @@ <string name="qs_record_issue_label" msgid="8166290137285529059">"Rapporter le problème"</string> <string name="qs_record_issue_start" msgid="2979831312582567056">"Commencer"</string> <string name="qs_record_issue_stop" msgid="3531747965741982657">"Arrêter"</string> - <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) --> - <skip /> + <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Rapport de bogue"</string> <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> @@ -1303,5 +1310,5 @@ <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> - <string name="home_controls_dream_description" msgid="4644150952104035789">"Accès rapide : domot. sous forme d\'Écran de veille"</string> + <string name="home_controls_dream_description" msgid="4644150952104035789">"Accès rapide : domotique sous forme d\'Écran de veille"</string> </resources> diff --git a/packages/SystemUI/res/values-fr/strings.xml b/packages/SystemUI/res/values-fr/strings.xml index 15235dee3724..109e76775aa5 100644 --- a/packages/SystemUI/res/values-fr/strings.xml +++ b/packages/SystemUI/res/values-fr/strings.xml @@ -270,12 +270,20 @@ <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Tout afficher"</string> <string name="turn_on_bluetooth" msgid="5681370462180289071">"Utiliser le Bluetooth"</string> <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Connecté"</string> + <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) --> + <skip /> <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Enregistré"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"dissocier"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"activer"</string> <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Réactiver automatiquement demain"</string> - <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Certaines fonctionnalités telles que Quick Share, Localiser mon appareil ou encore la position de l\'appareil utilisent le Bluetooth"</string> - <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Le Bluetooth sera activé demain à 5h00"</string> + <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) --> + <skip /> + <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) --> + <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> @@ -350,8 +358,7 @@ <string name="qs_record_issue_label" msgid="8166290137285529059">"Enregistrer le problème"</string> <string name="qs_record_issue_start" msgid="2979831312582567056">"Début"</string> <string name="qs_record_issue_stop" msgid="3531747965741982657">"Arrêter"</string> - <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) --> - <skip /> + <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Rapport de bug"</string> <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> diff --git a/packages/SystemUI/res/values-gl/strings.xml b/packages/SystemUI/res/values-gl/strings.xml index 7f4ae81e6f6e..06c984a24c2a 100644 --- a/packages/SystemUI/res/values-gl/strings.xml +++ b/packages/SystemUI/res/values-gl/strings.xml @@ -270,12 +270,20 @@ <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Ver todo"</string> <string name="turn_on_bluetooth" msgid="5681370462180289071">"Usar Bluetooth"</string> <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Estableceuse a conexión"</string> + <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) --> + <skip /> <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Gardouse"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"desconectar"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"activar"</string> <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Volver activar automaticamente mañá"</string> - <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"As funcións como Quick Share, Localizar o meu dispositivo ou a de localización do dispositivo utilizan o Bluetooth"</string> - <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"O Bluetooth activarase mañá ás 05:00"</string> + <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) --> + <skip /> + <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) --> + <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> @@ -350,8 +358,7 @@ <string name="qs_record_issue_label" msgid="8166290137285529059">"Rexistrar problema"</string> <string name="qs_record_issue_start" msgid="2979831312582567056">"Iniciar"</string> <string name="qs_record_issue_stop" msgid="3531747965741982657">"Deter"</string> - <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) --> - <skip /> + <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Informe de erros"</string> <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> @@ -1303,5 +1310,5 @@ <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> - <string name="home_controls_dream_description" msgid="4644150952104035789">"Controis domóticos como protector de pantalla"</string> + <string name="home_controls_dream_description" msgid="4644150952104035789">"Usa os controis domóticos como protector de pantalla"</string> </resources> diff --git a/packages/SystemUI/res/values-gu/strings.xml b/packages/SystemUI/res/values-gu/strings.xml index 9425774f47c3..c9a56627902c 100644 --- a/packages/SystemUI/res/values-gu/strings.xml +++ b/packages/SystemUI/res/values-gu/strings.xml @@ -270,12 +270,20 @@ <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"તમામ જુઓ"</string> <string name="turn_on_bluetooth" msgid="5681370462180289071">"બ્લૂટૂથનો ઉપયોગ કરો"</string> <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"કનેક્ટેડ છે"</string> + <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) --> + <skip /> <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_info_disabled" msgid="8267380591344023327">"ક્વિક શેર, Find My Device અને ડિવાઇસના લોકેશન જેવી સુવિધાઓ બ્લૂટૂથનો ઉપયોગ કરે છે"</string> - <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"બ્લૂટૂથ આવતીકાલે સવારે 5 વાગ્યે ચાલુ થશે"</string> + <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) --> + <skip /> + <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) --> + <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> diff --git a/packages/SystemUI/res/values-hi/strings.xml b/packages/SystemUI/res/values-hi/strings.xml index e4291aca6c8a..b2b20370d50a 100644 --- a/packages/SystemUI/res/values-hi/strings.xml +++ b/packages/SystemUI/res/values-hi/strings.xml @@ -270,12 +270,20 @@ <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"सभी देखें"</string> <string name="turn_on_bluetooth" msgid="5681370462180289071">"ब्लूटूथ इस्तेमाल करें"</string> <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"कनेक्ट है"</string> + <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) --> + <skip /> <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_info_disabled" msgid="8267380591344023327">"क्विक शेयर, Find My Device, और डिवाइस की जगह की जानकारी का पता लगाने जैसी सुविधाएं, ब्लूटूथ का इस्तेमाल करती हैं"</string> - <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"कल सुबह 5 बजे ब्लूटूथ चालू हो जाएगा"</string> + <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) --> + <skip /> + <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) --> + <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> @@ -1191,7 +1199,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> diff --git a/packages/SystemUI/res/values-hr/strings.xml b/packages/SystemUI/res/values-hr/strings.xml index e0d2478206c4..64c35b8665f7 100644 --- a/packages/SystemUI/res/values-hr/strings.xml +++ b/packages/SystemUI/res/values-hr/strings.xml @@ -270,19 +270,27 @@ <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Pogledajte sve"</string> <string name="turn_on_bluetooth" msgid="5681370462180289071">"Koristi Bluetooth"</string> <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Povezano"</string> + <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) --> + <skip /> <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Spremljeno"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"prekini vezu"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktiviraj"</string> <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatski ponovo uključi sutra"</string> - <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Značajke kao što su brzo dijeljenje, Pronađi moj uređaj i lokacija uređaja upotrebljavaju Bluetooth"</string> - <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth će se uključiti sutra u 5:00"</string> + <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) --> + <skip /> + <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) --> + <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> <string name="quick_settings_bluetooth_secondary_label_input" msgid="3887552721233148132">"Unos"</string> <string name="quick_settings_bluetooth_secondary_label_hearing_aids" msgid="5553051568867097111">"Slušna pomagala"</string> <string name="quick_settings_bluetooth_secondary_label_transient" msgid="3882884317600669650">"Uključivanje…"</string> - <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Autom. zakretanje"</string> + <string name="quick_settings_rotation_unlocked_label" msgid="2359922767950346112">"Automatsko zakretanje"</string> <string name="accessibility_quick_settings_rotation" msgid="4800050198392260738">"Automatsko zakretanje zaslona"</string> <string name="quick_settings_location_label" msgid="2621868789013389163">"Lokacija"</string> <string name="quick_settings_screensaver_label" msgid="1495003469366524120">"Čuvar zaslona"</string> @@ -1301,6 +1309,6 @@ <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Nedavno koristila aplikacija <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</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">"Upr. kuć. uređ."</string> - <string name="home_controls_dream_description" msgid="4644150952104035789">"Brzo pristupajte Upr. kuć. uređ. kao čuvaru zasl."</string> + <string name="home_controls_dream_label" msgid="6567105701292324257">"Upravljanje uređajima"</string> + <string name="home_controls_dream_description" msgid="4644150952104035789">"Brzo upravljajte uređajima putem čuvara zaslona"</string> </resources> diff --git a/packages/SystemUI/res/values-hu/strings.xml b/packages/SystemUI/res/values-hu/strings.xml index 7acfbe62e950..96c3e83aaa39 100644 --- a/packages/SystemUI/res/values-hu/strings.xml +++ b/packages/SystemUI/res/values-hu/strings.xml @@ -270,12 +270,20 @@ <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Összes megtekintése"</string> <string name="turn_on_bluetooth" msgid="5681370462180289071">"Bluetooth használata"</string> <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Csatlakozva"</string> + <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) --> + <skip /> <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Mentve"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"leválasztás"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktiválás"</string> <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatikus visszakapcsolás holnap"</string> - <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Az olyan funkciók, mint a Quick Share, a Készülékkereső és az eszköz helyadatai Bluetootht használnak"</string> - <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"A Bluetooth bekapcsol holnap reggel 5-kor"</string> + <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) --> + <skip /> + <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) --> + <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> @@ -350,8 +358,7 @@ <string name="qs_record_issue_label" msgid="8166290137285529059">"Probléma rögzítése"</string> <string name="qs_record_issue_start" msgid="2979831312582567056">"Indítás"</string> <string name="qs_record_issue_stop" msgid="3531747965741982657">"Leállítás"</string> - <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) --> - <skip /> + <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Hibajelentés"</string> <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> diff --git a/packages/SystemUI/res/values-hy/strings.xml b/packages/SystemUI/res/values-hy/strings.xml index 7dc33b604b83..00c33184591c 100644 --- a/packages/SystemUI/res/values-hy/strings.xml +++ b/packages/SystemUI/res/values-hy/strings.xml @@ -270,12 +270,20 @@ <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Տեսնել բոլորը"</string> <string name="turn_on_bluetooth" msgid="5681370462180289071">"Միացնել Bluetooth-ը"</string> <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Միացված է"</string> + <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) --> + <skip /> <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_info_disabled" msgid="8267380591344023327">"Գործառույթները, ինչպիսիք են Quick Share-ը, «Գտնել իմ սարքը» գործառույթը և սարքի տեղորոշումը, օգտագործում են Bluetooth-ը"</string> - <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth-ը կմիանա վաղը՝ ժամը 05։00-ին"</string> + <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) --> + <skip /> + <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) --> + <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> @@ -350,8 +358,7 @@ <string name="qs_record_issue_label" msgid="8166290137285529059">"Ձայնագրել"</string> <string name="qs_record_issue_start" msgid="2979831312582567056">"Սկսել"</string> <string name="qs_record_issue_stop" msgid="3531747965741982657">"Կանգնեցնել"</string> - <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) --> - <skip /> + <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Հաղորդում սխալի մասին"</string> <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> @@ -1303,5 +1310,5 @@ <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> - <string name="home_controls_dream_description" msgid="4644150952104035789">"Օգտագործեք տան կառավարման տարրերը որպես էկրանապահ"</string> + <string name="home_controls_dream_description" msgid="4644150952104035789">"Տան կառավարման տարրերը դարձրեք էկրանապահ"</string> </resources> diff --git a/packages/SystemUI/res/values-in/strings.xml b/packages/SystemUI/res/values-in/strings.xml index 3da9a9024612..826fc1a48fa3 100644 --- a/packages/SystemUI/res/values-in/strings.xml +++ b/packages/SystemUI/res/values-in/strings.xml @@ -270,12 +270,20 @@ <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Lihat semua"</string> <string name="turn_on_bluetooth" msgid="5681370462180289071">"Gunakan Bluetooth"</string> <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Terhubung"</string> + <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) --> + <skip /> <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Disimpan"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"putuskan koneksi"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktifkan"</string> <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Otomatis aktifkan lagi besok"</string> - <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Fitur seperti Quick Share, Temukan Perangkat Saya, dan lokasi perangkat menggunakan Bluetooth"</string> - <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth akan diaktifkan besok pada pukul 05.00"</string> + <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) --> + <skip /> + <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) --> + <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> diff --git a/packages/SystemUI/res/values-is/strings.xml b/packages/SystemUI/res/values-is/strings.xml index b400f8cec8c0..1a0b3f29281d 100644 --- a/packages/SystemUI/res/values-is/strings.xml +++ b/packages/SystemUI/res/values-is/strings.xml @@ -270,12 +270,20 @@ <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Sjá allt"</string> <string name="turn_on_bluetooth" msgid="5681370462180289071">"Nota Bluetooth"</string> <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Tengt"</string> + <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) --> + <skip /> <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Vistað"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"aftengja"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"virkja"</string> <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Kveikja sjálfkrafa aftur á morgun"</string> - <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Eiginleikar á borð við flýtideilingu, „Finna tækið mitt“ og staðsetningu tækis nota Bluetooth"</string> - <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Kveikt verður á Bluetooth á morgun kl. 05:00"</string> + <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) --> + <skip /> + <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) --> + <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> @@ -1302,5 +1310,5 @@ <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> - <string name="home_controls_dream_description" msgid="4644150952104035789">"Fáðu skjótan aðgang að heimastýringum með því að stilla þær sem skjávara"</string> + <string name="home_controls_dream_description" msgid="4644150952104035789">"Fáðu skjótan aðgang að heimastýringum sem skjávara"</string> </resources> diff --git a/packages/SystemUI/res/values-it/strings.xml b/packages/SystemUI/res/values-it/strings.xml index aa7f15229c95..d7e5cdc772d6 100644 --- a/packages/SystemUI/res/values-it/strings.xml +++ b/packages/SystemUI/res/values-it/strings.xml @@ -270,12 +270,20 @@ <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Visualizza tutti"</string> <string name="turn_on_bluetooth" msgid="5681370462180289071">"Usa Bluetooth"</string> <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Dispositivo connesso"</string> + <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) --> + <skip /> <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Dispositivo salvato"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"disconnetti"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"attiva"</string> <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Riattiva automaticamente domani"</string> - <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Funzionalità come Quick Share, Trova il mio dispositivo e la posizione del dispositivo usano il Bluetooth"</string> - <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Il Bluetooth verrà attivato domani alle 05:00"</string> + <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) --> + <skip /> + <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) --> + <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> @@ -350,8 +358,7 @@ <string name="qs_record_issue_label" msgid="8166290137285529059">"Registra problema"</string> <string name="qs_record_issue_start" msgid="2979831312582567056">"Avvia"</string> <string name="qs_record_issue_stop" msgid="3531747965741982657">"Interrompi"</string> - <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) --> - <skip /> + <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Segnalazione di bug"</string> <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Quali problemi ha l\'esperienza del dispositivo?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Seleziona il tipo di problema"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Regis. dello schermo"</string> @@ -1303,5 +1310,5 @@ <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> - <string name="home_controls_dream_description" msgid="4644150952104035789">"Accedi ai controlli della casa dal salvaschermo"</string> + <string name="home_controls_dream_description" msgid="4644150952104035789">"Accedi rapidamente ai controlli della casa dal salvaschermo"</string> </resources> diff --git a/packages/SystemUI/res/values-iw/strings.xml b/packages/SystemUI/res/values-iw/strings.xml index a804271054f9..bed2fc746761 100644 --- a/packages/SystemUI/res/values-iw/strings.xml +++ b/packages/SystemUI/res/values-iw/strings.xml @@ -270,12 +270,20 @@ <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"הצגת הכול"</string> <string name="turn_on_bluetooth" msgid="5681370462180289071">"Bluetooth"</string> <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"מחובר"</string> + <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) --> + <skip /> <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_info_disabled" msgid="8267380591344023327">"תכונות כמו \'שיתוף מהיר\', \'איפה המכשיר שלי\' ומיקום המכשיר משתמשות בחיבור Bluetooth"</string> - <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth יופעל מחר בשעה 5 בבוקר"</string> + <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) --> + <skip /> + <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) --> + <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> @@ -350,8 +358,7 @@ <string name="qs_record_issue_label" msgid="8166290137285529059">"תיעוד הבעיה"</string> <string name="qs_record_issue_start" msgid="2979831312582567056">"התחלה"</string> <string name="qs_record_issue_stop" msgid="3531747965741982657">"עצירה"</string> - <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) --> - <skip /> + <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"דיווח על באג"</string> <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> @@ -479,10 +486,10 @@ <string name="media_projection_entry_app_permission_dialog_warning_single_app" msgid="5211695779082563959">"בזמן שיתוף, הקלטה או העברה (cast) של אפליקציה, תהיה ל-<xliff:g id="APP_SEEKING_PERMISSION">%s</xliff:g> גישה לכל מה שגלוי באפליקציה או מופעל מהאפליקציה. מומלץ להיזהר עם סיסמאות, פרטי תשלום, הודעות, תמונות, אודיו וסרטונים."</string> <string name="media_projection_entry_app_permission_dialog_continue" msgid="295463518195075840">"התחלה"</string> <string name="media_projection_entry_app_permission_dialog_single_app_disabled" msgid="8999903044874669995">"האפליקציה <xliff:g id="APP_NAME">%1$s</xliff:g> השביתה את האפשרות הזו"</string> - <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"להתחיל את ההעברה?"</string> + <string name="media_projection_entry_cast_permission_dialog_title" msgid="8860150223172993547">"להפעיל Cast?"</string> <string name="media_projection_entry_cast_permission_dialog_warning_entire_screen" msgid="1986212276016817231">"בזמן העברה (cast), תהיה ל-Android גישה לכל הפרטים שגלויים במסך שלך או מופעלים מהמכשיר שלך. מומלץ להיזהר עם סיסמאות, פרטי תשלום, הודעות, תמונות, אודיו וסרטונים."</string> - <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"בזמן העברה (cast) של אפליקציה, תהיה ל-Android גישה לכל מה שגלוי באפליקציה או מופעל מהאפליקציה. כדאי להיזהר עם סיסמאות, פרטי תשלום, הודעות, תמונות, אודיו וסרטונים."</string> - <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"התחלת ההעברה (cast)"</string> + <string name="media_projection_entry_cast_permission_dialog_warning_single_app" msgid="9900961380294292">"בזמן Cast מאפליקציה, תהיה ל-Android גישה לכל מה שמופיע באפליקציה ולכל מדיה שפועלת בה. כדאי להיזהר עם סיסמאות, פרטי תשלום, הודעות, תמונות, אודיו וסרטונים."</string> + <string name="media_projection_entry_cast_permission_dialog_continue" msgid="7209890669948870042">"הפעלת Cast"</string> <string name="media_projection_entry_generic_permission_dialog_title" msgid="4519802931547483628">"להתחיל את השיתוף?"</string> <string name="media_projection_entry_generic_permission_dialog_warning_entire_screen" msgid="5407906851409410209">"בזמן שיתוף, הקלטה או העברה (cast) תהיה ל-Android גישה לכל הפרטים שגלויים במסך שלך או מופעלים מהמכשיר שלך. מומלץ להיזהר עם סיסמאות, פרטי תשלום, הודעות, תמונות, אודיו וסרטונים."</string> <string name="media_projection_entry_generic_permission_dialog_warning_single_app" msgid="3454859977888159495">"בזמן שיתוף, הקלטה או העברה (cast) של אפליקציה, תהיה ל-Android גישה לכל מה שגלוי באפליקציה או מופעל מהאפליקציה. מומלץ להיזהר עם סיסמאות, פרטי תשלום, הודעות, תמונות, אודיו וסרטונים."</string> @@ -1245,7 +1252,7 @@ <string name="home_quick_affordance_unavailable_configure_the_app" msgid="604424593994493281">"• יש לפחות מכשיר אחד או פאנל מכשיר אחד זמינים"</string> <string name="notes_app_quick_affordance_unavailable_explanation" msgid="4796955161600178530">"צריך לבחור אפליקציית פתקים שתיפתח כברירת מחדל כשייעשה שימוש במקש הקיצור לכתיבת פתקים"</string> <string name="keyguard_affordance_enablement_dialog_notes_app_action" msgid="6821710209675089470">"בחירת אפליקציה"</string> - <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"מקש קיצור ללחיצה ארוכה"</string> + <string name="keyguard_affordance_press_too_short" msgid="8145437175134998864">"צריך ללחוץ לחיצה ארוכה על הלחצן"</string> <string name="rear_display_bottom_sheet_cancel" msgid="3461468855493357248">"ביטול"</string> <string name="rear_display_bottom_sheet_confirm" msgid="1507591562761552899">"כן, אני רוצה להחליף בין המסכים"</string> <string name="rear_display_folded_bottom_sheet_title" msgid="3930008746560711990">"פתיחת הטלפון"</string> @@ -1302,6 +1309,6 @@ <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"נעשה שימוש לאחרונה על ידי <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</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> + <string name="home_controls_dream_label" msgid="6567105701292324257">"שליטה במכשירים"</string> <string name="home_controls_dream_description" msgid="4644150952104035789">"גישה מהירה לממשק השליטה במכשירים כשומר מסך"</string> </resources> diff --git a/packages/SystemUI/res/values-ja/strings.xml b/packages/SystemUI/res/values-ja/strings.xml index 6cd1dcb1cbd7..f4033081f02a 100644 --- a/packages/SystemUI/res/values-ja/strings.xml +++ b/packages/SystemUI/res/values-ja/strings.xml @@ -173,7 +173,7 @@ <string name="biometric_dialog_wrong_pin" msgid="1878539073972762803">"PIN が正しくありません"</string> <string name="biometric_dialog_wrong_pattern" msgid="8954812279840889029">"パターンが正しくありません"</string> <string name="biometric_dialog_wrong_password" msgid="69477929306843790">"パスワードが正しくありません"</string> - <string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"間違えた回数が上限を超えました。\n<xliff:g id="NUMBER">%d</xliff:g> 秒後にもう一度お試しください。"</string> + <string name="biometric_dialog_credential_too_many_attempts" msgid="3083141271737748716">"試行回数が上限に達しました。\n<xliff:g id="NUMBER">%d</xliff:g> 秒後にもう一度お試しください。"</string> <string name="work_challenge_emergency_button_text" msgid="8946588434515599288">"緊急通報"</string> <string name="biometric_dialog_credential_attempts_before_wipe" msgid="6751859711975516999">"もう一度お試しください。入力回数: <xliff:g id="ATTEMPTS_0">%1$d</xliff:g>/<xliff:g id="MAX_ATTEMPTS">%2$d</xliff:g> 回"</string> <string name="biometric_dialog_last_attempt_before_wipe_dialog_title" msgid="2874250099278693477">"データが削除されます"</string> @@ -270,12 +270,20 @@ <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"すべて表示"</string> <string name="turn_on_bluetooth" msgid="5681370462180289071">"Bluetooth を使用"</string> <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"接続しました"</string> + <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) --> + <skip /> <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">"明日自動的に ON に戻す"</string> - <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"クイック共有、デバイスを探す、デバイスの位置情報などの機能は Bluetooth を使用します"</string> - <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"午前 5 時に Bluetooth が ON になります"</string> + <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) --> + <skip /> + <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) --> + <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> @@ -350,8 +358,7 @@ <string name="qs_record_issue_label" msgid="8166290137285529059">"録音に関する問題"</string> <string name="qs_record_issue_start" msgid="2979831312582567056">"開始"</string> <string name="qs_record_issue_stop" msgid="3531747965741982657">"停止"</string> - <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) --> - <skip /> + <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"バグレポート"</string> <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> @@ -549,7 +556,7 @@ <string name="monitoring_description_parental_controls" msgid="8184693528917051626">"このデバイスは保護者によって管理されています。保護者は、あなたが使用するアプリ、あなたの現在地、デバイスの利用時間などの情報を確認したり、管理したりできます。"</string> <string name="legacy_vpn_name" msgid="4174223520162559145">"VPN"</string> <string name="keyguard_indication_trust_unlocked" msgid="7395154975733744547">"信頼エージェントがロック解除を管理"</string> - <string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"認証の試行回数が上限を超えたため、デバイスがロックされました"</string> + <string name="kg_prompt_after_adaptive_auth_lock" msgid="2587481497846342760">"認証の試行回数が上限に達したため、デバイスがロックされました"</string> <string name="keyguard_indication_after_adaptive_auth_lock" msgid="2323400645470712787">"デバイスがロックされました\n認証に失敗しました"</string> <string name="zen_mode_and_condition" msgid="5043165189511223718">"<xliff:g id="ZEN_MODE">%1$s</xliff:g>。<xliff:g id="EXIT_CONDITION">%2$s</xliff:g>"</string> <string name="accessibility_volume_settings" msgid="1458961116951564784">"音声の設定"</string> @@ -606,7 +613,7 @@ <string name="volume_panel_spatial_audio_title" msgid="3367048857932040660">"空間オーディオ"</string> <string name="volume_panel_spatial_audio_off" msgid="4177490084606772989">"OFF"</string> <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"固定"</string> - <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"ヘッド トラッキング"</string> + <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"ヘッド トラッキング"</string> <string name="volume_ringer_change" msgid="3574969197796055532">"タップすると、着信音のモードを変更できます"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"ミュート"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"ミュートを解除"</string> @@ -1303,5 +1310,5 @@ <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> - <string name="home_controls_dream_description" msgid="4644150952104035789">"ホーム コントロールにスクリーンセーバーとしてすばやくアクセス"</string> + <string name="home_controls_dream_description" msgid="4644150952104035789">"ホーム コントロールにスクリーンセーバーからすばやくアクセス"</string> </resources> diff --git a/packages/SystemUI/res/values-ka/strings.xml b/packages/SystemUI/res/values-ka/strings.xml index 15bb4fcc0134..d9483ec9d0f5 100644 --- a/packages/SystemUI/res/values-ka/strings.xml +++ b/packages/SystemUI/res/values-ka/strings.xml @@ -270,12 +270,20 @@ <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"ყველას ნახვა"</string> <string name="turn_on_bluetooth" msgid="5681370462180289071">"Bluetooth-ის გამოყენება"</string> <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"დაკავშირებული"</string> + <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) --> + <skip /> <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_info_disabled" msgid="8267380591344023327">"ფუნქციები, როგორებიცაა „სწრაფი გაზიარება“, „ჩემი მოწყობილობის პოვნა“ და „მოწყობილობის მდებარეობა“ იყენებს Bluetooth-ს"</string> - <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth ჩაირთვება ხვალ დილის 5 საათზე"</string> + <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) --> + <skip /> + <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) --> + <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> @@ -350,8 +358,7 @@ <string name="qs_record_issue_label" msgid="8166290137285529059">"ჩაწერასთან დაკავშირებული პრობლემა"</string> <string name="qs_record_issue_start" msgid="2979831312582567056">"დაწყება"</string> <string name="qs_record_issue_stop" msgid="3531747965741982657">"გაჩერება"</string> - <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) --> - <skip /> + <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"სისტემის ხარვეზის ანგარიში"</string> <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> diff --git a/packages/SystemUI/res/values-kk/strings.xml b/packages/SystemUI/res/values-kk/strings.xml index 835a0cabfceb..36ae88aaf43a 100644 --- a/packages/SystemUI/res/values-kk/strings.xml +++ b/packages/SystemUI/res/values-kk/strings.xml @@ -270,12 +270,20 @@ <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Барлығын көру"</string> <string name="turn_on_bluetooth" msgid="5681370462180289071">"Bluetooth-ты пайдалану"</string> <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Қосылды"</string> + <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) --> + <skip /> <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_info_disabled" msgid="8267380591344023327">"Quick Share, Find My Device сияқты функциялар мен құрылғы локациясы Bluetooth пайдаланады."</string> - <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth ертең таңғы сағат 5-те қосылады."</string> + <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) --> + <skip /> + <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) --> + <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> @@ -350,8 +358,7 @@ <string name="qs_record_issue_label" msgid="8166290137285529059">"Ақауды жазу"</string> <string name="qs_record_issue_start" msgid="2979831312582567056">"Бастау"</string> <string name="qs_record_issue_stop" msgid="3531747965741982657">"Тоқтату"</string> - <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) --> - <skip /> + <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Қате туралы есеп"</string> <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> @@ -604,7 +611,7 @@ <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Дыбысын өшіру үшін түртіңіз."</string> <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"Шуды реттеу"</string> <string name="volume_panel_spatial_audio_title" msgid="3367048857932040660">"Кеңістіктік дыбыс"</string> - <string name="volume_panel_spatial_audio_off" msgid="4177490084606772989">"Өшіру"</string> + <string name="volume_panel_spatial_audio_off" msgid="4177490084606772989">"Өшірілген"</string> <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Бекітілген"</string> <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Бас қимылын қадағалау"</string> <string name="volume_ringer_change" msgid="3574969197796055532">"Қоңырау режимін өзгерту үшін түртіңіз."</string> @@ -1303,5 +1310,5 @@ <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> - <string name="home_controls_dream_description" msgid="4644150952104035789">"Үй басқару элементтерін скринсейвер ретінде жылдам қолдану"</string> + <string name="home_controls_dream_description" msgid="4644150952104035789">"Үй басқару элементтерін скринсейверден қолдану"</string> </resources> diff --git a/packages/SystemUI/res/values-km/strings.xml b/packages/SystemUI/res/values-km/strings.xml index d56c20de4692..c7868db655d1 100644 --- a/packages/SystemUI/res/values-km/strings.xml +++ b/packages/SystemUI/res/values-km/strings.xml @@ -270,12 +270,20 @@ <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"មើលទាំងអស់"</string> <string name="turn_on_bluetooth" msgid="5681370462180289071">"ប្រើប៊្លូធូស"</string> <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"បានភ្ជាប់"</string> + <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) --> + <skip /> <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_info_disabled" msgid="8267380591344023327">"មុខងារដូចជា Quick Share, រកឧបករណ៍របស់ខ្ញុំ និងប៊្លូធូសប្រើប្រាស់ទីតាំងឧបករណ៍"</string> - <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"ប៊្លូធូសនឹងបើកនៅថ្ងៃស្អែកនៅម៉ោង 5 ព្រឹក"</string> + <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) --> + <skip /> + <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) --> + <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> diff --git a/packages/SystemUI/res/values-kn/strings.xml b/packages/SystemUI/res/values-kn/strings.xml index 23bde222e11a..42d655e105df 100644 --- a/packages/SystemUI/res/values-kn/strings.xml +++ b/packages/SystemUI/res/values-kn/strings.xml @@ -237,7 +237,7 @@ <string name="accessibility_desc_qs_notification_shade" msgid="8327226953072700376">"ತ್ವರಿತ ಸೆಟ್ಟಿಂಗ್ಗಳು ಮತ್ತು ಅಧಿಸೂಚನೆಯ ಪರದೆ."</string> <string name="accessibility_desc_lock_screen" msgid="5983125095181194887">"ಲಾಕ್ ಸ್ಕ್ರೀನ್."</string> <string name="accessibility_desc_work_lock" msgid="4355620395354680575">"ಕೆಲಸದ ಲಾಕ್ ಪರದೆ"</string> - <string name="accessibility_desc_close" msgid="8293708213442107755">"ಮುಚ್ಚು"</string> + <string name="accessibility_desc_close" msgid="8293708213442107755">"ಮುಚ್ಚಿ"</string> <string name="accessibility_quick_settings_dnd_none_on" msgid="3235552940146035383">"ಸಂಪೂರ್ಣ ನಿಶ್ಯಬ್ಧ"</string> <string name="accessibility_quick_settings_dnd_alarms_on" msgid="3375848309132140014">"ಅಲಾರಮ್ಗಳು ಮಾತ್ರ"</string> <string name="accessibility_quick_settings_dnd" msgid="2415967452264206047">"ಅಡಚಣೆ ಮಾಡಬೇಡಿ."</string> @@ -270,12 +270,20 @@ <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"ಎಲ್ಲವನ್ನೂ ನೋಡಿ"</string> <string name="turn_on_bluetooth" msgid="5681370462180289071">"ಬ್ಲೂಟೂತ್ ಬಳಸಿ"</string> <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"ಕನೆಕ್ಟ್ ಆಗಿದೆ"</string> + <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) --> + <skip /> <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_info_disabled" msgid="8267380591344023327">"ಕ್ವಿಕ್ ಶೇರ್, Find My Device ನಂತಹ ಫೀಚರ್ಗಳು ಹಾಗೂ ಸಾಧನದ ಸ್ಥಳವು ಬ್ಲೂಟೂತ್ ಅನ್ನು ಬಳಸುತ್ತವೆ"</string> - <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"ಬ್ಲೂಟೂತ್ ನಾಳೆ ಬೆಳಗ್ಗೆ 5 ಗಂಟೆಗೆ ಆನ್ ಆಗುತ್ತದೆ"</string> + <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) --> + <skip /> + <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) --> + <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> @@ -350,8 +358,7 @@ <string name="qs_record_issue_label" msgid="8166290137285529059">"ರೆಕಾರ್ಡ್ ದೋಷ"</string> <string name="qs_record_issue_start" msgid="2979831312582567056">"ಪ್ರಾರಂಭಿಸಿ"</string> <string name="qs_record_issue_stop" msgid="3531747965741982657">"ನಿಲ್ಲಿಸಿ"</string> - <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) --> - <skip /> + <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"ಬಗ್ ವರದಿ ಮಾಡುವಿಕೆ"</string> <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> @@ -367,7 +374,7 @@ <string name="quick_settings_contrast_high" msgid="656049259587494499">"ಹೆಚ್ಚು"</string> <string name="quick_settings_hearing_devices_label" msgid="7277170419679404129">"ಹಿಯರಿಂಗ್ ಸಾಧನಗಳು"</string> <string name="quick_settings_hearing_devices_dialog_title" msgid="9004774017688484981">"ಹಿಯರಿಂಗ್ ಸಾಧನಗಳು"</string> - <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"ಹೊಸ ಸಾಧನವನ್ನು ಜೋಡಿಸಿ"</string> + <string name="quick_settings_pair_hearing_devices" msgid="5987105102207447322">"ಹೊಸ ಸಾಧನವನ್ನು ಪೇರ್ ಮಾಡಿ"</string> <string name="accessibility_hearing_device_pair_new_device" msgid="8440082580186130090">"ಹೊಸ ಸಾಧನವನ್ನು ಜೋಡಿಸಲು ಕ್ಲಿಕ್ ಮಾಡಿ"</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> @@ -1302,6 +1309,6 @@ <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"ಇತ್ತೀಚೆಗೆ <xliff:g id="APP_NAME">%1$s</xliff:g> ಇದನ್ನು ಬಳಸಿದೆ (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</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> - <string name="home_controls_dream_description" msgid="4644150952104035789">"ಹೋಮ್ ನಿಯಂತ್ರಣವನ್ನು ಸ್ಕ್ರೀನ್ಸೇವರ್ನಂತೆ ತ್ವರಿತವಾಗಿ ಆ್ಯಕ್ಸೆಸ್ ಮಾಡಿ"</string> + <string name="home_controls_dream_label" msgid="6567105701292324257">"ಮನೆ ನಿಯಂತ್ರಣಗಳು"</string> + <string name="home_controls_dream_description" msgid="4644150952104035789">"ಮನೆ ನಿಯಂತ್ರಣವನ್ನು ಸ್ಕ್ರೀನ್ಸೇವರ್ನಂತೆ ಬೇಗ ಆ್ಯಕ್ಸೆಸ್ ಮಾಡಿ"</string> </resources> diff --git a/packages/SystemUI/res/values-ko/strings.xml b/packages/SystemUI/res/values-ko/strings.xml index 8f8246522b7e..3c34f8d258ba 100644 --- a/packages/SystemUI/res/values-ko/strings.xml +++ b/packages/SystemUI/res/values-ko/strings.xml @@ -270,12 +270,20 @@ <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"모두 보기"</string> <string name="turn_on_bluetooth" msgid="5681370462180289071">"블루투스 사용"</string> <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"연결됨"</string> + <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) --> + <skip /> <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_info_disabled" msgid="8267380591344023327">"Quick Share, 내 기기 찾기, 기기 위치 등의 기능에서 블루투스를 사용합니다."</string> - <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"블루투스가 내일 오전 5시에 켜집니다."</string> + <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) --> + <skip /> + <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) --> + <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> @@ -350,8 +358,7 @@ <string name="qs_record_issue_label" msgid="8166290137285529059">"문제 기록"</string> <string name="qs_record_issue_start" msgid="2979831312582567056">"시작"</string> <string name="qs_record_issue_stop" msgid="3531747965741982657">"중지"</string> - <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) --> - <skip /> + <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"버그 신고"</string> <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> @@ -1303,5 +1310,5 @@ <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> - <string name="home_controls_dream_description" msgid="4644150952104035789">"화면 보호기로 홈 컨트롤에 빠르게 액세스합니다."</string> + <string name="home_controls_dream_description" msgid="4644150952104035789">"화면 보호기로 홈 컨트롤에 빠르게 액세스하기"</string> </resources> diff --git a/packages/SystemUI/res/values-ky/strings.xml b/packages/SystemUI/res/values-ky/strings.xml index 40a427cebcb8..ad9d2f7c811b 100644 --- a/packages/SystemUI/res/values-ky/strings.xml +++ b/packages/SystemUI/res/values-ky/strings.xml @@ -270,12 +270,20 @@ <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Баарын көрүү"</string> <string name="turn_on_bluetooth" msgid="5681370462180289071">"Иштетүү"</string> <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Туташты"</string> + <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) --> + <skip /> <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_info_disabled" msgid="8267380591344023327">"Тез Бөлүшүү, \"Түзмөгүм кайда?\" жана түзмөктүн турган жерин аныктоо сыяктуу функциялар Bluetooth\'ду колдонот"</string> - <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth эртең саат 05:00 күйөт"</string> + <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) --> + <skip /> + <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) --> + <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> diff --git a/packages/SystemUI/res/values-lo/strings.xml b/packages/SystemUI/res/values-lo/strings.xml index f7dfc7e1c5a1..eb9e1ebeda9c 100644 --- a/packages/SystemUI/res/values-lo/strings.xml +++ b/packages/SystemUI/res/values-lo/strings.xml @@ -270,12 +270,20 @@ <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"ເບິ່ງທັງໝົດ"</string> <string name="turn_on_bluetooth" msgid="5681370462180289071">"ໃຊ້ Bluetooth"</string> <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"ເຊື່ອມຕໍ່ແລ້ວ"</string> + <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) --> + <skip /> <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_info_disabled" msgid="8267380591344023327">"ຄຸນສົມບັດຕ່າງໆ ເຊັ່ນ: ການແຊຣ໌ດ່ວນ, ຊອກຫາອຸປະກອນຂອງຂ້ອຍ ແລະ ສະຖານທີ່ຂອງອຸປະກອນແມ່ນໃຊ້ Bluetooth"</string> - <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth ຈະເປີດມື້ອື່ນເວລາ 05:00 ໂມງ"</string> + <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) --> + <skip /> + <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) --> + <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> diff --git a/packages/SystemUI/res/values-lt/strings.xml b/packages/SystemUI/res/values-lt/strings.xml index 734daf892d63..63971ab060d6 100644 --- a/packages/SystemUI/res/values-lt/strings.xml +++ b/packages/SystemUI/res/values-lt/strings.xml @@ -270,12 +270,20 @@ <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Žiūrėti viską"</string> <string name="turn_on_bluetooth" msgid="5681370462180289071">"„Bluetooth“ naudojimas"</string> <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Prisijungta"</string> + <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) --> + <skip /> <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Išsaugota"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"atjungti"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"suaktyvinti"</string> <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="8267380591344023327">"Tokioms funkcijoms kaip „Spartusis bendrinimas“, „Rasti įrenginį“ ir įrenginio vietovė naudojamas „Bluetooth“ ryšys"</string> - <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"„Bluetooth“ bus įjungtas rytoj, 5 val."</string> + <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) --> + <skip /> + <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) --> + <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> @@ -350,8 +358,7 @@ <string name="qs_record_issue_label" msgid="8166290137285529059">"Įrašyti problemą"</string> <string name="qs_record_issue_start" msgid="2979831312582567056">"Pradėti"</string> <string name="qs_record_issue_stop" msgid="3531747965741982657">"Stabdyti"</string> - <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) --> - <skip /> + <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Pranešimas apie riktą"</string> <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> diff --git a/packages/SystemUI/res/values-lv/strings.xml b/packages/SystemUI/res/values-lv/strings.xml index 74a9239c288a..4b7507de43d1 100644 --- a/packages/SystemUI/res/values-lv/strings.xml +++ b/packages/SystemUI/res/values-lv/strings.xml @@ -270,12 +270,20 @@ <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Skatīt visas"</string> <string name="turn_on_bluetooth" msgid="5681370462180289071">"Izmantot Bluetooth"</string> <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Savienojums izveidots"</string> + <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) --> + <skip /> <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Saglabāta"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"atvienot"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktivizēt"</string> <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="8267380591344023327">"Tādas funkcijas kā “Ātrā kopīgošana”, “Atrast ierīci” un ierīces atrašanās vietas noteikšana izmanto tehnoloģiju Bluetooth."</string> - <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth tiks ieslēgts rīt plkst. 5:00"</string> + <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) --> + <skip /> + <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) --> + <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> @@ -350,8 +358,7 @@ <string name="qs_record_issue_label" msgid="8166290137285529059">"Problēmas ierakstīšana"</string> <string name="qs_record_issue_start" msgid="2979831312582567056">"Sākt"</string> <string name="qs_record_issue_stop" msgid="3531747965741982657">"Apturēt"</string> - <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) --> - <skip /> + <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Kļūdas pārskats"</string> <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> @@ -604,7 +611,7 @@ <string name="volume_stream_content_description_mute_a11y" msgid="5743548478357238156">"%1$s. Pieskarieties, lai izslēgtu skaņu."</string> <string name="volume_panel_noise_control_title" msgid="7413949943872304474">"Trokšņu kontrole"</string> <string name="volume_panel_spatial_audio_title" msgid="3367048857932040660">"Telpiskais audio"</string> - <string name="volume_panel_spatial_audio_off" msgid="4177490084606772989">"Izslēgts"</string> + <string name="volume_panel_spatial_audio_off" msgid="4177490084606772989">"Izslēgta"</string> <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Fiksēts"</string> <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Seko galvai"</string> <string name="volume_ringer_change" msgid="3574969197796055532">"Pieskarieties, lai mainītu zvanītāja režīmu."</string> diff --git a/packages/SystemUI/res/values-mk/strings.xml b/packages/SystemUI/res/values-mk/strings.xml index 36d49e4241e1..ba29ee6c89a1 100644 --- a/packages/SystemUI/res/values-mk/strings.xml +++ b/packages/SystemUI/res/values-mk/strings.xml @@ -270,12 +270,20 @@ <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Прикажи ги сите"</string> <string name="turn_on_bluetooth" msgid="5681370462180289071">"Користи Bluetooth"</string> <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Поврзано"</string> + <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) --> + <skip /> <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_info_disabled" msgid="8267380591344023327">"Функциите како „Брзо споделување“, „Најди го мојот уред“ и локација на уредот користат Bluetooth"</string> - <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth ќе се вклучи утре во 5:00"</string> + <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) --> + <skip /> + <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) --> + <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> @@ -350,8 +358,7 @@ <string name="qs_record_issue_label" msgid="8166290137285529059">"Евидентирајте проблем"</string> <string name="qs_record_issue_start" msgid="2979831312582567056">"Започнете"</string> <string name="qs_record_issue_stop" msgid="3531747965741982657">"Сопрете"</string> - <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) --> - <skip /> + <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Извештај за грешка"</string> <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> @@ -1303,5 +1310,5 @@ <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> - <string name="home_controls_dream_description" msgid="4644150952104035789">"Брзо прист. до контр. за дом. како штедач на екран"</string> + <string name="home_controls_dream_description" msgid="4644150952104035789">"Контролите за домот како штедач на екран"</string> </resources> diff --git a/packages/SystemUI/res/values-ml/strings.xml b/packages/SystemUI/res/values-ml/strings.xml index b20faa9af6e4..8e9392ce9b23 100644 --- a/packages/SystemUI/res/values-ml/strings.xml +++ b/packages/SystemUI/res/values-ml/strings.xml @@ -270,12 +270,20 @@ <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"എല്ലാം കാണുക"</string> <string name="turn_on_bluetooth" msgid="5681370462180289071">"Bluetooth ഉപയോഗിക്കുക"</string> <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"കണക്റ്റ് ചെയ്തു"</string> + <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) --> + <skip /> <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_info_disabled" msgid="8267380591344023327">"ക്വിക്ക് ഷെയർ, Find My Device, ഉപകരണ ലൊക്കേഷൻ എന്നിവ പോലുള്ള ഫീച്ചറുകൾ Bluetooth ഉപയോഗിക്കുന്നു"</string> - <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth നാളെ 5 AM-ന് ഓണാക്കും"</string> + <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) --> + <skip /> + <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) --> + <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> diff --git a/packages/SystemUI/res/values-mn/strings.xml b/packages/SystemUI/res/values-mn/strings.xml index 333ab55b6485..aab22b3fd590 100644 --- a/packages/SystemUI/res/values-mn/strings.xml +++ b/packages/SystemUI/res/values-mn/strings.xml @@ -270,12 +270,20 @@ <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Бүгдийг харах"</string> <string name="turn_on_bluetooth" msgid="5681370462180289071">"Bluetooth-г ашиглах"</string> <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Холбогдсон"</string> + <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) --> + <skip /> <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_info_disabled" msgid="8267380591344023327">"Түргэн хуваалцах, Миний төхөөрөмжийг олох зэрэг онцлогууд болон төхөөрөмжийн байршил Bluetooth-г ашигладаг"</string> - <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth маргааш ҮӨ 5 цагт асна"</string> + <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) --> + <skip /> + <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) --> + <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> @@ -350,8 +358,7 @@ <string name="qs_record_issue_label" msgid="8166290137285529059">"Асуудлыг бичих"</string> <string name="qs_record_issue_start" msgid="2979831312582567056">"Эхлүүлэх"</string> <string name="qs_record_issue_stop" msgid="3531747965741982657">"Зогсоох"</string> - <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) --> - <skip /> + <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Алдааны мэдээ"</string> <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> diff --git a/packages/SystemUI/res/values-mr/strings.xml b/packages/SystemUI/res/values-mr/strings.xml index 5bed1b432909..071935accbcd 100644 --- a/packages/SystemUI/res/values-mr/strings.xml +++ b/packages/SystemUI/res/values-mr/strings.xml @@ -270,12 +270,20 @@ <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"सर्व पहा"</string> <string name="turn_on_bluetooth" msgid="5681370462180289071">"ब्लूटूथ वापरा"</string> <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"कनेक्ट केले"</string> + <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) --> + <skip /> <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_info_disabled" msgid="8267380591344023327">"क्विक शेअर, Find My Device आणि डिव्हाइसचे स्थान यांसारखी वैशिष्ट्ये ब्लूटूथ वापरतात"</string> - <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"ब्लूटूथ उद्या सकाळी ५ वाजता सुरू होईल"</string> + <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) --> + <skip /> + <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) --> + <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> @@ -350,8 +358,7 @@ <string name="qs_record_issue_label" msgid="8166290137285529059">"समस्या रेकॉर्ड करा"</string> <string name="qs_record_issue_start" msgid="2979831312582567056">"सुरुवात करा"</string> <string name="qs_record_issue_stop" msgid="3531747965741982657">"थांबवा"</string> - <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) --> - <skip /> + <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"बग रिपोर्ट"</string> <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> diff --git a/packages/SystemUI/res/values-ms/strings.xml b/packages/SystemUI/res/values-ms/strings.xml index 4df6540c617e..95d42373dece 100644 --- a/packages/SystemUI/res/values-ms/strings.xml +++ b/packages/SystemUI/res/values-ms/strings.xml @@ -270,12 +270,20 @@ <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Lihat semua"</string> <string name="turn_on_bluetooth" msgid="5681370462180289071">"Gunakan Bluetooth"</string> <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Disambungkan"</string> + <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) --> + <skip /> <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Disimpan"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"putuskan sambungan"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktifkan"</string> <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Dihidupkan sekali lagi esok secara automatik"</string> - <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Ciri seperti Quick Share, Find My Device dan lokasi peranti menggunakan Bluetooth"</string> - <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth akan dihidupkan esok pada pukul 5 PG"</string> + <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) --> + <skip /> + <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) --> + <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> @@ -1302,5 +1310,5 @@ <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> - <string name="home_controls_dream_description" msgid="4644150952104035789">"Akses kawalan rumah anda sebagai penyelamat skrin dengan cepat"</string> + <string name="home_controls_dream_description" msgid="4644150952104035789">"Jadikan kawalan rumah anda sebagai penyelamat skrin"</string> </resources> diff --git a/packages/SystemUI/res/values-my/strings.xml b/packages/SystemUI/res/values-my/strings.xml index a1894b04277b..7941935a7b53 100644 --- a/packages/SystemUI/res/values-my/strings.xml +++ b/packages/SystemUI/res/values-my/strings.xml @@ -270,12 +270,20 @@ <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"အားလုံးကြည့်ရန်"</string> <string name="turn_on_bluetooth" msgid="5681370462180289071">"ဘလူးတုသ်သုံးရန်"</string> <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"ချိတ်ဆက်ထားသည်"</string> + <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) --> + <skip /> <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_info_disabled" msgid="8267380591344023327">"‘အမြန် မျှဝေပါ’၊ Find My Device နှင့် စက်ပစ္စည်းတည်နေရာကဲ့သို့ တူးလ်များသည် ဘလူးတုသ်သုံးသည်"</string> - <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"မနက်ဖြန် မနက် ၅ နာရီတွင် ဘလူးတုသ်ကို ဖွင့်ပါမည်"</string> + <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) --> + <skip /> + <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) --> + <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> @@ -350,8 +358,7 @@ <string name="qs_record_issue_label" msgid="8166290137285529059">"ပြဿနာကို မှတ်တမ်းတင်ခြင်း"</string> <string name="qs_record_issue_start" msgid="2979831312582567056">"စတင်ပါ"</string> <string name="qs_record_issue_stop" msgid="3531747965741982657">"ရပ်ပါ"</string> - <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) --> - <skip /> + <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"ချွတ်ယွင်းမှု အစီရင်ခံစာ"</string> <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> diff --git a/packages/SystemUI/res/values-nb/strings.xml b/packages/SystemUI/res/values-nb/strings.xml index 5039150d06eb..59afb3e52a47 100644 --- a/packages/SystemUI/res/values-nb/strings.xml +++ b/packages/SystemUI/res/values-nb/strings.xml @@ -270,12 +270,20 @@ <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Se alle"</string> <string name="turn_on_bluetooth" msgid="5681370462180289071">"Bruk Bluetooth"</string> <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Tilkoblet"</string> + <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) --> + <skip /> <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Lagret"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"koble fra"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktiver"</string> <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="8267380591344023327">"Funksjoner som Quick Share, Finn enheten min og enhetsposisjon bruker Bluetooth"</string> - <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth slås på i morgen kl. 05.00"</string> + <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) --> + <skip /> + <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) --> + <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> @@ -350,8 +358,7 @@ <string name="qs_record_issue_label" msgid="8166290137285529059">"Registrer problem"</string> <string name="qs_record_issue_start" msgid="2979831312582567056">"Start"</string> <string name="qs_record_issue_stop" msgid="3531747965741982657">"Stopp"</string> - <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) --> - <skip /> + <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Feilrapport"</string> <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> diff --git a/packages/SystemUI/res/values-ne/strings.xml b/packages/SystemUI/res/values-ne/strings.xml index 28c7b05a290f..3ab647fa3745 100644 --- a/packages/SystemUI/res/values-ne/strings.xml +++ b/packages/SystemUI/res/values-ne/strings.xml @@ -270,12 +270,20 @@ <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"सबै डिभाइसहरू हेर्नुहोस्"</string> <string name="turn_on_bluetooth" msgid="5681370462180289071">"ब्लुटुथ प्रयोग गर्नुहोस्"</string> <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"कनेक्ट गरिएको छ"</string> + <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) --> + <skip /> <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_info_disabled" msgid="8267380591344023327">"क्विक सेयर, Find My Device र डिभाइसको लोकेसन जस्ता सुविधाहरूले ब्लुटुथ प्रयोग गर्छन्"</string> - <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"ब्लुटुथ भोलि बिहान ५ बजे अन हुने छ"</string> + <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) --> + <skip /> + <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) --> + <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> diff --git a/packages/SystemUI/res/values-nl/strings.xml b/packages/SystemUI/res/values-nl/strings.xml index d0500c04568e..e85a1da3f1e6 100644 --- a/packages/SystemUI/res/values-nl/strings.xml +++ b/packages/SystemUI/res/values-nl/strings.xml @@ -270,12 +270,20 @@ <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Alles tonen"</string> <string name="turn_on_bluetooth" msgid="5681370462180289071">"Bluetooth gebruiken"</string> <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Verbonden"</string> + <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) --> + <skip /> <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Opgeslagen"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"loskoppelen"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"activeren"</string> <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Morgen weer automatisch aanzetten"</string> - <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Functies zoals Quick Share, Vind mijn apparaat en apparaatlocatie maken gebruik van bluetooth"</string> - <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth gaat morgen om 05:00 uur aan"</string> + <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) --> + <skip /> + <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) --> + <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> @@ -350,8 +358,7 @@ <string name="qs_record_issue_label" msgid="8166290137285529059">"Probleem vastleggen"</string> <string name="qs_record_issue_start" msgid="2979831312582567056">"Starten"</string> <string name="qs_record_issue_stop" msgid="3531747965741982657">"Stoppen"</string> - <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) --> - <skip /> + <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Bugrapport"</string> <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Op welk onderdeel van de apparaatfunctionaliteit had dit effect?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Probleemtype selecteren"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Schermopname"</string> @@ -606,7 +613,7 @@ <string name="volume_panel_spatial_audio_title" msgid="3367048857932040660">"Ruimtelijke audio"</string> <string name="volume_panel_spatial_audio_off" msgid="4177490084606772989">"Uit"</string> <string name="volume_panel_spatial_audio_fixed" msgid="3136080137827746046">"Vast"</string> - <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Hoofdbeweging volgen"</string> + <string name="volume_panel_spatial_audio_tracking" msgid="5711115234001762974">"Hoofdbeweging volgen"</string> <string name="volume_ringer_change" msgid="3574969197796055532">"Tik om de beltoonmodus te wijzigen"</string> <string name="volume_ringer_hint_mute" msgid="4263821214125126614">"geluid uit"</string> <string name="volume_ringer_hint_unmute" msgid="6119086890306456976">"geluid aanzetten"</string> diff --git a/packages/SystemUI/res/values-or/strings.xml b/packages/SystemUI/res/values-or/strings.xml index eb6865ea4111..77ed062a17fd 100644 --- a/packages/SystemUI/res/values-or/strings.xml +++ b/packages/SystemUI/res/values-or/strings.xml @@ -270,12 +270,20 @@ <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"ସବୁ ଦେଖନ୍ତୁ"</string> <string name="turn_on_bluetooth" msgid="5681370462180289071">"ବ୍ଲୁଟୁଥ ବ୍ୟବହାର କରନ୍ତୁ"</string> <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"କନେକ୍ଟ କରାଯାଇଛି"</string> + <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) --> + <skip /> <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_info_disabled" msgid="8267380591344023327">"Quick Share, Find My Device ଏବଂ ଡିଭାଇସ ଲୋକେସନ ପରି ଫିଚରଗୁଡ଼ିକ ବ୍ଲୁଟୁଥ ବ୍ୟବହାର କରେ"</string> - <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"ବ୍ଲୁଟୁଥ ଆସନ୍ତାକାଲି 5 AMରେ ଚାଲୁ ହେବ"</string> + <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) --> + <skip /> + <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) --> + <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> @@ -1302,5 +1310,5 @@ <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> - <string name="home_controls_dream_description" msgid="4644150952104035789">"ସ୍କ୍ରିନସେଭର ଭାବେ ହୋମ କଣ୍ଟ୍ରୋଲ୍ସକୁ ଶୀଘ୍ର ଆକ୍ସେସ କର"</string> + <string name="home_controls_dream_description" msgid="4644150952104035789">"ସ୍କ୍ରିନସେଭର ଭାବେ ହୋମ କଣ୍ଟ୍ରୋଲ୍ସକୁ ଶୀଘ୍ର ଆକ୍ସେସ କରନ୍ତୁ"</string> </resources> diff --git a/packages/SystemUI/res/values-pa/strings.xml b/packages/SystemUI/res/values-pa/strings.xml index 3066c1d3e00a..0cc753b49456 100644 --- a/packages/SystemUI/res/values-pa/strings.xml +++ b/packages/SystemUI/res/values-pa/strings.xml @@ -270,12 +270,20 @@ <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"ਸਭ ਦੇਖੋ"</string> <string name="turn_on_bluetooth" msgid="5681370462180289071">"ਬਲੂਟੁੱਥ ਵਰਤੋ"</string> <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"ਕਨੈਕਟ ਹੈ"</string> + <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) --> + <skip /> <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_info_disabled" msgid="8267380591344023327">"ਕਵਿੱਕ ਸ਼ੇਅਰ, Find My Device ਅਤੇ ਡੀਵਾਈਸ ਦਾ ਟਿਕਾਣਾ ਵਰਗੀਆਂ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਬਲੂਟੁੱਥ ਦੀ ਵਰਤੋਂ ਕਰਦੀਆਂ ਹਨ"</string> - <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"ਬਲੂਟੁੱਥ ਕੱਲ੍ਹ ਸਵੇਰੇ 5 ਵਜੇ ਚਾਲੂ ਹੋਵੇਗਾ"</string> + <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) --> + <skip /> + <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) --> + <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> @@ -350,8 +358,7 @@ <string name="qs_record_issue_label" msgid="8166290137285529059">"ਸਮੱਸਿਆ ਰਿਕਾਰਡ ਕਰੋ"</string> <string name="qs_record_issue_start" msgid="2979831312582567056">"ਸ਼ੁਰੂ ਕਰੋ"</string> <string name="qs_record_issue_stop" msgid="3531747965741982657">"ਬੰਦ ਕਰੋ"</string> - <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) --> - <skip /> + <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"ਬੱਗ ਰਿਪੋਰਟ"</string> <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> diff --git a/packages/SystemUI/res/values-pl/strings.xml b/packages/SystemUI/res/values-pl/strings.xml index 6ff18b9319ee..814d321b145d 100644 --- a/packages/SystemUI/res/values-pl/strings.xml +++ b/packages/SystemUI/res/values-pl/strings.xml @@ -270,12 +270,15 @@ <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Pokaż wszystkie"</string> <string name="turn_on_bluetooth" msgid="5681370462180289071">"Używaj Bluetootha"</string> <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Połączone"</string> + <string name="quick_settings_bluetooth_device_audio_sharing" msgid="1496358082943301670">"Udostępnianie dźwięku"</string> <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Zapisane"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"rozłącz"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktywuj"</string> <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automatycznie włącz ponownie jutro"</string> - <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Funkcje takie jak szybkie udostępnianie, Znajdź moje urządzenie czy lokalizacja urządzenia używają Bluetootha"</string> - <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth włączy się jutro o 5 rano"</string> + <string name="turn_on_bluetooth_auto_info_disabled" msgid="682984290339848844">"Funkcje takie jak szybkie udostępnianie czy Znajdź moje urządzenie korzystają z Bluetootha"</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> <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> diff --git a/packages/SystemUI/res/values-pt-rBR/strings.xml b/packages/SystemUI/res/values-pt-rBR/strings.xml index 4d8156b6fa37..17fde25e7606 100644 --- a/packages/SystemUI/res/values-pt-rBR/strings.xml +++ b/packages/SystemUI/res/values-pt-rBR/strings.xml @@ -270,12 +270,20 @@ <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Mostrar tudo"</string> <string name="turn_on_bluetooth" msgid="5681370462180289071">"Usar Bluetooth"</string> <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Conectado"</string> + <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) --> + <skip /> <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Salvo"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"desconectar"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"ativar"</string> <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Ativar automaticamente de novo amanhã"</string> - <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Recursos como o Quick Share, o Encontre Meu Dispositivo e a localização do dispositivo usam o Bluetooth"</string> - <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"O Bluetooth será ativado amanhã às 5h"</string> + <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) --> + <skip /> + <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) --> + <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> @@ -350,8 +358,7 @@ <string name="qs_record_issue_label" msgid="8166290137285529059">"Problema na gravação"</string> <string name="qs_record_issue_start" msgid="2979831312582567056">"Iniciar"</string> <string name="qs_record_issue_stop" msgid="3531747965741982657">"Parar"</string> - <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) --> - <skip /> + <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Relatório do bug"</string> <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> @@ -1303,5 +1310,5 @@ <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> - <string name="home_controls_dream_description" msgid="4644150952104035789">"Acesse rapidamente a automação residencial como um protetor de tela"</string> + <string name="home_controls_dream_description" msgid="4644150952104035789">"Controles de automação residencial no protetor de tela"</string> </resources> diff --git a/packages/SystemUI/res/values-pt-rPT/strings.xml b/packages/SystemUI/res/values-pt-rPT/strings.xml index 3c7578f5e756..dfb7695072de 100644 --- a/packages/SystemUI/res/values-pt-rPT/strings.xml +++ b/packages/SystemUI/res/values-pt-rPT/strings.xml @@ -270,12 +270,20 @@ <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Ver tudo"</string> <string name="turn_on_bluetooth" msgid="5681370462180289071">"Usar Bluetooth"</string> <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Ligado"</string> + <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) --> + <skip /> <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Guardado"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"desassociar"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"ativar"</string> <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Reativar amanhã automaticamente"</string> - <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"As funcionalidades como Partilha rápida, Localizar o meu dispositivo e localização do dispositivo usam o Bluetooth"</string> - <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"O Bluetooth vai ser ativado amanhã às 05:00"</string> + <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) --> + <skip /> + <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) --> + <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> @@ -1302,5 +1310,5 @@ <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> - <string name="home_controls_dream_description" msgid="4644150952104035789">"Aceda rapid. aos contr. domést. como prot. de ecrã"</string> + <string name="home_controls_dream_description" msgid="4644150952104035789">"Use controlos domésticos como proteção de ecrã"</string> </resources> diff --git a/packages/SystemUI/res/values-pt/strings.xml b/packages/SystemUI/res/values-pt/strings.xml index 4d8156b6fa37..17fde25e7606 100644 --- a/packages/SystemUI/res/values-pt/strings.xml +++ b/packages/SystemUI/res/values-pt/strings.xml @@ -270,12 +270,20 @@ <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Mostrar tudo"</string> <string name="turn_on_bluetooth" msgid="5681370462180289071">"Usar Bluetooth"</string> <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Conectado"</string> + <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) --> + <skip /> <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Salvo"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"desconectar"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"ativar"</string> <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Ativar automaticamente de novo amanhã"</string> - <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Recursos como o Quick Share, o Encontre Meu Dispositivo e a localização do dispositivo usam o Bluetooth"</string> - <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"O Bluetooth será ativado amanhã às 5h"</string> + <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) --> + <skip /> + <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) --> + <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> @@ -350,8 +358,7 @@ <string name="qs_record_issue_label" msgid="8166290137285529059">"Problema na gravação"</string> <string name="qs_record_issue_start" msgid="2979831312582567056">"Iniciar"</string> <string name="qs_record_issue_stop" msgid="3531747965741982657">"Parar"</string> - <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) --> - <skip /> + <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Relatório do bug"</string> <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> @@ -1303,5 +1310,5 @@ <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> - <string name="home_controls_dream_description" msgid="4644150952104035789">"Acesse rapidamente a automação residencial como um protetor de tela"</string> + <string name="home_controls_dream_description" msgid="4644150952104035789">"Controles de automação residencial no protetor de tela"</string> </resources> diff --git a/packages/SystemUI/res/values-ro/strings.xml b/packages/SystemUI/res/values-ro/strings.xml index 172ee12ad9c9..2af8013fe85c 100644 --- a/packages/SystemUI/res/values-ro/strings.xml +++ b/packages/SystemUI/res/values-ro/strings.xml @@ -270,12 +270,20 @@ <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Afișează tot"</string> <string name="turn_on_bluetooth" msgid="5681370462180289071">"Folosește Bluetooth"</string> <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Conectat"</string> + <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) --> + <skip /> <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Salvat"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"deconectează"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"activează"</string> <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="8267380591344023327">"Funcții precum Quick Share, Găsește-mi dispozitivul și locația dispozitivului folosesc Bluetooth"</string> - <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth se va activa mâine la 5 dimineața"</string> + <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) --> + <skip /> + <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) --> + <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> @@ -350,8 +358,7 @@ <string name="qs_record_issue_label" msgid="8166290137285529059">"Problemă legată de înregistrare"</string> <string name="qs_record_issue_start" msgid="2979831312582567056">"Începe"</string> <string name="qs_record_issue_stop" msgid="3531747965741982657">"Oprește"</string> - <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) --> - <skip /> + <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Raport de eroare"</string> <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> diff --git a/packages/SystemUI/res/values-ru/strings.xml b/packages/SystemUI/res/values-ru/strings.xml index 249be7fec750..16fe33162330 100644 --- a/packages/SystemUI/res/values-ru/strings.xml +++ b/packages/SystemUI/res/values-ru/strings.xml @@ -270,12 +270,20 @@ <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Все"</string> <string name="turn_on_bluetooth" msgid="5681370462180289071">"Использовать"</string> <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Подключено"</string> + <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) --> + <skip /> <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_info_disabled" msgid="8267380591344023327">"Bluetooth используется в сервисе \"Найти устройство\", таких функциях, как Быстрая отправка, и при определении местоположения устройства"</string> - <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth будет включен завтра в 05:00"</string> + <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) --> + <skip /> + <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) --> + <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> @@ -1302,5 +1310,5 @@ <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> - <string name="home_controls_dream_description" msgid="4644150952104035789">"Добавьте настройки умного дома на заставку"</string> + <string name="home_controls_dream_description" msgid="4644150952104035789">"Быстрый доступ к управлению домом через заставку"</string> </resources> diff --git a/packages/SystemUI/res/values-si/strings.xml b/packages/SystemUI/res/values-si/strings.xml index 90c87f06be68..458d9eeaad62 100644 --- a/packages/SystemUI/res/values-si/strings.xml +++ b/packages/SystemUI/res/values-si/strings.xml @@ -270,12 +270,20 @@ <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"සියල්ල බලන්න"</string> <string name="turn_on_bluetooth" msgid="5681370462180289071">"බ්ලූටූත් භාවිතා කරන්න"</string> <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"සම්බන්ධිතයි"</string> + <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) --> + <skip /> <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_info_disabled" msgid="8267380591344023327">"ඉක්මන් බෙදා ගැනීම, මගේ උපාංගය සෙවීම, සහ උපාංග ස්ථානය වැනි විශේෂාංග බ්ලූටූත් භාවිත කරයි"</string> - <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"බ්ලූටූත් හෙට පෙ.ව. 5ට ක්රියාත්මක වනු ඇත"</string> + <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) --> + <skip /> + <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) --> + <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> @@ -350,8 +358,7 @@ <string name="qs_record_issue_label" msgid="8166290137285529059">"පටිගත කිරීමේ ගැටලුව"</string> <string name="qs_record_issue_start" msgid="2979831312582567056">"අරඹන්න"</string> <string name="qs_record_issue_stop" msgid="3531747965741982657">"නවත්වන්න"</string> - <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) --> - <skip /> + <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"දෝෂ වර්තාව"</string> <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> diff --git a/packages/SystemUI/res/values-sk/strings.xml b/packages/SystemUI/res/values-sk/strings.xml index 7751c6d09d34..c8c2ee5d073f 100644 --- a/packages/SystemUI/res/values-sk/strings.xml +++ b/packages/SystemUI/res/values-sk/strings.xml @@ -270,12 +270,20 @@ <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Zobraziť všetko"</string> <string name="turn_on_bluetooth" msgid="5681370462180289071">"Použiť Bluetooth"</string> <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Pripojené"</string> + <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) --> + <skip /> <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Uložené"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"odpojiť"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktivovať"</string> <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Automaticky zajtra znova zapnúť"</string> - <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Funkcie, ako sú Quick Share, Nájdi moje zariadenie a poloha zariadenia, používajú Bluetooth"</string> - <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth sa zapne zajtra o 5:00."</string> + <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) --> + <skip /> + <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) --> + <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> diff --git a/packages/SystemUI/res/values-sl/strings.xml b/packages/SystemUI/res/values-sl/strings.xml index 53be1bde3369..03021991593e 100644 --- a/packages/SystemUI/res/values-sl/strings.xml +++ b/packages/SystemUI/res/values-sl/strings.xml @@ -270,12 +270,20 @@ <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Pokaži vse"</string> <string name="turn_on_bluetooth" msgid="5681370462180289071">"Uporabi Bluetooth"</string> <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Povezano"</string> + <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) --> + <skip /> <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Shranjeno"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"prekinitev povezave"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktiviranje"</string> <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Samodejno znova vklopi jutri"</string> - <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Funkcije, kot so Hitro deljenje, Poišči mojo napravo in zaznavanje lokacije naprave, uporabljajo Bluetooth"</string> - <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth se bo vklopil jutri ob 5. uri"</string> + <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) --> + <skip /> + <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) --> + <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> @@ -1302,5 +1310,5 @@ <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> - <string name="home_controls_dream_description" msgid="4644150952104035789">"Hiter dostop do kontrol. za dom na ohranj. zaslona"</string> + <string name="home_controls_dream_description" msgid="4644150952104035789">"Hiter dostop do kontrolnikov za dom na ohranjevalniku zaslona"</string> </resources> diff --git a/packages/SystemUI/res/values-sq/strings.xml b/packages/SystemUI/res/values-sq/strings.xml index 7e75984a4c35..8e621ce9fd88 100644 --- a/packages/SystemUI/res/values-sq/strings.xml +++ b/packages/SystemUI/res/values-sq/strings.xml @@ -270,12 +270,20 @@ <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Shiko të gjitha"</string> <string name="turn_on_bluetooth" msgid="5681370462180289071">"Përdor Bluetooth-in"</string> <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Lidhur"</string> + <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) --> + <skip /> <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Ruajtur"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"shkëput"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktivizo"</string> <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Aktivizoje automatikisht nesër"</string> - <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Veçoritë si \"Ndarja e shpejtë\", \"Gjej pajisjen time\" dhe vendndodhja e pajisjes përdorin Bluetooth-in"</string> - <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth-i do të aktivizohet nesër në 5:00"</string> + <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) --> + <skip /> + <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) --> + <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> @@ -350,8 +358,7 @@ <string name="qs_record_issue_label" msgid="8166290137285529059">"Regjistro problemin"</string> <string name="qs_record_issue_start" msgid="2979831312582567056">"Nis"</string> <string name="qs_record_issue_stop" msgid="3531747965741982657">"Ndalo"</string> - <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) --> - <skip /> + <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Raporti i defekteve në kod"</string> <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> diff --git a/packages/SystemUI/res/values-sr/strings.xml b/packages/SystemUI/res/values-sr/strings.xml index 2b1882dbcf64..a1825bfecc5f 100644 --- a/packages/SystemUI/res/values-sr/strings.xml +++ b/packages/SystemUI/res/values-sr/strings.xml @@ -270,12 +270,20 @@ <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Прикажи све"</string> <string name="turn_on_bluetooth" msgid="5681370462180289071">"Користи Bluetooth"</string> <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Повезано"</string> + <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) --> + <skip /> <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_info_disabled" msgid="8267380591344023327">"Функције као што су Quick Share, Пронађи мој уређај и локација уређаја користе Bluetooth"</string> - <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth ће се укључити сутра у 5:00"</string> + <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) --> + <skip /> + <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) --> + <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> diff --git a/packages/SystemUI/res/values-sv/strings.xml b/packages/SystemUI/res/values-sv/strings.xml index f075c8c94cb7..abb5d098f7d4 100644 --- a/packages/SystemUI/res/values-sv/strings.xml +++ b/packages/SystemUI/res/values-sv/strings.xml @@ -270,12 +270,20 @@ <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Se alla"</string> <string name="turn_on_bluetooth" msgid="5681370462180289071">"Använd Bluetooth"</string> <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Ansluten"</string> + <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) --> + <skip /> <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Sparad"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"koppla från"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"aktivera"</string> <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Aktivera automatiskt igen i morgon"</string> - <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Funktioner som Snabbdelning, Hitta min enhet och enhetens plats använder Bluetooth"</string> - <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth aktiveras i morgon kl. 5.00"</string> + <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) --> + <skip /> + <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) --> + <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> @@ -350,8 +358,7 @@ <string name="qs_record_issue_label" msgid="8166290137285529059">"Registrera problem"</string> <string name="qs_record_issue_start" msgid="2979831312582567056">"Starta"</string> <string name="qs_record_issue_stop" msgid="3531747965741982657">"Stoppa"</string> - <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) --> - <skip /> + <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Felrapport"</string> <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Vilken enhetsupplevelse påverkades?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Välj problemtyp"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Skärminspelning"</string> @@ -1303,5 +1310,5 @@ <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> - <string name="home_controls_dream_description" msgid="4644150952104035789">"Kom snabbt åt hemstyrningen som en skärmsläckare"</string> + <string name="home_controls_dream_description" msgid="4644150952104035789">"Kom snabbt åt hemstyrningen via skärmsläckaren"</string> </resources> diff --git a/packages/SystemUI/res/values-sw/strings.xml b/packages/SystemUI/res/values-sw/strings.xml index 102926b0246c..392a74d97450 100644 --- a/packages/SystemUI/res/values-sw/strings.xml +++ b/packages/SystemUI/res/values-sw/strings.xml @@ -270,12 +270,20 @@ <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Angalia vyote"</string> <string name="turn_on_bluetooth" msgid="5681370462180289071">"Tumia Bluetooth"</string> <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Imeunganishwa"</string> + <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) --> + <skip /> <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Imehifadhiwa"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ondoa"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"anza kutumia"</string> <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Iwashe tena kesho kiotomatiki"</string> - <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Vipengele kama vile Kutuma Haraka, Tafuta Kifaa Changu na mahali kifaa kilipo hutumia Bluetooth"</string> - <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth itawaka kesho saa 11 alfajiri"</string> + <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) --> + <skip /> + <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) --> + <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> @@ -1301,6 +1309,6 @@ <string name="privacy_dialog_recent_app_usage_2" msgid="2874689735085367167">"Ilitumiwa hivi majuzi na <xliff:g id="APP_NAME">%1$s</xliff:g> (<xliff:g id="ATTRIBUTION_LABEL">%2$s</xliff:g> • <xliff:g id="PROXY_LABEL">%3$s</xliff:g>)"</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">"Vidhibiti vya Vifaa Nyumbani"</string> - <string name="home_controls_dream_description" msgid="4644150952104035789">"Fikia haraka vidhibiti vya vifaa nyumbani kama taswira ya skrini"</string> + <string name="home_controls_dream_label" msgid="6567105701292324257">"Dhibiti Vifaa Nyumbani"</string> + <string name="home_controls_dream_description" msgid="4644150952104035789">"Fikia haraka vidhibiti vya vifaa nyumbani vikiwa taswira ya skrini"</string> </resources> diff --git a/packages/SystemUI/res/values-ta/strings.xml b/packages/SystemUI/res/values-ta/strings.xml index 9afa0d1db9eb..dfc00df3f9d3 100644 --- a/packages/SystemUI/res/values-ta/strings.xml +++ b/packages/SystemUI/res/values-ta/strings.xml @@ -270,12 +270,20 @@ <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"அனைத்தையும் காட்டு"</string> <string name="turn_on_bluetooth" msgid="5681370462180289071">"புளூடூத்தைப் பயன்படுத்துதல்"</string> <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"இணைக்கப்பட்டது"</string> + <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) --> + <skip /> <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_info_disabled" msgid="8267380591344023327">"விரைவுப் பகிர்தல், Find My Device போன்ற அம்சங்களும் சாதன இருப்பிடமும் புளூடூத்தைப் பயன்படுத்துகின்றன"</string> - <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"நாளை 5 AMக்கு புளூடூத் ஆன் ஆகும்"</string> + <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) --> + <skip /> + <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) --> + <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> @@ -350,8 +358,7 @@ <string name="qs_record_issue_label" msgid="8166290137285529059">"சிக்கலை ரெக்கார்டு செய்தல்"</string> <string name="qs_record_issue_start" msgid="2979831312582567056">"தொடங்குங்கள்"</string> <string name="qs_record_issue_stop" msgid="3531747965741982657">"நிறுத்துங்கள்"</string> - <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) --> - <skip /> + <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"பிழை அறிக்கை"</string> <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> @@ -1303,5 +1310,5 @@ <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> - <string name="home_controls_dream_description" msgid="4644150952104035789">"ஹோம் கன்ட்ரோல்களை ஸ்கிரீன் சேவராக விரைவாக அணுகலாம்"</string> + <string name="home_controls_dream_description" msgid="4644150952104035789">"ஹோம் கன்ட்ரோல்களை ஸ்கிரீன் சேவராக அணுகலாம்"</string> </resources> diff --git a/packages/SystemUI/res/values-te/strings.xml b/packages/SystemUI/res/values-te/strings.xml index 7f82ebf46a71..518883fea543 100644 --- a/packages/SystemUI/res/values-te/strings.xml +++ b/packages/SystemUI/res/values-te/strings.xml @@ -270,12 +270,20 @@ <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"అన్నింటినీ చూడండి"</string> <string name="turn_on_bluetooth" msgid="5681370462180289071">"బ్లూటూత్ వాడండి"</string> <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"కనెక్ట్ అయింది"</string> + <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) --> + <skip /> <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_info_disabled" msgid="8267380591344023327">"క్విక్ షేర్, Find My Device, పరికర లొకేషన్ వంటి ఫీచర్లు బ్లూటూత్ను ఉపయోగిస్తాయి"</string> - <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"బ్లూటూత్ రేపు ఉదయం 5 గంటలకు ఆన్ అవుతుంది"</string> + <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) --> + <skip /> + <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) --> + <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> @@ -1302,5 +1310,5 @@ <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> - <string name="home_controls_dream_description" msgid="4644150952104035789">"స్క్రీన్ సేవర్గా మీ హోమ్ కంట్రోల్స్ను త్వరగా యాక్సెస్ చేయండి"</string> + <string name="home_controls_dream_description" msgid="4644150952104035789">"హోమ్ కంట్రోల్స్ను స్క్రీన్ సేవర్గా చేసి వేగంగా యాక్సెస్ పొందండి"</string> </resources> diff --git a/packages/SystemUI/res/values-th/strings.xml b/packages/SystemUI/res/values-th/strings.xml index dece9c1ffeb5..77f506cc16ec 100644 --- a/packages/SystemUI/res/values-th/strings.xml +++ b/packages/SystemUI/res/values-th/strings.xml @@ -260,7 +260,7 @@ <string name="accessibility_rotation_lock_on_landscape" msgid="936972553861524360">"ขณะนี้หน้าจอถูกล็อกให้วางในแนวนอน"</string> <string name="accessibility_rotation_lock_on_portrait" msgid="2356633398683813837">"ขณะนี้หน้าจอถูกล็อกให้วางในแนวตั้ง"</string> <string name="dessert_case" msgid="9104973640704357717">"ชั้นแสดงของหวาน"</string> - <string name="start_dreams" msgid="9131802557946276718">"โปรแกรมรักษาหน้าจอ"</string> + <string name="start_dreams" msgid="9131802557946276718">"ภาพพักหน้าจอ"</string> <string name="ethernet_label" msgid="2203544727007463351">"อีเทอร์เน็ต"</string> <string name="quick_settings_dnd_label" msgid="7728690179108024338">"ห้ามรบกวน"</string> <string name="quick_settings_bluetooth_label" msgid="7018763367142041481">"บลูทูธ"</string> @@ -270,12 +270,20 @@ <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"ดูทั้งหมด"</string> <string name="turn_on_bluetooth" msgid="5681370462180289071">"ใช้บลูทูธ"</string> <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"เชื่อมต่อแล้ว"</string> + <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) --> + <skip /> <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_info_disabled" msgid="8267380591344023327">"ฟีเจอร์ต่างๆ เช่น Quick Share, หาอุปกรณ์ของฉัน และตำแหน่งของอุปกรณ์ ใช้บลูทูธ"</string> - <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"บลูทูธจะเปิดพรุ่งนี้เวลา 05:00 น."</string> + <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) --> + <skip /> + <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) --> + <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> @@ -747,7 +755,7 @@ <string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"ไม่พบแป้นพิมพ์ลัด"</string> <string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"ระบบ"</string> <string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"อินพุต"</string> - <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"แอปที่เปิดอยู่"</string> + <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"เปิดแอป"</string> <string name="keyboard_shortcut_search_category_current_app" msgid="2011953559133734491">"แอปปัจจุบัน"</string> <string name="keyboard_shortcut_a11y_show_search_results" msgid="2865241062981833705">"แสดงผลการค้นหา"</string> <string name="keyboard_shortcut_a11y_filter_system" msgid="7744143131119370483">"แสดงแป้นพิมพ์ลัดสำหรับระบบ"</string> @@ -1302,5 +1310,5 @@ <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> - <string name="home_controls_dream_description" msgid="4644150952104035789">"เข้าถึงระบบควบคุมอุปกรณ์สมาร์ทโฮมได้อย่างรวดเร็วที่การพักหน้าจอ"</string> + <string name="home_controls_dream_description" msgid="4644150952104035789">"เข้าถึงระบบควบคุมอุปกรณ์สมาร์ทโฮมได้อย่างรวดเร็วผ่านภาพพักหน้าจอ"</string> </resources> diff --git a/packages/SystemUI/res/values-tl/strings.xml b/packages/SystemUI/res/values-tl/strings.xml index 9f408f4168e8..922bf880e48b 100644 --- a/packages/SystemUI/res/values-tl/strings.xml +++ b/packages/SystemUI/res/values-tl/strings.xml @@ -270,12 +270,20 @@ <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Tingnan lahat"</string> <string name="turn_on_bluetooth" msgid="5681370462180289071">"Gumamit ng Bluetooth"</string> <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Nakakonekta"</string> + <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) --> + <skip /> <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Na-save"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"idiskonekta"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"i-activate"</string> <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Awtomatikong i-on ulit bukas"</string> - <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Guamgamit ng Bluetooth ang mga feature tulad ng Quick Share, Hanapin ang Aking Device, at lokasyon ng device"</string> - <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Mag-o-on ang Bluetooth bukas nang 5 AM"</string> + <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) --> + <skip /> + <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) --> + <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> diff --git a/packages/SystemUI/res/values-tr/strings.xml b/packages/SystemUI/res/values-tr/strings.xml index c07c35037642..35f13dece0f9 100644 --- a/packages/SystemUI/res/values-tr/strings.xml +++ b/packages/SystemUI/res/values-tr/strings.xml @@ -270,12 +270,20 @@ <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Tümünü göster"</string> <string name="turn_on_bluetooth" msgid="5681370462180289071">"Bluetooth\'u kullan"</string> <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Bağlandı"</string> + <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) --> + <skip /> <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Kaydedildi"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"bağlantıyı kes"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"etkinleştir"</string> <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="8267380591344023327">"Quick Share, Cihazımı Bul ve cihaz konumu gibi özellikler Bluetooth\'u kullanır"</string> - <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth yarın saat 05:00\'te açılacak"</string> + <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) --> + <skip /> + <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) --> + <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> @@ -350,8 +358,7 @@ <string name="qs_record_issue_label" msgid="8166290137285529059">"Sorunu Kaydedin"</string> <string name="qs_record_issue_start" msgid="2979831312582567056">"Başlayın"</string> <string name="qs_record_issue_stop" msgid="3531747965741982657">"Durdurun"</string> - <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) --> - <skip /> + <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Hata Raporu"</string> <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> @@ -1303,5 +1310,5 @@ <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> - <string name="home_controls_dream_description" msgid="4644150952104035789">"Ekran koruyucu olan ev kontrollerinize hızlıca erişin"</string> + <string name="home_controls_dream_description" msgid="4644150952104035789">"Ekran koruyucu olarak ev kontrollerinize hızla erişin"</string> </resources> diff --git a/packages/SystemUI/res/values-uk/strings.xml b/packages/SystemUI/res/values-uk/strings.xml index 65630348c22c..0d97121636b3 100644 --- a/packages/SystemUI/res/values-uk/strings.xml +++ b/packages/SystemUI/res/values-uk/strings.xml @@ -270,12 +270,20 @@ <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Показати всі"</string> <string name="turn_on_bluetooth" msgid="5681370462180289071">"Увімкнути Bluetooth"</string> <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Підключено"</string> + <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) --> + <skip /> <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_info_disabled" msgid="8267380591344023327">"Такі функції, як швидкий обмін, \"Знайти пристрій\" і визначення місцезнаходження пристрою, використовують Bluetooth"</string> - <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth увімкнеться завтра о 5:00"</string> + <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) --> + <skip /> + <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) --> + <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> @@ -350,8 +358,7 @@ <string name="qs_record_issue_label" msgid="8166290137285529059">"Запис помилки"</string> <string name="qs_record_issue_start" msgid="2979831312582567056">"Почати"</string> <string name="qs_record_issue_stop" msgid="3531747965741982657">"Зупинити"</string> - <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) --> - <skip /> + <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Звіт про помилку"</string> <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> diff --git a/packages/SystemUI/res/values-ur/strings.xml b/packages/SystemUI/res/values-ur/strings.xml index 452beaf4b07b..888be9c28dc5 100644 --- a/packages/SystemUI/res/values-ur/strings.xml +++ b/packages/SystemUI/res/values-ur/strings.xml @@ -270,12 +270,20 @@ <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"سبھی دیکھیں"</string> <string name="turn_on_bluetooth" msgid="5681370462180289071">"بلوٹوتھ استعمال کریں"</string> <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"منسلک ہے"</string> + <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) --> + <skip /> <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_info_disabled" msgid="8267380591344023327">"فوری اشتراک، میرا آلہ ڈھونڈیں، اور آلہ کے مقام جیسی خصوصیات بلوٹوتھ کا استعمال کرتی ہیں"</string> - <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"بلوٹوتھ کل صبح 5 بجے آن ہو جائے گا"</string> + <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) --> + <skip /> + <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) --> + <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> diff --git a/packages/SystemUI/res/values-uz/strings.xml b/packages/SystemUI/res/values-uz/strings.xml index 7a5cbad4aae1..6581db528afe 100644 --- a/packages/SystemUI/res/values-uz/strings.xml +++ b/packages/SystemUI/res/values-uz/strings.xml @@ -270,12 +270,20 @@ <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Hammasi"</string> <string name="turn_on_bluetooth" msgid="5681370462180289071">"Bluetooth ishlatish"</string> <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Ulangan"</string> + <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) --> + <skip /> <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Saqlangan"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"uzish"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"faollashtirish"</string> <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Ertaga yana avtomatik yoqilsin"</string> - <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Tezkor ulashuv, Qurilmamni top va qurilma geolokatsiyasi kabi funksiyalar Bluetooth ishlatadi"</string> - <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth ertaga soat 5 da yoqiladi"</string> + <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) --> + <skip /> + <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) --> + <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> @@ -350,8 +358,7 @@ <string name="qs_record_issue_label" msgid="8166290137285529059">"Yozib olishda xato"</string> <string name="qs_record_issue_start" msgid="2979831312582567056">"Boshlash"</string> <string name="qs_record_issue_stop" msgid="3531747965741982657">"Toʻxtatish"</string> - <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) --> - <skip /> + <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Xatoliklar hisoboti"</string> <string name="qs_record_issue_dropdown_header" msgid="5995983175678658329">"Qurilma ishlashining qaysi qismiga taʼsir qildi?"</string> <string name="qs_record_issue_dropdown_prompt" msgid="2526949919167046219">"Muammo turini tanlang"</string> <string name="qs_record_issue_dropdown_screenrecord" msgid="6396141928484257626">"Ekran yozuvi"</string> diff --git a/packages/SystemUI/res/values-vi/strings.xml b/packages/SystemUI/res/values-vi/strings.xml index 85421eef947f..cce810e7002d 100644 --- a/packages/SystemUI/res/values-vi/strings.xml +++ b/packages/SystemUI/res/values-vi/strings.xml @@ -270,12 +270,20 @@ <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Xem tất cả"</string> <string name="turn_on_bluetooth" msgid="5681370462180289071">"Bật Bluetooth"</string> <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Đã kết nối"</string> + <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) --> + <skip /> <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Đã lưu"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"ngắt kết nối"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"kích hoạt"</string> <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="8267380591344023327">"Các tính năng như Chia sẻ nhanh, Tìm thiết bị của tôi và dịch vụ vị trí trên thiết bị có sử dụng Bluetooth"</string> - <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"Bluetooth sẽ bật vào ngày mai lúc 5 giờ sáng"</string> + <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) --> + <skip /> + <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) --> + <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> @@ -350,8 +358,7 @@ <string name="qs_record_issue_label" msgid="8166290137285529059">"Ghi lại vấn đề"</string> <string name="qs_record_issue_start" msgid="2979831312582567056">"Bắt đầu"</string> <string name="qs_record_issue_stop" msgid="3531747965741982657">"Dừng"</string> - <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) --> - <skip /> + <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Báo cáo lỗi"</string> <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> diff --git a/packages/SystemUI/res/values-zh-rCN/strings.xml b/packages/SystemUI/res/values-zh-rCN/strings.xml index d23a0fd97bc4..f683133c0942 100644 --- a/packages/SystemUI/res/values-zh-rCN/strings.xml +++ b/packages/SystemUI/res/values-zh-rCN/strings.xml @@ -270,12 +270,20 @@ <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"查看全部"</string> <string name="turn_on_bluetooth" msgid="5681370462180289071">"使用蓝牙"</string> <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"已连接"</string> + <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) --> + <skip /> <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_info_disabled" msgid="8267380591344023327">"“快速分享”“查找我的设备”“设备位置信息”等功能会使用蓝牙"</string> - <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"蓝牙将于明天早晨 5 点开启"</string> + <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) --> + <skip /> + <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) --> + <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> @@ -350,8 +358,7 @@ <string name="qs_record_issue_label" msgid="8166290137285529059">"录制问题"</string> <string name="qs_record_issue_start" msgid="2979831312582567056">"开始"</string> <string name="qs_record_issue_stop" msgid="3531747965741982657">"停止"</string> - <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) --> - <skip /> + <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"错误报告"</string> <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> @@ -748,7 +755,7 @@ <string name="keyboard_shortcut_search_list_no_result" msgid="6819302191660875501">"未找到任何快捷键"</string> <string name="keyboard_shortcut_search_category_system" msgid="1151182120757052669">"系统"</string> <string name="keyboard_shortcut_search_category_input" msgid="5440558509904296233">"输入"</string> - <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"已打开的应用"</string> + <string name="keyboard_shortcut_search_category_open_apps" msgid="1450959949739257562">"打开应用"</string> <string name="keyboard_shortcut_search_category_current_app" msgid="2011953559133734491">"当前应用"</string> <string name="keyboard_shortcut_a11y_show_search_results" msgid="2865241062981833705">"目前显示的是搜索结果"</string> <string name="keyboard_shortcut_a11y_filter_system" msgid="7744143131119370483">"目前显示的是系统快捷键"</string> diff --git a/packages/SystemUI/res/values-zh-rHK/strings.xml b/packages/SystemUI/res/values-zh-rHK/strings.xml index 04565e53be1c..e6fcb7fd6589 100644 --- a/packages/SystemUI/res/values-zh-rHK/strings.xml +++ b/packages/SystemUI/res/values-zh-rHK/strings.xml @@ -270,12 +270,20 @@ <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"查看全部"</string> <string name="turn_on_bluetooth" msgid="5681370462180289071">"使用藍牙"</string> <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"已連接"</string> + <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) --> + <skip /> <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_info_disabled" msgid="8267380591344023327">"「快速共享」、「尋找我的裝置」和裝置位置等功能都會使用藍牙"</string> - <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"藍牙將於明天上午 5 時開啟"</string> + <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) --> + <skip /> + <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) --> + <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> @@ -350,8 +358,7 @@ <string name="qs_record_issue_label" msgid="8166290137285529059">"錄製問題"</string> <string name="qs_record_issue_start" msgid="2979831312582567056">"開始"</string> <string name="qs_record_issue_stop" msgid="3531747965741982657">"停止"</string> - <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) --> - <skip /> + <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"錯誤報告"</string> <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> @@ -1303,5 +1310,5 @@ <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> - <string name="home_controls_dream_description" msgid="4644150952104035789">"在螢幕保護程式畫面上快速存取家居控制功能"</string> + <string name="home_controls_dream_description" msgid="4644150952104035789">"在螢幕保護程式畫面上控制智能家居"</string> </resources> diff --git a/packages/SystemUI/res/values-zh-rTW/strings.xml b/packages/SystemUI/res/values-zh-rTW/strings.xml index 11d2c3ed0b0f..b02bf81fd883 100644 --- a/packages/SystemUI/res/values-zh-rTW/strings.xml +++ b/packages/SystemUI/res/values-zh-rTW/strings.xml @@ -270,12 +270,20 @@ <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"查看全部"</string> <string name="turn_on_bluetooth" msgid="5681370462180289071">"使用藍牙"</string> <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"已連線"</string> + <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) --> + <skip /> <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_info_disabled" msgid="8267380591344023327">"藍牙會用於快速分享、「尋找我的裝置」,以及裝置位置資訊等功能"</string> - <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"藍牙將在明天早上 5 點開啟"</string> + <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) --> + <skip /> + <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) --> + <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> @@ -350,8 +358,7 @@ <string name="qs_record_issue_label" msgid="8166290137285529059">"記錄問題"</string> <string name="qs_record_issue_start" msgid="2979831312582567056">"開始"</string> <string name="qs_record_issue_stop" msgid="3531747965741982657">"停止"</string> - <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) --> - <skip /> + <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"錯誤報告"</string> <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> diff --git a/packages/SystemUI/res/values-zu/strings.xml b/packages/SystemUI/res/values-zu/strings.xml index 6f87762c1b41..a4d66a4e3595 100644 --- a/packages/SystemUI/res/values-zu/strings.xml +++ b/packages/SystemUI/res/values-zu/strings.xml @@ -270,12 +270,20 @@ <string name="see_all_bluetooth_devices" msgid="1761596816620200433">"Buka konke"</string> <string name="turn_on_bluetooth" msgid="5681370462180289071">"Sebenzisa i-Bluetooth"</string> <string name="quick_settings_bluetooth_device_connected" msgid="7884777006729260996">"Ixhunyiwe"</string> + <!-- no translation found for quick_settings_bluetooth_device_audio_sharing (1496358082943301670) --> + <skip /> <string name="quick_settings_bluetooth_device_saved" msgid="7549938728928069477">"Ilondoloziwe"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_disconnect" msgid="415980329093277342">"nqamula"</string> <string name="accessibility_quick_settings_bluetooth_device_tap_to_activate" msgid="3724301751036877403">"yenza kusebenze"</string> <string name="turn_on_bluetooth_auto_tomorrow" msgid="414836329962473906">"Vula ngokuzenzekela futhi kusasa"</string> - <string name="turn_on_bluetooth_auto_info_disabled" msgid="8267380591344023327">"Izakhi ezifana Nokwabelana Ngokushesha, okuthi Thola Idivayisi Yami, kanye nendawo yedivayisi zisebenzisa i-Bluetooth"</string> - <string name="turn_on_bluetooth_auto_info_enabled" msgid="4802071533678400330">"I-Bluetooth izovulwa kusasa ngo-5 AM"</string> + <!-- no translation found for turn_on_bluetooth_auto_info_disabled (682984290339848844) --> + <skip /> + <!-- no translation found for turn_on_bluetooth_auto_info_enabled (7440944034584560279) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button (4499275822759907822) --> + <skip /> + <!-- no translation found for quick_settings_bluetooth_audio_sharing_button_sharing (8626191139359072540) --> + <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> @@ -350,8 +358,7 @@ <string name="qs_record_issue_label" msgid="8166290137285529059">"Rekhoda Inkinga"</string> <string name="qs_record_issue_start" msgid="2979831312582567056">"Qala"</string> <string name="qs_record_issue_stop" msgid="3531747965741982657">"Misa"</string> - <!-- no translation found for qs_record_issue_bug_report (8229031766918650079) --> - <skip /> + <string name="qs_record_issue_bug_report" msgid="8229031766918650079">"Umbiko Wesiphazamisi"</string> <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> diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java index 458a21c5c426..75d925dc4ab0 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardInputViewController.java @@ -107,7 +107,10 @@ public abstract class KeyguardInputViewController<T extends KeyguardInputView> } } - private void updateMessageAreaVisibility() { + /** + * Determines whether to show the message area controlled by MessageAreaController. + */ + public void updateMessageAreaVisibility() { if (mMessageAreaController == null) return; if (Flags.revampedBouncerMessages()) { mMessageAreaController.disable(); diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java index 558679e993e1..3ef3418bfed4 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPinViewController.java @@ -118,6 +118,12 @@ public class KeyguardSimPinViewController } @Override + public void updateMessageAreaVisibility() { + if (mMessageAreaController == null) return; + mMessageAreaController.setIsVisible(true); + } + + @Override void resetState() { super.resetState(); if (DEBUG) Log.v(TAG, "Resetting state"); diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java index cb1c4b3064ce..46225c7ea58a 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java @@ -115,6 +115,12 @@ public class KeyguardSimPukViewController } @Override + public void updateMessageAreaVisibility() { + if (mMessageAreaController == null) return; + mMessageAreaController.setIsVisible(true); + } + + @Override public void onResume(int reason) { super.onResume(reason); if (mShowDefaultMessage) { diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt index 61d1c713fb77..4a60d195ea36 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt @@ -323,7 +323,7 @@ class UdfpsControllerOverlay @JvmOverloads constructor( overlayParams = updatedOverlayParams sensorBounds = updatedOverlayParams.sensorBounds getTouchOverlay()?.let { - if (addViewRunnable != null) { + if (addViewRunnable == null) { // Only updateViewLayout if there's no pending view to add to WM. // If there is a pending view, that means the view hasn't been added yet so there's // no need to update any layouts. Instead the correct params will be used when the diff --git a/packages/SystemUI/src/com/android/systemui/common/coroutine/ConflatedCallbackFlow.kt b/packages/SystemUI/src/com/android/systemui/common/coroutine/ConflatedCallbackFlow.kt index d4a1f74234ef..0c181e99b21c 100644 --- a/packages/SystemUI/src/com/android/systemui/common/coroutine/ConflatedCallbackFlow.kt +++ b/packages/SystemUI/src/com/android/systemui/common/coroutine/ConflatedCallbackFlow.kt @@ -16,14 +16,14 @@ package com.android.systemui.common.coroutine +import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow as wrapped import kotlin.experimental.ExperimentalTypeInference -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.channels.ProducerScope import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.buffer import kotlinx.coroutines.flow.callbackFlow +@Deprecated("Use com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow instead") object ConflatedCallbackFlow { /** @@ -32,9 +32,15 @@ object ConflatedCallbackFlow { * consumer(s) of the values in the flow), the values are buffered and, if the buffer fills up, * we drop the oldest values automatically instead of suspending the producer. */ - @Suppress("EXPERIMENTAL_IS_NOT_ENABLED") - @OptIn(ExperimentalTypeInference::class, ExperimentalCoroutinesApi::class) + @Deprecated( + "Use com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow instead", + ReplaceWith( + "conflatedCallbackFlow", + "com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow" + ) + ) + @OptIn(ExperimentalTypeInference::class) fun <T> conflatedCallbackFlow( @BuilderInference block: suspend ProducerScope<T>.() -> Unit, - ): Flow<T> = callbackFlow(block).buffer(capacity = Channel.CONFLATED) + ): Flow<T> = wrapped(block) } diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/domain/interactor/ConfigurationInteractor.kt b/packages/SystemUI/src/com/android/systemui/common/ui/domain/interactor/ConfigurationInteractor.kt index 5f6ff82c6038..638af5844c3e 100644 --- a/packages/SystemUI/src/com/android/systemui/common/ui/domain/interactor/ConfigurationInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/common/ui/domain/interactor/ConfigurationInteractor.kt @@ -56,6 +56,13 @@ class ConfigurationInteractor @Inject constructor(private val repository: Config val naturalMaxBounds: Flow<Rect> = repository.configurationValues.map { it.naturalScreenBounds }.distinctUntilChanged() + /** + * The layout direction. Will be either `View#LAYOUT_DIRECTION_LTR` or + * `View#LAYOUT_DIRECTION_RTL`. + */ + val layoutDirection: Flow<Int> = + repository.configurationValues.map { it.layoutDirection }.distinctUntilChanged() + /** Given [resourceId], emit the dimension pixel size on config change */ fun dimensionPixelSize(resourceId: Int): Flow<Int> { return onAnyConfigurationChange.mapLatest { repository.getDimensionPixelSize(resourceId) } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java index 165de7cea9e6..21af0a07a6d8 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java +++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java @@ -1373,7 +1373,7 @@ public class KeyguardViewMediator implements CoreStartable, Dumpable, private final Lazy<DreamViewModel> mDreamViewModel; private final Lazy<CommunalTransitionViewModel> mCommunalTransitionViewModel; private RemoteAnimationTarget mRemoteAnimationTarget; - private Boolean mShowCommunalByDefault; + private boolean mShowCommunalByDefault = false; private final Lazy<WindowManagerLockscreenVisibilityManager> mWmLockscreenVisibilityManager; diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractor.kt index 80e94a27bec5..20b7b2a91ade 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractor.kt @@ -23,12 +23,14 @@ import com.android.systemui.keyguard.domain.interactor.WindowManagerLockscreenVi import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.KeyguardSurfaceBehindModel import com.android.systemui.statusbar.notification.domain.interactor.NotificationLaunchAnimationInteractor +import com.android.systemui.util.kotlin.sample import com.android.systemui.util.kotlin.toPx import dagger.Lazy import javax.inject.Inject import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.map /** * Distance over which the surface behind the keyguard is animated in during a Y-translation @@ -96,13 +98,21 @@ constructor( .distinctUntilChanged() /** + * Whether a notification launch animation is running when we're not already in the GONE state. + */ + private val isNotificationLaunchAnimationRunningOnKeyguard = + notificationLaunchInteractor.isLaunchAnimationRunning + .sample(transitionInteractor.finishedKeyguardState) + .map { it != KeyguardState.GONE } + + /** * Whether we're animating the surface, or a notification launch animation is running (which * means we're going to animate the surface, even if animators aren't yet running). */ val isAnimatingSurface = combine( repository.isAnimatingSurface, - notificationLaunchInteractor.isLaunchAnimationRunning + isNotificationLaunchAnimationRunningOnKeyguard, ) { animatingSurface, animatingLaunch -> animatingSurface || animatingLaunch } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt index 6255f0d44609..7178e1bd9357 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinder.kt @@ -36,7 +36,6 @@ import com.android.systemui.keyguard.ui.view.layout.sections.ClockSection import com.android.systemui.keyguard.ui.viewmodel.KeyguardClockViewModel import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.plugins.clocks.ClockController -import com.android.systemui.shared.clocks.DEFAULT_CLOCK_ID import kotlinx.coroutines.launch object KeyguardClockViewBinder { @@ -76,13 +75,13 @@ object KeyguardClockViewBinder { } launch { if (!MigrateClocksToBlueprint.isEnabled) return@launch - viewModel.clockShouldBeCentered.collect { clockShouldBeCentered -> + viewModel.clockShouldBeCentered.collect { viewModel.currentClock.value?.let { - // Weather clock also has hasCustomPositionUpdatedAnimation as true - // TODO(b/323020908): remove ID check + // TODO(b/301502635): remove "!it.config.useCustomClockScene" when + // migrate clocks to blueprint is fully rolled out if ( it.largeClock.config.hasCustomPositionUpdatedAnimation && - it.config.id == DEFAULT_CLOCK_ID + !it.config.useCustomClockScene ) { blueprintInteractor.refreshBlueprint(Type.DefaultClockStepping) } else { @@ -93,12 +92,9 @@ object KeyguardClockViewBinder { } launch { if (!MigrateClocksToBlueprint.isEnabled) return@launch - viewModel.isAodIconsVisible.collect { isAodIconsVisible -> + viewModel.isAodIconsVisible.collect { viewModel.currentClock.value?.let { - // Weather clock also has hasCustomPositionUpdatedAnimation as true - if ( - viewModel.useLargeClock && it.config.id == "DIGITAL_CLOCK_WEATHER" - ) { + if (viewModel.useLargeClock && it.config.useCustomClockScene) { blueprintInteractor.refreshBlueprint(Type.DefaultTransition) } } diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt index 5ee35e4f8eb6..cc54920236da 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardRootViewBinder.kt @@ -30,6 +30,9 @@ import android.view.ViewGroup import android.view.ViewGroup.OnHierarchyChangeListener import android.view.ViewPropertyAnimator import android.view.WindowInsets +import androidx.activity.OnBackPressedDispatcher +import androidx.activity.OnBackPressedDispatcherOwner +import androidx.activity.setViewTreeOnBackPressedDispatcherOwner import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle import com.android.app.animation.Interpolators @@ -49,6 +52,7 @@ import com.android.systemui.keyguard.KeyguardBottomAreaRefactor import com.android.systemui.keyguard.KeyguardViewMediator import com.android.systemui.keyguard.MigrateClocksToBlueprint import com.android.systemui.keyguard.domain.interactor.KeyguardClockInteractor +import com.android.systemui.keyguard.shared.ComposeLockscreen import com.android.systemui.keyguard.shared.model.KeyguardState import com.android.systemui.keyguard.shared.model.TransitionState import com.android.systemui.keyguard.ui.viewmodel.BurnInParameters @@ -125,6 +129,21 @@ object KeyguardRootViewBinder { disposables += view.repeatWhenAttached { repeatOnLifecycle(Lifecycle.State.CREATED) { + if (ComposeLockscreen.isEnabled) { + view.setViewTreeOnBackPressedDispatcherOwner( + object : OnBackPressedDispatcherOwner { + override val onBackPressedDispatcher = + OnBackPressedDispatcher().apply { + setOnBackInvokedDispatcher( + view.viewRootImpl.onBackInvokedDispatcher + ) + } + + override val lifecycle: Lifecycle = + this@repeatWhenAttached.lifecycle + } + ) + } launch { occludingAppDeviceEntryMessageViewModel.message.collect { biometricMessage -> diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModel.kt index 06a0c7291f92..5a559fc3aa45 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModel.kt @@ -18,61 +18,29 @@ package com.android.systemui.keyguard.ui.viewmodel import android.graphics.Color -import com.android.systemui.keyguard.domain.interactor.FromAlternateBouncerTransitionInteractor.Companion.TRANSITION_DURATION_MS +import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER -import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager -import com.android.wm.shell.animation.Interpolators import javax.inject.Inject import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.merge @ExperimentalCoroutinesApi class AlternateBouncerViewModel @Inject constructor( private val statusBarKeyguardViewManager: StatusBarKeyguardViewManager, - animationFlow: KeyguardTransitionAnimationFlow, + keyguardTransitionInteractor: KeyguardTransitionInteractor, ) { // When we're fully transitioned to the AlternateBouncer, the alpha of the scrim should be: private val alternateBouncerScrimAlpha = .66f - private val toAlternateBouncerTransition = - animationFlow - .setup( - duration = TRANSITION_DURATION_MS, - from = null, - to = ALTERNATE_BOUNCER, - ) - .sharedFlow( - duration = TRANSITION_DURATION_MS, - onStep = { it }, - onFinish = { 1f }, - // Reset on cancel - onCancel = { 0f }, - interpolator = Interpolators.FAST_OUT_SLOW_IN, - ) - private val fromAlternateBouncerTransition = - animationFlow - .setup( - TRANSITION_DURATION_MS, - from = ALTERNATE_BOUNCER, - to = null, - ) - .sharedFlow( - duration = TRANSITION_DURATION_MS, - onStep = { 1f - it }, - // Reset on cancel - onCancel = { 0f }, - interpolator = Interpolators.FAST_OUT_SLOW_IN, - ) /** Progress to a fully transitioned alternate bouncer. 1f represents fully transitioned. */ val transitionToAlternateBouncerProgress = - merge(fromAlternateBouncerTransition, toAlternateBouncerTransition) + keyguardTransitionInteractor.transitionValue(ALTERNATE_BOUNCER) val forcePluginOpen: Flow<Boolean> = transitionToAlternateBouncerProgress.map { it > 0f }.distinctUntilChanged() 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 f6da033f1bd3..a6d3312fd6d2 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 @@ -118,8 +118,7 @@ constructor( currentClock ) { isLargeClockVisible, clockShouldBeCentered, shadeMode, currentClock -> val shouldUseSplitShade = shadeMode == ShadeMode.Split - // TODO(b/326098079): make id a constant field in config - if (currentClock?.config?.id == "DIGITAL_CLOCK_WEATHER") { + if (currentClock?.config?.useCustomClockScene == true) { val weatherClockLayout = when { shouldUseSplitShade && clockShouldBeCentered -> diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImpl.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImpl.kt index e8d32746086d..5432a189cf7c 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImpl.kt @@ -69,7 +69,11 @@ constructor( private val mediaFlags: MediaFlags, private val mediaFilterRepository: MediaFilterRepository, ) : MediaDataManager.Listener { - lateinit var mediaDataManager: MediaDataManager + /** Non-UI listeners to media changes. */ + private val _listeners: MutableSet<MediaDataProcessor.Listener> = mutableSetOf() + val listeners: Set<MediaDataProcessor.Listener> + get() = _listeners.toSet() + lateinit var mediaDataProcessor: MediaDataProcessor // Ensure the field (and associated reference) isn't removed during optimization. @KeepForWeakReference @@ -113,6 +117,9 @@ constructor( mediaFilterRepository.addMediaDataLoadingState( MediaDataLoadingModel.Loaded(data.instanceId) ) + + // Notify listeners + listeners.forEach { it.onMediaDataLoaded(key, oldKey, data) } } override fun onSmartspaceMediaDataLoaded( @@ -171,6 +178,20 @@ constructor( mediaFilterRepository.addMediaDataLoadingState( MediaDataLoadingModel.Loaded(lastActiveId) ) + listeners.forEach { listener -> + getKey(lastActiveId)?.let { lastActiveKey -> + listener.onMediaDataLoaded( + lastActiveKey, + lastActiveKey, + mediaData, + receivedSmartspaceCardLatency = + (systemClock.currentTimeMillis() - + data.headphoneConnectionTimeMillis) + .toInt(), + isSsReactivated = true + ) + } + } } } else if (data.isActive) { // Mark to prioritize Smartspace card if no recent media. @@ -189,6 +210,7 @@ constructor( mediaFilterRepository.setRecommendationsLoadingState( SmartspaceMediaLoadingModel.Loaded(key, shouldPrioritizeMutable) ) + listeners.forEach { it.onSmartspaceMediaDataLoaded(key, data, shouldPrioritizeMutable) } } override fun onMediaDataRemoved(key: String) { @@ -198,6 +220,8 @@ constructor( mediaFilterRepository.addMediaDataLoadingState( MediaDataLoadingModel.Removed(instanceId) ) + // Only notify listeners if something actually changed + listeners.forEach { it.onMediaDataRemoved(key) } } } } @@ -212,6 +236,11 @@ constructor( mediaFilterRepository.addMediaDataLoadingState( MediaDataLoadingModel.Loaded(lastActiveId, immediately) ) + listeners.forEach { listener -> + getKey(lastActiveId)?.let { lastActiveKey -> + listener.onMediaDataLoaded(lastActiveKey, lastActiveKey, it, immediately) + } + } } } @@ -227,6 +256,7 @@ constructor( mediaFilterRepository.setRecommendationsLoadingState( SmartspaceMediaLoadingModel.Removed(key, immediately) ) + listeners.forEach { it.onSmartspaceMediaDataRemoved(key, immediately) } } @VisibleForTesting @@ -240,6 +270,7 @@ constructor( mediaFilterRepository.addMediaDataLoadingState( MediaDataLoadingModel.Removed(data.instanceId) ) + listeners.forEach { listener -> listener.onMediaDataRemoved(key) } } } } @@ -247,6 +278,7 @@ constructor( @VisibleForTesting internal fun handleUserSwitched() { // If the user changes, remove all current MediaData objects. + val listenersCopy = listeners val keyCopy = mediaFilterRepository.selectedUserEntries.value.keys.toMutableList() // Clear the list first and update loading state to remove media from UI. mediaFilterRepository.clearSelectedUserMedia() @@ -255,6 +287,9 @@ constructor( mediaFilterRepository.addMediaDataLoadingState( MediaDataLoadingModel.Removed(instanceId) ) + getKey(instanceId)?.let { + listenersCopy.forEach { listener -> listener.onMediaDataRemoved(it) } + } } mediaFilterRepository.allUserEntries.value.forEach { (key, data) -> @@ -268,6 +303,7 @@ constructor( mediaFilterRepository.addMediaDataLoadingState( MediaDataLoadingModel.Loaded(data.instanceId) ) + listenersCopy.forEach { listener -> listener.onMediaDataLoaded(key, null, data) } } } } @@ -279,7 +315,7 @@ constructor( mediaEntries.forEach { (key, data) -> if (mediaFilterRepository.selectedUserEntries.value.containsKey(data.instanceId)) { // Force updates to listeners, needed for re-activated card - mediaDataManager.setInactive(key, timedOut = true, forceUpdate = true) + mediaDataProcessor.setInactive(key, timedOut = true, forceUpdate = true) } } val smartspaceMediaData = mediaFilterRepository.smartspaceMediaData.value @@ -301,7 +337,7 @@ constructor( if (mediaFlags.isPersistentSsCardEnabled()) { mediaFilterRepository.setRecommendation(smartspaceMediaData.copy(isActive = false)) - mediaDataManager.setRecommendationInactive(smartspaceMediaData.targetId) + mediaDataProcessor.setRecommendationInactive(smartspaceMediaData.targetId) } else { mediaFilterRepository.setRecommendation( EMPTY_SMARTSPACE_MEDIA_DATA.copy( @@ -309,7 +345,7 @@ constructor( instanceId = smartspaceMediaData.instanceId, ) ) - mediaDataManager.dismissSmartspaceRecommendation( + mediaDataProcessor.dismissSmartspaceRecommendation( smartspaceMediaData.targetId, delay = 0L, ) @@ -317,6 +353,12 @@ constructor( } } + /** Add a listener for filtered [MediaData] changes */ + fun addListener(listener: MediaDataProcessor.Listener) = _listeners.add(listener) + + /** Remove a listener that was registered with addListener */ + fun removeListener(listener: MediaDataProcessor.Listener) = _listeners.remove(listener) + /** * Return the time since last active for the most-recent media. * @@ -336,6 +378,16 @@ constructor( return sortedEntries[lastActiveInstanceId]?.let { now - it.lastActive } ?: Long.MAX_VALUE } + private fun getKey(instanceId: InstanceId): String? { + val allEntries = mediaFilterRepository.allUserEntries.value + val filteredEntries = allEntries.filter { (_, data) -> data.instanceId == instanceId } + return if (filteredEntries.isNotEmpty()) { + filteredEntries.keys.first() + } else { + null + } + } + companion object { /** * Maximum age of a media control to re-activate on smartspace signal. If there is no media diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt index c7cfb0b7d775..0e2814b3d91e 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/MediaDeviceManager.kt @@ -35,6 +35,7 @@ import com.android.settingslib.flags.Flags.legacyLeAudioSharing import com.android.settingslib.media.LocalMediaManager import com.android.settingslib.media.MediaDevice import com.android.settingslib.media.PhoneMediaDevice +import com.android.settingslib.media.flags.Flags import com.android.systemui.dagger.qualifiers.Background import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.media.controls.shared.model.MediaData @@ -178,7 +179,9 @@ constructor( bgExecutor.execute { if (!started) { localMediaManager.registerCallback(this) - localMediaManager.startScan() + if (!Flags.removeUnnecessaryRouteScanning()) { + localMediaManager.startScan() + } muteAwaitConnectionManager.startListening() playbackType = controller?.playbackInfo?.playbackType ?: PLAYBACK_TYPE_UNKNOWN playbackVolumeControlId = controller?.playbackInfo?.volumeControlId @@ -195,7 +198,9 @@ constructor( if (started) { started = false controller?.unregisterCallback(this) - localMediaManager.stopScan() + if (!Flags.removeUnnecessaryRouteScanning()) { + localMediaManager.stopScan() + } localMediaManager.unregisterCallback(this) muteAwaitConnectionManager.stopListening() configurationController.removeCallback(configListener) diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractor.kt b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractor.kt index c3ba913c2105..b04e93835418 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/domain/pipeline/interactor/MediaCarouselInteractor.kt @@ -37,6 +37,7 @@ import com.android.systemui.media.controls.domain.resume.MediaResumeListener import com.android.systemui.media.controls.shared.model.MediaCommonModel import com.android.systemui.media.controls.shared.model.MediaDataLoadingModel import com.android.systemui.media.controls.shared.model.SmartspaceMediaLoadingModel +import com.android.systemui.media.controls.util.MediaControlsRefactorFlag import com.android.systemui.media.controls.util.MediaFlags import java.io.PrintWriter import javax.inject.Inject @@ -156,13 +157,19 @@ constructor( mediaDataProcessor.onSessionDestroyed(key) } mediaResumeListener.setManager(this) - mediaDataFilter.mediaDataManager = this + mediaDataFilter.mediaDataProcessor = mediaDataProcessor } - override fun setInactive(key: String, timedOut: Boolean, forceUpdate: Boolean) { - mediaDataProcessor.setInactive(key, timedOut, forceUpdate) + override fun addListener(listener: MediaDataManager.Listener) { + mediaDataFilter.addListener(listener) } + override fun removeListener(listener: MediaDataManager.Listener) { + mediaDataFilter.removeListener(listener) + } + + override fun setInactive(key: String, timedOut: Boolean, forceUpdate: Boolean) = unsupported + override fun onNotificationAdded(key: String, sbn: StatusBarNotification) { mediaDataProcessor.onNotificationAdded(key, sbn) } @@ -207,9 +214,7 @@ constructor( return mediaDataProcessor.dismissSmartspaceRecommendation(key, delay) } - override fun setRecommendationInactive(key: String) { - mediaDataProcessor.setRecommendationInactive(key) - } + override fun setRecommendationInactive(key: String) = unsupported override fun onNotificationRemoved(key: String) { mediaDataProcessor.onNotificationRemoved(key) @@ -240,4 +245,12 @@ constructor( override fun dump(pw: PrintWriter, args: Array<out String>) { mediaDeviceManager.dump(pw) } + + companion object { + val unsupported: Nothing + get() = + error( + "Code path not supported when ${MediaControlsRefactorFlag.FLAG_NAME} is enabled" + ) + } } diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaControlViewBinder.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaControlViewBinder.kt new file mode 100644 index 000000000000..14a917999bb7 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/binder/MediaControlViewBinder.kt @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.media.controls.ui.binder + +import android.widget.ImageButton +import androidx.constraintlayout.widget.ConstraintSet +import com.android.systemui.res.R + +object MediaControlViewBinder { + + fun setVisibleAndAlpha(set: ConstraintSet, resId: Int, visible: Boolean) { + setVisibleAndAlpha(set, resId, visible, ConstraintSet.GONE) + } + + private fun setVisibleAndAlpha( + set: ConstraintSet, + resId: Int, + visible: Boolean, + notVisibleValue: Int + ) { + set.setVisibility(resId, if (visible) ConstraintSet.VISIBLE else notVisibleValue) + set.setAlpha(resId, if (visible) 1.0f else 0.0f) + } + + fun updateSeekBarVisibility(constraintSet: ConstraintSet, isSeekBarEnabled: Boolean) { + if (isSeekBarEnabled) { + constraintSet.setVisibility(R.id.media_progress_bar, ConstraintSet.VISIBLE) + constraintSet.setAlpha(R.id.media_progress_bar, 1.0f) + } else { + constraintSet.setVisibility(R.id.media_progress_bar, ConstraintSet.INVISIBLE) + constraintSet.setAlpha(R.id.media_progress_bar, 0.0f) + } + } + + fun setSemanticButtonVisibleAndAlpha( + button: ImageButton, + expandedSet: ConstraintSet, + collapsedSet: ConstraintSet, + visible: Boolean, + notVisibleValue: Int, + showInCollapsed: Boolean + ) { + if (notVisibleValue == ConstraintSet.INVISIBLE) { + // Since time views should appear instead of buttons. + button.isFocusable = visible + button.isClickable = visible + } + setVisibleAndAlpha(expandedSet, button.id, visible, notVisibleValue) + setVisibleAndAlpha(collapsedSet, button.id, visible = visible && showInCollapsed) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt index b315cac28953..7fced5f8036f 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/controller/MediaViewController.kt @@ -16,41 +16,73 @@ package com.android.systemui.media.controls.ui.controller +import android.animation.Animator +import android.animation.AnimatorInflater +import android.animation.AnimatorSet import android.content.Context import android.content.res.Configuration +import android.graphics.Color +import android.graphics.Paint +import android.graphics.drawable.Drawable +import android.provider.Settings +import android.view.View +import android.view.animation.Interpolator import androidx.annotation.VisibleForTesting import androidx.constraintlayout.widget.ConstraintSet import androidx.constraintlayout.widget.ConstraintSet.MATCH_CONSTRAINT +import com.android.app.animation.Interpolators import com.android.app.tracing.traceSection +import com.android.systemui.Flags +import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.media.controls.ui.animation.ColorSchemeTransition +import com.android.systemui.media.controls.ui.animation.MetadataAnimationHandler +import com.android.systemui.media.controls.ui.binder.MediaControlViewBinder +import com.android.systemui.media.controls.ui.binder.SeekBarObserver import com.android.systemui.media.controls.ui.controller.MediaCarouselController.Companion.calculateAlpha import com.android.systemui.media.controls.ui.view.GutsViewHolder import com.android.systemui.media.controls.ui.view.MediaHostState import com.android.systemui.media.controls.ui.view.MediaViewHolder import com.android.systemui.media.controls.ui.view.RecommendationViewHolder +import com.android.systemui.media.controls.ui.viewmodel.MediaControlViewModel +import com.android.systemui.media.controls.ui.viewmodel.SeekBarViewModel import com.android.systemui.media.controls.util.MediaFlags import com.android.systemui.res.R import com.android.systemui.statusbar.policy.ConfigurationController +import com.android.systemui.surfaceeffects.PaintDrawCallback +import com.android.systemui.surfaceeffects.loadingeffect.LoadingEffect +import com.android.systemui.surfaceeffects.loadingeffect.LoadingEffectView +import com.android.systemui.surfaceeffects.ripple.MultiRippleController +import com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseAnimationConfig +import com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseController +import com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseShader +import com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseView import com.android.systemui.util.animation.MeasurementInput import com.android.systemui.util.animation.MeasurementOutput import com.android.systemui.util.animation.TransitionLayout import com.android.systemui.util.animation.TransitionLayoutController import com.android.systemui.util.animation.TransitionViewState +import com.android.systemui.util.concurrency.DelayableExecutor +import com.android.systemui.util.settings.GlobalSettings import java.lang.Float.max import java.lang.Float.min +import java.util.Random import javax.inject.Inject /** * A class responsible for controlling a single instance of a media player handling interactions * with the view instance and keeping the media view states up to date. */ -class MediaViewController +open class MediaViewController @Inject constructor( private val context: Context, private val configurationController: ConfigurationController, private val mediaHostStatesManager: MediaHostStatesManager, private val logger: MediaViewLogger, + private val seekBarViewModel: SeekBarViewModel, + @Main private val mainExecutor: DelayableExecutor, private val mediaFlags: MediaFlags, + private val globalSettings: GlobalSettings, ) { /** @@ -131,6 +163,72 @@ constructor( return transitionLayout?.translationY ?: 0.0f } + /** Whether artwork is bound. */ + var isArtworkBound: Boolean = false + + /** previous background artwork */ + var prevArtwork: Drawable? = null + + /** Whether scrubbing time can show */ + var canShowScrubbingTime: Boolean = false + + /** Whether user is touching the seek bar to change the position */ + var isScrubbing: Boolean = false + + var isSeekBarEnabled: Boolean = false + + /** Not visible value for previous button when scrubbing */ + private var prevNotVisibleValue = ConstraintSet.GONE + private var isPrevButtonAvailable = false + + /** Not visible value for next button when scrubbing */ + private var nextNotVisibleValue = ConstraintSet.GONE + private var isNextButtonAvailable = false + + private lateinit var mediaViewHolder: MediaViewHolder + private lateinit var seekBarObserver: SeekBarObserver + private lateinit var turbulenceNoiseController: TurbulenceNoiseController + private lateinit var loadingEffect: LoadingEffect + private lateinit var turbulenceNoiseAnimationConfig: TurbulenceNoiseAnimationConfig + private lateinit var noiseDrawCallback: PaintDrawCallback + private lateinit var stateChangedCallback: LoadingEffect.AnimationStateChangedCallback + internal lateinit var metadataAnimationHandler: MetadataAnimationHandler + internal lateinit var colorSchemeTransition: ColorSchemeTransition + internal lateinit var multiRippleController: MultiRippleController + + private val scrubbingChangeListener = + object : SeekBarViewModel.ScrubbingChangeListener { + override fun onScrubbingChanged(scrubbing: Boolean) { + if (!mediaFlags.isMediaControlsRefactorEnabled()) return + if (isScrubbing == scrubbing) return + isScrubbing = scrubbing + updateDisplayForScrubbingChange() + } + } + + private val enabledChangeListener = + object : SeekBarViewModel.EnabledChangeListener { + override fun onEnabledChanged(enabled: Boolean) { + if (!mediaFlags.isMediaControlsRefactorEnabled()) return + if (isSeekBarEnabled == enabled) return + isSeekBarEnabled = enabled + MediaControlViewBinder.updateSeekBarVisibility(expandedLayout, isSeekBarEnabled) + } + } + + /** + * Sets the listening state of the player. + * + * Should be set to true when the QS panel is open. Otherwise, false. This is a signal to avoid + * unnecessary work when the QS panel is closed. + * + * @param listening True when player should be active. Otherwise, false. + */ + fun setListening(listening: Boolean) { + if (!mediaFlags.isMediaControlsRefactorEnabled()) return + seekBarViewModel.listening = listening + } + /** A callback for config changes */ private val configurationListener = object : ConfigurationController.ConfigurationListener { @@ -232,6 +330,14 @@ constructor( * Notify this controller that the view has been removed and all listeners should be destroyed */ fun onDestroy() { + if (mediaFlags.isMediaControlsRefactorEnabled()) { + if (this::seekBarObserver.isInitialized) { + seekBarViewModel.progress.removeObserver(seekBarObserver) + } + seekBarViewModel.removeScrubbingChangeListener(scrubbingChangeListener) + seekBarViewModel.removeEnabledChangeListener(enabledChangeListener) + seekBarViewModel.onDestroy() + } mediaHostStatesManager.removeController(this) configurationController.removeCallback(configurationListener) } @@ -546,6 +652,178 @@ constructor( ) } + fun attachPlayer(mediaViewHolder: MediaViewHolder) { + if (!mediaFlags.isMediaControlsRefactorEnabled()) return + this.mediaViewHolder = mediaViewHolder + + // Setting up seek bar. + seekBarObserver = SeekBarObserver(mediaViewHolder) + seekBarViewModel.progress.observeForever(seekBarObserver) + seekBarViewModel.attachTouchHandlers(mediaViewHolder.seekBar) + seekBarViewModel.setScrubbingChangeListener(scrubbingChangeListener) + seekBarViewModel.setEnabledChangeListener(enabledChangeListener) + + val mediaCard = mediaViewHolder.player + attach(mediaViewHolder.player, TYPE.PLAYER) + + val turbulenceNoiseView = mediaViewHolder.turbulenceNoiseView + turbulenceNoiseController = TurbulenceNoiseController(turbulenceNoiseView) + + multiRippleController = MultiRippleController(mediaViewHolder.multiRippleView) + + // Metadata Animation + val titleText = mediaViewHolder.titleText + val artistText = mediaViewHolder.artistText + val explicitIndicator = mediaViewHolder.explicitIndicator + val enter = + loadAnimator( + mediaCard.context, + R.anim.media_metadata_enter, + Interpolators.EMPHASIZED_DECELERATE, + titleText, + artistText, + explicitIndicator + ) + val exit = + loadAnimator( + mediaCard.context, + R.anim.media_metadata_exit, + Interpolators.EMPHASIZED_ACCELERATE, + titleText, + artistText, + explicitIndicator + ) + metadataAnimationHandler = MetadataAnimationHandler(exit, enter) + + colorSchemeTransition = + ColorSchemeTransition( + mediaCard.context, + mediaViewHolder, + multiRippleController, + turbulenceNoiseController + ) + + // For Turbulence noise. + val loadingEffectView = mediaViewHolder.loadingEffectView + turbulenceNoiseAnimationConfig = + createTurbulenceNoiseConfig( + loadingEffectView, + turbulenceNoiseView, + colorSchemeTransition + ) + noiseDrawCallback = + object : PaintDrawCallback { + override fun onDraw(paint: Paint) { + loadingEffectView.draw(paint) + } + } + stateChangedCallback = + object : LoadingEffect.AnimationStateChangedCallback { + override fun onStateChanged( + oldState: LoadingEffect.AnimationState, + newState: LoadingEffect.AnimationState + ) { + if (newState === LoadingEffect.AnimationState.NOT_PLAYING) { + loadingEffectView.visibility = View.INVISIBLE + } else { + loadingEffectView.visibility = View.VISIBLE + } + } + } + } + + fun updateAnimatorDurationScale() { + if (!mediaFlags.isMediaControlsRefactorEnabled()) return + if (this::seekBarObserver.isInitialized) { + seekBarObserver.animationEnabled = + globalSettings.getFloat(Settings.Global.ANIMATOR_DURATION_SCALE, 1f) > 0f + } + } + + /** update view with the needed UI changes when user touches seekbar. */ + private fun updateDisplayForScrubbingChange() { + mainExecutor.execute { + val isTimeVisible = canShowScrubbingTime && isScrubbing + MediaControlViewBinder.setVisibleAndAlpha( + expandedLayout, + mediaViewHolder.scrubbingTotalTimeView.id, + isTimeVisible + ) + MediaControlViewBinder.setVisibleAndAlpha( + expandedLayout, + mediaViewHolder.scrubbingElapsedTimeView.id, + isTimeVisible + ) + + MediaControlViewModel.SEMANTIC_ACTIONS_HIDE_WHEN_SCRUBBING.forEach { id -> + val isButtonVisible: Boolean + val notVisibleValue: Int + when (id) { + R.id.actionPrev -> { + isButtonVisible = isPrevButtonAvailable && !isTimeVisible + notVisibleValue = prevNotVisibleValue + } + R.id.actionNext -> { + isButtonVisible = isNextButtonAvailable && !isTimeVisible + notVisibleValue = nextNotVisibleValue + } + else -> { + isButtonVisible = !isTimeVisible + notVisibleValue = ConstraintSet.GONE + } + } + MediaControlViewBinder.setSemanticButtonVisibleAndAlpha( + mediaViewHolder.getAction(id), + expandedLayout, + collapsedLayout, + isButtonVisible, + notVisibleValue, + showInCollapsed = true + ) + } + + if (!metadataAnimationHandler.isRunning) { + refreshState() + } + } + } + + fun bindSeekBar(onSeek: () -> Unit, onBindSeekBar: (SeekBarViewModel) -> Unit) { + if (!mediaFlags.isMediaControlsRefactorEnabled()) return + seekBarViewModel.logSeek = onSeek + onBindSeekBar.invoke(seekBarViewModel) + } + + fun setUpTurbulenceNoise() { + if (!mediaFlags.isMediaControlsRefactorEnabled()) return + if (Flags.shaderlibLoadingEffectRefactor()) { + if (!this::loadingEffect.isInitialized) { + loadingEffect = + LoadingEffect( + TurbulenceNoiseShader.Companion.Type.SIMPLEX_NOISE, + turbulenceNoiseAnimationConfig, + noiseDrawCallback, + stateChangedCallback + ) + } + colorSchemeTransition.loadingEffect = loadingEffect + loadingEffect.play() + mainExecutor.executeDelayed( + loadingEffect::finish, + MediaControlViewModel.TURBULENCE_NOISE_PLAY_MS_DURATION + ) + } else { + turbulenceNoiseController.play( + TurbulenceNoiseShader.Companion.Type.SIMPLEX_NOISE, + turbulenceNoiseAnimationConfig + ) + mainExecutor.executeDelayed( + turbulenceNoiseController::finish, + MediaControlViewModel.TURBULENCE_NOISE_PLAY_MS_DURATION + ) + } + } + /** * Obtain a measurement for a given location. This makes sure that the state is up to date and * all widgets know their location. Calling this method may create a measurement if we don't @@ -801,6 +1079,75 @@ constructor( applyImmediately = true ) } + + @VisibleForTesting + protected open fun loadAnimator( + context: Context, + animId: Int, + motionInterpolator: Interpolator?, + vararg targets: View? + ): AnimatorSet { + val animators = ArrayList<Animator>() + for (target in targets) { + val animator = AnimatorInflater.loadAnimator(context, animId) as AnimatorSet + animator.childAnimations[0].interpolator = motionInterpolator + animator.setTarget(target) + animators.add(animator) + } + val result = AnimatorSet() + result.playTogether(animators) + return result + } + + private fun createTurbulenceNoiseConfig( + loadingEffectView: LoadingEffectView, + turbulenceNoiseView: TurbulenceNoiseView, + colorSchemeTransition: ColorSchemeTransition + ): TurbulenceNoiseAnimationConfig { + val targetView: View = + if (Flags.shaderlibLoadingEffectRefactor()) { + loadingEffectView + } else { + turbulenceNoiseView + } + val width = targetView.width + val height = targetView.height + val random = Random() + return TurbulenceNoiseAnimationConfig( + gridCount = 2.14f, + TurbulenceNoiseAnimationConfig.DEFAULT_LUMINOSITY_MULTIPLIER, + random.nextFloat(), + random.nextFloat(), + random.nextFloat(), + noiseMoveSpeedX = 0.42f, + noiseMoveSpeedY = 0f, + TurbulenceNoiseAnimationConfig.DEFAULT_NOISE_SPEED_Z, + // Color will be correctly updated in ColorSchemeTransition. + colorSchemeTransition.accentPrimary.currentColor, + screenColor = Color.BLACK, + width.toFloat(), + height.toFloat(), + TurbulenceNoiseAnimationConfig.DEFAULT_MAX_DURATION_IN_MILLIS, + easeInDuration = 1350f, + easeOutDuration = 1350f, + targetView.context.resources.displayMetrics.density, + lumaMatteBlendFactor = 0.26f, + lumaMatteOverallBrightness = 0.09f, + shouldInverseNoiseLuminosity = false + ) + } + + fun setUpPrevButtonInfo(isAvailable: Boolean, notVisibleValue: Int = ConstraintSet.GONE) { + if (!mediaFlags.isMediaControlsRefactorEnabled()) return + isPrevButtonAvailable = isAvailable + prevNotVisibleValue = notVisibleValue + } + + fun setUpNextButtonInfo(isAvailable: Boolean, notVisibleValue: Int = ConstraintSet.GONE) { + if (!mediaFlags.isMediaControlsRefactorEnabled()) return + isNextButtonAvailable = isAvailable + nextNotVisibleValue = notVisibleValue + } } /** An internal key for the cache of mediaViewStates. This is a subset of the full host state. */ diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaActionViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaActionViewModel.kt index 1e67a77250ee..82099e61009f 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaActionViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaActionViewModel.kt @@ -24,7 +24,8 @@ data class MediaActionViewModel( val icon: Drawable?, val contentDescription: CharSequence?, val background: Drawable?, - val isVisible: Boolean = true, + /** whether action is visible if user is touching seekbar to change position. */ + val isVisibleWhenScrubbing: Boolean = true, val notVisibleValue: Int = ConstraintSet.GONE, val showInCollapsed: Boolean, val rebindId: Int? = null, diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt index 7c599953f9b9..d74506dc2e8d 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModel.kt @@ -18,6 +18,7 @@ package com.android.systemui.media.controls.ui.viewmodel import android.content.Context import android.content.pm.PackageManager +import android.media.session.MediaController import android.media.session.MediaSession.Token import android.text.TextUtils import android.util.Log @@ -40,6 +41,7 @@ import com.android.systemui.monet.ColorScheme import com.android.systemui.monet.Style import com.android.systemui.res.R import com.android.systemui.util.kotlin.sample +import java.util.concurrent.Executor import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow @@ -51,6 +53,7 @@ import kotlinx.coroutines.flow.flowOn class MediaControlViewModel( @Application private val applicationContext: Context, @Background private val backgroundDispatcher: CoroutineDispatcher, + @Background private val backgroundExecutor: Executor, private val interactor: MediaControlInteractor, private val logger: MediaUiEventLogger, ) { @@ -124,13 +127,15 @@ class MediaControlViewModel( } }, backgroundCover = model.artwork, - appIcon = getAppIcon(model.appIcon, model.isResume, model.packageName), + appIcon = model.appIcon, + launcherIcon = getIconFromApp(model.packageName), useGrayColorFilter = model.appIcon == null || model.isResume, artistName = model.artistName ?: "", titleName = model.songName ?: "", isExplicitVisible = model.showExplicit, + shouldAddGradient = wallpaperColors != null, colorScheme = scheme, - isTimeVisible = canShowScrubbingTimeViews(model.semanticActionButtons), + canShowTime = canShowScrubbingTimeViews(model.semanticActionButtons), playTurbulenceNoise = playTurbulenceNoise, useSemanticActions = model.semanticActionButtons != null, actionButtons = toActionViewModels(model), @@ -146,6 +151,21 @@ class MediaControlViewModel( onLongClicked = { logger.logLongPressOpen(model.uid, model.packageName, model.instanceId) }, + onSeek = { + logger.logSeek(model.uid, model.packageName, model.instanceId) + // TODO (b/330897926) log smartspace card reported (SMARTSPACE_CARD_CLICK_EVENT) + }, + onBindSeekbar = { seekBarViewModel -> + if (model.isResume && model.resumeProgress != null) { + seekBarViewModel.updateStaticProgress(model.resumeProgress) + } else { + backgroundExecutor.execute { + seekBarViewModel.updateController( + model.token?.let { MediaController(applicationContext, it) } + ) + } + } + } ) } @@ -278,16 +298,16 @@ class MediaControlViewModel( model: MediaControlModel, mediaAction: MediaAction, buttonId: Int, - isScrubbingTimeEnabled: Boolean + canShowScrubbingTimeViews: Boolean ): MediaActionViewModel { val showInCollapsed = SEMANTIC_ACTIONS_COMPACT.contains(buttonId) val hideWhenScrubbing = SEMANTIC_ACTIONS_HIDE_WHEN_SCRUBBING.contains(buttonId) - val shouldHideDueToScrubbing = isScrubbingTimeEnabled && hideWhenScrubbing + val shouldHideWhenScrubbing = canShowScrubbingTimeViews && hideWhenScrubbing return MediaActionViewModel( icon = mediaAction.icon, contentDescription = mediaAction.contentDescription, background = mediaAction.background, - isVisible = !shouldHideDueToScrubbing, + isVisibleWhenScrubbing = !shouldHideWhenScrubbing, notVisibleValue = if ( (buttonId == R.id.actionPrev && model.semanticActionButtons!!.reservePrev) || @@ -342,19 +362,6 @@ class MediaControlViewModel( action.run() } - private fun getAppIcon( - icon: android.graphics.drawable.Icon?, - isResume: Boolean, - packageName: String - ): Icon { - if (icon != null && !isResume) { - icon.loadDrawable(applicationContext)?.let { drawable -> - return Icon.Loaded(drawable, null) - } - } - return getIconFromApp(packageName) - } - private fun getIconFromApp(packageName: String): Icon { return try { Icon.Loaded(applicationContext.packageManager.getApplicationIcon(packageName), null) @@ -381,17 +388,17 @@ class MediaControlViewModel( private const val DISABLED_ALPHA = 0.38f /** Buttons to show in small player when using semantic actions */ - private val SEMANTIC_ACTIONS_COMPACT = + val SEMANTIC_ACTIONS_COMPACT = listOf(R.id.actionPlayPause, R.id.actionPrev, R.id.actionNext) /** * Buttons that should get hidden when we are scrubbing (they will be replaced with the * views showing scrubbing time) */ - private val SEMANTIC_ACTIONS_HIDE_WHEN_SCRUBBING = listOf(R.id.actionPrev, R.id.actionNext) + val SEMANTIC_ACTIONS_HIDE_WHEN_SCRUBBING = listOf(R.id.actionPrev, R.id.actionNext) /** Buttons to show in player when using semantic actions. */ - private val SEMANTIC_ACTIONS_ALL = + val SEMANTIC_ACTIONS_ALL = listOf( R.id.actionPlayPause, R.id.actionPrev, @@ -399,5 +406,9 @@ class MediaControlViewModel( R.id.action0, R.id.action1 ) + + const val TURBULENCE_NOISE_PLAY_MS_DURATION = 7500L + const val MEDIA_PLAYER_SCRIM_START_ALPHA = 0.25f + const val MEDIA_PLAYER_SCRIM_END_ALPHA = 1.0f } } diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaPlayerViewModel.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaPlayerViewModel.kt index 9029a65bd9ee..d1014e83ea11 100644 --- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaPlayerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/viewmodel/MediaPlayerViewModel.kt @@ -24,13 +24,15 @@ import com.android.systemui.monet.ColorScheme data class MediaPlayerViewModel( val contentDescription: (Boolean) -> CharSequence, val backgroundCover: android.graphics.drawable.Icon?, - val appIcon: Icon, + val appIcon: android.graphics.drawable.Icon?, + val launcherIcon: Icon, val useGrayColorFilter: Boolean, val artistName: CharSequence, val titleName: CharSequence, val isExplicitVisible: Boolean, + val shouldAddGradient: Boolean, val colorScheme: ColorScheme, - val isTimeVisible: Boolean, + val canShowTime: Boolean, val playTurbulenceNoise: Boolean, val useSemanticActions: Boolean, val actionButtons: List<MediaActionViewModel?>, @@ -38,4 +40,6 @@ data class MediaPlayerViewModel( val gutsMenu: GutsViewModel, val onClicked: (Expandable) -> Unit, val onLongClicked: () -> Unit, + val onSeek: () -> Unit, + val onBindSeekbar: (SeekBarViewModel) -> Unit, ) diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/scroll/LongScreenshotActivity.java b/packages/SystemUI/src/com/android/systemui/screenshot/scroll/LongScreenshotActivity.java index 706ac9c46be1..b43a1d23da24 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/scroll/LongScreenshotActivity.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/scroll/LongScreenshotActivity.java @@ -23,6 +23,7 @@ import android.content.ContentProvider; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.HardwareRenderer; +import android.graphics.Insets; import android.graphics.Matrix; import android.graphics.RecordingCanvas; import android.graphics.Rect; @@ -38,9 +39,11 @@ import android.util.Log; import android.view.Display; import android.view.ScrollCaptureResponse; import android.view.View; +import android.view.WindowInsets; import android.widget.ImageView; import androidx.constraintlayout.widget.ConstraintLayout; +import androidx.core.view.WindowCompat; import com.android.internal.app.ChooserActivity; import com.android.internal.logging.UiEventLogger; @@ -127,6 +130,10 @@ public class LongScreenshotActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + + // Enable edge-to-edge explicitly. + WindowCompat.setDecorFitsSystemWindows(getWindow(), false); + setContentView(R.layout.long_screenshot); mPreview = requireViewById(R.id.preview); @@ -149,6 +156,13 @@ public class LongScreenshotActivity extends Activity { (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> updateImageDimensions()); + requireViewById(R.id.root).setOnApplyWindowInsetsListener( + (view, windowInsets) -> { + Insets insets = windowInsets.getInsets(WindowInsets.Type.systemBars()); + view.setPadding(insets.left, insets.top, insets.right, insets.bottom); + return WindowInsets.CONSUMED; + }); + Intent intent = getIntent(); mScrollCaptureResponse = intent.getParcelableExtra(EXTRA_CAPTURE_RESPONSE); mScreenshotUserHandle = intent.getParcelableExtra(EXTRA_SCREENSHOT_USER_HANDLE, diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java index 00bc752c1b83..a763641841d9 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java @@ -659,6 +659,9 @@ public class NotificationShadeWindowViewController implements Dumpable { mTouchCancelled = true; } mAmbientState.setSwipingUp(false); + if (MigrateClocksToBlueprint.isEnabled()) { + mDragDownHelper.stopDragging(); + } } @Override diff --git a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt index 980f665ae61f..5b76acb1df58 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModel.kt @@ -37,6 +37,7 @@ import com.android.systemui.settings.brightness.ui.viewModel.BrightnessMirrorVie import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.shade.shared.model.ShadeMode import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationsPlaceholderViewModel +import com.android.systemui.unfold.domain.interactor.UnfoldTransitionInteractor import java.util.concurrent.atomic.AtomicBoolean import javax.inject.Inject import kotlinx.coroutines.CoroutineScope @@ -64,6 +65,7 @@ constructor( private val footerActionsViewModelFactory: FooterActionsViewModel.Factory, private val footerActionsController: FooterActionsController, private val sceneInteractor: SceneInteractor, + private val unfoldTransitionInteractor: UnfoldTransitionInteractor, ) { val destinationScenes: StateFlow<Map<UserAction, UserActionResult>> = combine( @@ -106,6 +108,14 @@ constructor( val shadeMode: StateFlow<ShadeMode> = shadeInteractor.shadeMode + /** + * Amount of X-axis translation to apply to various elements as the unfolded foldable is folded + * slightly, in pixels. + */ + fun unfoldTranslationX(isOnStartSide: Boolean): Flow<Float> { + return unfoldTransitionInteractor.unfoldTranslationX(isOnStartSide) + } + /** Notifies that some content in the shade was clicked. */ fun onContentClicked() { if (!isClickable.value) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt index 519d719d1929..e3db62634339 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt @@ -959,7 +959,7 @@ class DragDownHelper( anim.start() } - private fun stopDragging() { + fun stopDragging() { if (startingChild != null) { cancelChildExpansion(startingChild!!) startingChild = null diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java index 9e0b16cfb312..0c8518f0b284 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java @@ -1767,7 +1767,8 @@ public class ExpandableNotificationRow extends ActivatableNotificationView */ public ExpandableNotificationRow(Context context, AttributeSet attrs) { this(context, attrs, context); - Log.e(TAG, "This constructor shouldn't be called"); + // NOTE(b/317503801): Always crash when using the insecure constructor. + throw new UnsupportedOperationException("Insecure constructor"); } /** @@ -2804,12 +2805,7 @@ public class ExpandableNotificationRow extends ActivatableNotificationView } public boolean isExpanded(boolean allowOnKeyguard) { - if (DEBUG) { - if (!mShowingPublicInitialized && !allowOnKeyguard) { - Log.d(TAG, "mShowingPublic is not initialized."); - } - } - return !mShowingPublic && (!mOnKeyguard || allowOnKeyguard) + return (!shouldShowPublic()) && (!mOnKeyguard || allowOnKeyguard) && (!hasUserChangedExpansion() && (isSystemExpanded() || isSystemChildExpanded()) || isUserExpanded()); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowInflaterTask.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowInflaterTask.java index 5fbcebda7cd6..35afda7084d9 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowInflaterTask.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowInflaterTask.java @@ -33,6 +33,8 @@ import com.android.systemui.statusbar.InflationTask; import com.android.systemui.statusbar.notification.collection.NotificationEntry; import com.android.systemui.util.time.SystemClock; +import java.util.concurrent.Executor; + import javax.inject.Inject; /** @@ -58,10 +60,22 @@ public class RowInflaterTask implements InflationTask, AsyncLayoutInflater.OnInf } /** - * Inflates a new notificationView. This should not be called twice on this object + * Inflates a new notificationView asynchronously, calling the {@code listener} on the main + * thread when done. This should not be called twice on this object. */ public void inflate(Context context, ViewGroup parent, NotificationEntry entry, RowInflationFinishedListener listener) { + inflate(context, parent, entry, null, listener); + } + + /** + * Inflates a new notificationView asynchronously, calling the {@code listener} on the supplied + * {@code listenerExecutor} (or the main thread if null) when done. This should not be called + * twice on this object. + */ + @VisibleForTesting + public void inflate(Context context, ViewGroup parent, NotificationEntry entry, + @Nullable Executor listenerExecutor, RowInflationFinishedListener listener) { if (TRACE_ORIGIN) { mInflateOrigin = new Throwable("inflate requested here"); } @@ -72,7 +86,7 @@ public class RowInflaterTask implements InflationTask, AsyncLayoutInflater.OnInf mLogger.logInflateStart(entry); mInflateStartTimeMs = mSystemClock.elapsedRealtime(); - inflater.inflate(R.layout.status_bar_notification_row, parent, this); + inflater.inflate(R.layout.status_bar_notification_row, parent, listenerExecutor, this); } private RowAsyncLayoutInflater makeRowInflater(NotificationEntry entry) { @@ -80,12 +94,12 @@ public class RowInflaterTask implements InflationTask, AsyncLayoutInflater.OnInf } @VisibleForTesting - static class RowAsyncLayoutInflater implements AsyncLayoutFactory { + public static class RowAsyncLayoutInflater implements AsyncLayoutFactory { private final NotificationEntry mEntry; private final SystemClock mSystemClock; private final RowInflaterTaskLogger mLogger; - RowAsyncLayoutInflater(NotificationEntry entry, SystemClock systemClock, + public RowAsyncLayoutInflater(NotificationEntry entry, SystemClock systemClock, RowInflaterTaskLogger logger) { mEntry = entry; mSystemClock = systemClock; 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 50996821a156..37bbbd0466d4 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 @@ -66,6 +66,7 @@ import com.android.systemui.scene.shared.flag.SceneContainerFlag import com.android.systemui.shade.domain.interactor.ShadeInteractor import com.android.systemui.statusbar.notification.stack.domain.interactor.NotificationStackAppearanceInteractor import com.android.systemui.statusbar.notification.stack.domain.interactor.SharedNotificationContainerInteractor +import com.android.systemui.unfold.domain.interactor.UnfoldTransitionInteractor import com.android.systemui.util.kotlin.BooleanFlowOperators.and import com.android.systemui.util.kotlin.BooleanFlowOperators.or import com.android.systemui.util.kotlin.FlowDumperImpl @@ -80,6 +81,7 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combineTransform import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flow @@ -128,6 +130,7 @@ constructor( private val primaryBouncerToLockscreenTransitionViewModel: PrimaryBouncerToLockscreenTransitionViewModel, private val aodBurnInViewModel: AodBurnInViewModel, + unfoldTransitionInteractor: UnfoldTransitionInteractor, ) : FlowDumperImpl(dumpManager) { private val statesForConstrainedNotifications: Set<KeyguardState> = setOf(AOD, LOCKSCREEN, DOZING, ALTERNATE_BOUNCER, PRIMARY_BOUNCER) @@ -577,14 +580,20 @@ constructor( .dumpWhileCollecting("translationY") } - /** - * The container may need to be translated in the x direction as the keyguard fades out, such as - * when swiping open the glanceable hub from the lockscreen. - */ + /** Horizontal translation to apply to the container. */ val translationX: Flow<Float> = merge( + // The container may need to be translated along the X axis as the keyguard fades + // out, such as when swiping open the glanceable hub from the lockscreen. lockscreenToGlanceableHubTransitionViewModel.notificationTranslationX, glanceableHubToLockscreenTransitionViewModel.notificationTranslationX, + if (SceneContainerFlag.isEnabled) { + // The container may need to be translated along the X axis as the unfolded + // foldable is folded slightly. + unfoldTransitionInteractor.unfoldTranslationX(isOnStartSide = false) + } else { + emptyFlow() + } ) .dumpWhileCollecting("translationX") diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt index ebaeb39efe31..68d54e73774e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LegacyActivityStarterInternalImpl.kt @@ -131,7 +131,7 @@ constructor( val runnable = Runnable { assistManagerLazy.get().hideAssist() - intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK + intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP intent.addFlags(flags) val result = intArrayOf(ActivityManager.START_CANCELED) activityTransitionAnimator.startIntentWithAnimation( diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt index a0c5618041f6..5f08afdee74a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt @@ -128,6 +128,8 @@ object MobileIconBinder { mobileGroupView, dotView, ) + + view.requestLayout() } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractor.kt index 08ed0305bd13..054116dbdfc2 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractor.kt @@ -22,6 +22,8 @@ import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractor import com.android.systemui.statusbar.pipeline.satellite.data.DeviceBasedSatelliteRepository import com.android.systemui.statusbar.pipeline.satellite.shared.model.SatelliteConnectionState +import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractor +import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel import com.android.systemui.statusbar.policy.domain.interactor.DeviceProvisioningInteractor import javax.inject.Inject import kotlinx.coroutines.CoroutineScope @@ -41,6 +43,7 @@ constructor( val repo: DeviceBasedSatelliteRepository, iconsInteractor: MobileIconsInteractor, deviceProvisioningInteractor: DeviceProvisioningInteractor, + wifiInteractor: WifiInteractor, @Application scope: CoroutineScope, ) { /** Must be observed by any UI showing Satellite iconography */ @@ -73,6 +76,9 @@ constructor( val isDeviceProvisioned: Flow<Boolean> = deviceProvisioningInteractor.isDeviceProvisioned + val isWifiActive: Flow<Boolean> = + wifiInteractor.wifiNetwork.map { it is WifiNetworkModel.Active } + /** When all connections are considered OOS, satellite connectivity is potentially valid */ val areAllConnectionsOutOfService = if (Flags.oemEnabledSatelliteFlag()) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModel.kt index 40641bed0602..a0291b81c9a1 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModel.kt @@ -59,9 +59,10 @@ constructor( combine( interactor.isSatelliteAllowed, interactor.isDeviceProvisioned, + interactor.isWifiActive, airplaneModeRepository.isAirplaneMode - ) { isSatelliteAllowed, isDeviceProvisioned, isAirplaneMode -> - isSatelliteAllowed && isDeviceProvisioned && !isAirplaneMode + ) { isSatelliteAllowed, isDeviceProvisioned, isWifiActive, isAirplaneMode -> + isSatelliteAllowed && isDeviceProvisioned && !isWifiActive && !isAirplaneMode } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt index 1b56702f3907..0c2abd9099f7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AvalancheController.kt @@ -153,18 +153,20 @@ class AvalancheController @Inject constructor( // Use default duration, like we did before AvalancheController existed return autoDismissMs } + val showingList: MutableList<HeadsUpEntry> = mutableListOf() headsUpEntryShowing?.let { showingList.add(it) } + nextList.sort() val entryList = showingList + nextList - if (entryList.indexOf(entry) == entryList.size - 1) { - // Use default duration if last entry + val thisEntryIndex = entryList.indexOf(entry) + val nextEntryIndex = thisEntryIndex + 1 + + // If last entry, use default duration + if (nextEntryIndex >= entryList.size) { return autoDismissMs } - - nextList.sort() - val nextEntry = nextList[0] - + val nextEntry = entryList[nextEntryIndex] if (nextEntry.compareNonTimeFields(entry) == -1) { // Next entry is higher priority return 500 diff --git a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt index 35228508f727..37ef1f2c1d31 100644 --- a/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt +++ b/packages/SystemUI/src/com/android/systemui/unfold/UnfoldTransitionModule.kt @@ -32,8 +32,6 @@ import com.android.systemui.unfold.data.repository.FoldStateRepository import com.android.systemui.unfold.data.repository.FoldStateRepositoryImpl import com.android.systemui.unfold.data.repository.UnfoldTransitionRepository import com.android.systemui.unfold.data.repository.UnfoldTransitionRepositoryImpl -import com.android.systemui.unfold.domain.interactor.UnfoldTransitionInteractor -import com.android.systemui.unfold.domain.interactor.UnfoldTransitionInteractorImpl import com.android.systemui.unfold.system.SystemUnfoldSharedModule import com.android.systemui.unfold.updates.FoldProvider import com.android.systemui.unfold.updates.FoldStateProvider @@ -186,8 +184,6 @@ class UnfoldTransitionModule { interface Bindings { @Binds fun bindRepository(impl: UnfoldTransitionRepositoryImpl): UnfoldTransitionRepository - @Binds fun bindInteractor(impl: UnfoldTransitionInteractorImpl): UnfoldTransitionInteractor - @Binds fun bindFoldStateRepository(impl: FoldStateRepositoryImpl): FoldStateRepository } diff --git a/packages/SystemUI/src/com/android/systemui/unfold/data/repository/UnfoldTransitionRepository.kt b/packages/SystemUI/src/com/android/systemui/unfold/data/repository/UnfoldTransitionRepository.kt index 0d3682c9a24b..fbbd2b9c5de8 100644 --- a/packages/SystemUI/src/com/android/systemui/unfold/data/repository/UnfoldTransitionRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/unfold/data/repository/UnfoldTransitionRepository.kt @@ -15,9 +15,11 @@ */ package com.android.systemui.unfold.data.repository +import androidx.annotation.FloatRange import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow import com.android.systemui.unfold.UnfoldTransitionProgressProvider import com.android.systemui.unfold.data.repository.UnfoldTransitionStatus.TransitionFinished +import com.android.systemui.unfold.data.repository.UnfoldTransitionStatus.TransitionInProgress import com.android.systemui.unfold.data.repository.UnfoldTransitionStatus.TransitionStarted import com.android.systemui.util.kotlin.getOrNull import java.util.Optional @@ -42,6 +44,10 @@ interface UnfoldTransitionRepository { sealed class UnfoldTransitionStatus { /** Status that is sent when fold or unfold transition is in started state */ data object TransitionStarted : UnfoldTransitionStatus() + /** Status that is sent while fold or unfold transition is in progress */ + data class TransitionInProgress( + @FloatRange(from = 0.0, to = 1.0) val progress: Float, + ) : UnfoldTransitionStatus() /** Status that is sent when fold or unfold transition is finished */ data object TransitionFinished : UnfoldTransitionStatus() } @@ -66,6 +72,10 @@ constructor( trySend(TransitionStarted) } + override fun onTransitionProgress(progress: Float) { + trySend(TransitionInProgress(progress)) + } + override fun onTransitionFinished() { trySend(TransitionFinished) } diff --git a/packages/SystemUI/src/com/android/systemui/unfold/domain/interactor/UnfoldTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/unfold/domain/interactor/UnfoldTransitionInteractor.kt index 3e2e564c307c..03499cbdccb2 100644 --- a/packages/SystemUI/src/com/android/systemui/unfold/domain/interactor/UnfoldTransitionInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/unfold/domain/interactor/UnfoldTransitionInteractor.kt @@ -15,40 +15,79 @@ */ package com.android.systemui.unfold.domain.interactor +import android.view.View +import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.res.R import com.android.systemui.unfold.data.repository.UnfoldTransitionRepository import com.android.systemui.unfold.data.repository.UnfoldTransitionStatus.TransitionFinished +import com.android.systemui.unfold.data.repository.UnfoldTransitionStatus.TransitionInProgress import com.android.systemui.unfold.data.repository.UnfoldTransitionStatus.TransitionStarted import javax.inject.Inject +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onStart /** * Contains business-logic related to fold-unfold transitions while interacting with * [UnfoldTransitionRepository] */ -interface UnfoldTransitionInteractor { +@SysUISingleton +class UnfoldTransitionInteractor +@Inject +constructor( + private val repository: UnfoldTransitionRepository, + private val configurationInteractor: ConfigurationInteractor, +) { /** Returns availability of fold/unfold transitions on the device */ val isAvailable: Boolean + get() = repository.isAvailable - /** Suspends and waits for a fold/unfold transition to finish */ - suspend fun waitForTransitionFinish() - - /** Suspends and waits for a fold/unfold transition to start */ - suspend fun waitForTransitionStart() -} - -class UnfoldTransitionInteractorImpl -@Inject -constructor(private val repository: UnfoldTransitionRepository) : UnfoldTransitionInteractor { + /** + * This mapping emits 1 when the device is completely unfolded and 0.0 when the device is + * completely folded. + */ + private val unfoldProgress: Flow<Float> = + repository.transitionStatus + .map { (it as? TransitionInProgress)?.progress ?: 1f } + .onStart { emit(1f) } + .distinctUntilChanged() - override val isAvailable: Boolean - get() = repository.isAvailable + /** + * Amount of X-axis translation to apply to various elements as the unfolded foldable is folded + * slightly, in pixels. + * + * @param isOnStartSide Whether the consumer wishes to get a translation amount that's suitable + * for an element that's on the start-side (left hand-side in left-to-right layouts); if + * `true`, the values will provide positive translations to push the left-hand-side element + * towards the foldable hinge; if `false`, the values will be inverted to provide negative + * translations to push the right-hand-side element towards the foldable hinge. Note that this + * method already accounts for left-to-right vs. right-to-left layout directions. + */ + fun unfoldTranslationX(isOnStartSide: Boolean): Flow<Float> { + return combine( + unfoldProgress, + configurationInteractor.dimensionPixelSize(R.dimen.notification_side_paddings), + configurationInteractor.layoutDirection.map { + if (it == View.LAYOUT_DIRECTION_RTL) -1 else 1 + }, + ) { unfoldedAmount, max, layoutDirectionMultiplier -> + val sideMultiplier = if (isOnStartSide) 1 else -1 + max * (1 - unfoldedAmount) * sideMultiplier * layoutDirectionMultiplier + } + } - override suspend fun waitForTransitionFinish() { + /** Suspends and waits for a fold/unfold transition to finish */ + suspend fun waitForTransitionFinish() { repository.transitionStatus.filter { it is TransitionFinished }.first() } - override suspend fun waitForTransitionStart() { + /** Suspends and waits for a fold/unfold transition to start */ + suspend fun waitForTransitionStart() { repository.transitionStatus.filter { it is TransitionStarted }.first() } } diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/button/ui/viewmodel/ButtonViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/button/ui/viewmodel/ButtonViewModel.kt index 754d258b2b86..4d11f44e045a 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/button/ui/viewmodel/ButtonViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/button/ui/viewmodel/ButtonViewModel.kt @@ -1,17 +1,17 @@ /* * Copyright (C) 2024 The Android Open Source Project * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 + * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package com.android.systemui.volume.panel.component.button.ui.viewmodel @@ -22,4 +22,5 @@ import com.android.systemui.common.shared.model.Icon data class ButtonViewModel( val icon: Icon, val label: CharSequence, + val isActive: Boolean = true, ) diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/button/ui/viewmodel/ToggleButtonViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/button/ui/viewmodel/ToggleButtonViewModel.kt deleted file mode 100644 index 6c47aec563df..000000000000 --- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/button/ui/viewmodel/ToggleButtonViewModel.kt +++ /dev/null @@ -1,28 +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.volume.panel.component.button.ui.viewmodel - -import com.android.systemui.common.shared.model.Icon - -data class ToggleButtonViewModel( - val isChecked: Boolean, - val icon: Icon, - val label: CharSequence, -) - -fun ToggleButtonViewModel.toButtonViewModel(): ButtonViewModel = - ButtonViewModel(icon = icon, label = label) diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/captioning/ui/viewmodel/CaptioningViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/captioning/ui/viewmodel/CaptioningViewModel.kt index 01421f86311f..ca5aef8eee2e 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/captioning/ui/viewmodel/CaptioningViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/captioning/ui/viewmodel/CaptioningViewModel.kt @@ -21,7 +21,7 @@ import com.android.internal.logging.UiEventLogger import com.android.settingslib.view.accessibility.domain.interactor.CaptioningInteractor import com.android.systemui.common.shared.model.Icon import com.android.systemui.res.R -import com.android.systemui.volume.panel.component.button.ui.viewmodel.ToggleButtonViewModel +import com.android.systemui.volume.panel.component.button.ui.viewmodel.ButtonViewModel import com.android.systemui.volume.panel.dagger.scope.VolumePanelScope import com.android.systemui.volume.panel.ui.VolumePanelUiEvent import javax.inject.Inject @@ -43,11 +43,11 @@ constructor( private val uiEventLogger: UiEventLogger, ) { - val buttonViewModel: StateFlow<ToggleButtonViewModel?> = + val buttonViewModel: StateFlow<ButtonViewModel?> = captioningInteractor.isSystemAudioCaptioningEnabled .map { isEnabled -> - ToggleButtonViewModel( - isChecked = isEnabled, + ButtonViewModel( + isActive = isEnabled, icon = Icon.Resource( if (isEnabled) R.drawable.ic_volume_odi_captions diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/ui/viewmodel/SpatialAudioButtonViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/ui/viewmodel/SpatialAudioButtonViewModel.kt index e5c5a655c73e..f7a602e993e8 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/ui/viewmodel/SpatialAudioButtonViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/ui/viewmodel/SpatialAudioButtonViewModel.kt @@ -16,10 +16,10 @@ package com.android.systemui.volume.panel.component.spatial.ui.viewmodel -import com.android.systemui.volume.panel.component.button.ui.viewmodel.ToggleButtonViewModel +import com.android.systemui.volume.panel.component.button.ui.viewmodel.ButtonViewModel import com.android.systemui.volume.panel.component.spatial.domain.model.SpatialAudioEnabledModel data class SpatialAudioButtonViewModel( val model: SpatialAudioEnabledModel, - val button: ToggleButtonViewModel, + val button: ButtonViewModel, ) diff --git a/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/ui/viewmodel/SpatialAudioViewModel.kt b/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/ui/viewmodel/SpatialAudioViewModel.kt index b5e9ed27d664..4b2d26a6b28b 100644 --- a/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/ui/viewmodel/SpatialAudioViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/volume/panel/component/spatial/ui/viewmodel/SpatialAudioViewModel.kt @@ -22,8 +22,6 @@ import com.android.systemui.common.shared.model.Icon import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.res.R import com.android.systemui.volume.panel.component.button.ui.viewmodel.ButtonViewModel -import com.android.systemui.volume.panel.component.button.ui.viewmodel.ToggleButtonViewModel -import com.android.systemui.volume.panel.component.button.ui.viewmodel.toButtonViewModel import com.android.systemui.volume.panel.component.spatial.domain.SpatialAudioAvailabilityCriteria import com.android.systemui.volume.panel.component.spatial.domain.interactor.SpatialAudioComponentInteractor import com.android.systemui.volume.panel.component.spatial.domain.model.SpatialAudioAvailabilityModel @@ -53,8 +51,8 @@ constructor( val spatialAudioButton: StateFlow<ButtonViewModel?> = interactor.isEnabled .map { - it.toViewModel(true) - .toButtonViewModel() + val isChecked = it is SpatialAudioEnabledModel.SpatialAudioEnabled + it.toViewModel(isChecked) .copy(label = context.getString(R.string.volume_panel_spatial_audio_title)) } .stateIn(scope, SharingStarted.Eagerly, null) @@ -76,8 +74,7 @@ constructor( } .map { isEnabled -> val isChecked = isEnabled == currentIsEnabled - val buttonViewModel: ToggleButtonViewModel = - isEnabled.toViewModel(isChecked) + val buttonViewModel: ButtonViewModel = isEnabled.toViewModel(isChecked) SpatialAudioButtonViewModel(button = buttonViewModel, model = isEnabled) } } @@ -100,26 +97,26 @@ constructor( scope.launch { interactor.setEnabled(model) } } - private fun SpatialAudioEnabledModel.toViewModel(isChecked: Boolean): ToggleButtonViewModel { + private fun SpatialAudioEnabledModel.toViewModel(isChecked: Boolean): ButtonViewModel { if (this is SpatialAudioEnabledModel.HeadTrackingEnabled) { - return ToggleButtonViewModel( - isChecked = isChecked, + return ButtonViewModel( + isActive = isChecked, icon = Icon.Resource(R.drawable.ic_head_tracking, contentDescription = null), label = context.getString(R.string.volume_panel_spatial_audio_tracking) ) } if (this is SpatialAudioEnabledModel.SpatialAudioEnabled) { - return ToggleButtonViewModel( - isChecked = isChecked, + return ButtonViewModel( + isActive = isChecked, icon = Icon.Resource(R.drawable.ic_spatial_audio, contentDescription = null), label = context.getString(R.string.volume_panel_spatial_audio_fixed) ) } if (this is SpatialAudioEnabledModel.Disabled) { - return ToggleButtonViewModel( - isChecked = isChecked, + return ButtonViewModel( + isActive = isChecked, icon = Icon.Resource(R.drawable.ic_spatial_audio_off, contentDescription = null), label = context.getString(R.string.volume_panel_spatial_audio_off) ) diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java index a2499615e18c..319b61512f12 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerBaseTest.java @@ -205,9 +205,9 @@ public class KeyguardClockSwitchControllerBaseTest extends SysuiTestCase { when(mClockRegistry.createCurrentClock()).thenReturn(mClockController); when(mClockEventController.getClock()).thenReturn(mClockController); when(mSmallClockController.getConfig()) - .thenReturn(new ClockFaceConfig(ClockTickRate.PER_MINUTE, false, false)); + .thenReturn(new ClockFaceConfig(ClockTickRate.PER_MINUTE, false, false, false)); when(mLargeClockController.getConfig()) - .thenReturn(new ClockFaceConfig(ClockTickRate.PER_MINUTE, false, false)); + .thenReturn(new ClockFaceConfig(ClockTickRate.PER_MINUTE, false, false, false)); mSliceView = new View(getContext()); when(mView.findViewById(R.id.keyguard_slice_view)).thenReturn(mSliceView); diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java index 9d81b960336f..99b5a4b631a7 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java @@ -272,9 +272,9 @@ public class KeyguardClockSwitchControllerTest extends KeyguardClockSwitchContro assertEquals(View.VISIBLE, mFakeDateView.getVisibility()); when(mSmallClockController.getConfig()) - .thenReturn(new ClockFaceConfig(ClockTickRate.PER_MINUTE, true, false)); + .thenReturn(new ClockFaceConfig(ClockTickRate.PER_MINUTE, true, false, true)); when(mLargeClockController.getConfig()) - .thenReturn(new ClockFaceConfig(ClockTickRate.PER_MINUTE, true, false)); + .thenReturn(new ClockFaceConfig(ClockTickRate.PER_MINUTE, true, false, true)); verify(mClockRegistry).registerClockChangeListener(listenerArgumentCaptor.capture()); listenerArgumentCaptor.getValue().onCurrentClockChanged(); diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt index 9b5364ed6e6e..7151c429acf9 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPinViewControllerTest.kt @@ -115,6 +115,7 @@ class KeyguardSimPinViewControllerTest : SysuiTestCase() { @Test fun onViewAttached() { underTest.onViewAttached() + verify(keyguardMessageAreaController).setIsVisible(true) verify(keyguardMessageAreaController) .setMessage(context.resources.getString(R.string.keyguard_enter_your_pin), false) verify(keyguardUpdateMonitor) diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt index e71490c73aa1..acae913459b3 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSimPukViewControllerTest.kt @@ -103,7 +103,9 @@ class KeyguardSimPukViewControllerTest : SysuiTestCase() { @Test fun onViewAttached() { + Mockito.reset(keyguardMessageAreaController) underTest.onViewAttached() + Mockito.verify(keyguardMessageAreaController).setIsVisible(true) Mockito.verify(keyguardUpdateMonitor) .registerCallback(any(KeyguardUpdateMonitorCallback::class.java)) Mockito.verify(keyguardMessageAreaController) diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java index 11fe86277903..b2828a41c4b0 100644 --- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java @@ -98,7 +98,7 @@ public class KeyguardStatusViewControllerTest extends KeyguardStatusViewControll public void updatePosition_primaryClockAnimation() { ClockController mockClock = mock(ClockController.class); when(mKeyguardClockSwitchController.getClock()).thenReturn(mockClock); - when(mockClock.getConfig()).thenReturn(new ClockConfig("MOCK", "", "", false, true)); + when(mockClock.getConfig()).thenReturn(new ClockConfig("MOCK", "", "", false, true, false)); mController.updatePosition(10, 15, 20f, true); @@ -113,7 +113,7 @@ public class KeyguardStatusViewControllerTest extends KeyguardStatusViewControll public void updatePosition_alternateClockAnimation() { ClockController mockClock = mock(ClockController.class); when(mKeyguardClockSwitchController.getClock()).thenReturn(mockClock); - when(mockClock.getConfig()).thenReturn(new ClockConfig("MOCK", "", "", true, true)); + when(mockClock.getConfig()).thenReturn(new ClockConfig("MOCK", "", "", true, true, false)); mController.updatePosition(10, 15, 20f, true); diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractorTest.kt index 3f05bfae6777..9ccf2121b8d2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardSurfaceBehindInteractorTest.kt @@ -19,6 +19,7 @@ package com.android.systemui.keyguard.domain.interactor import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase +import com.android.systemui.coroutines.collectLastValue import com.android.systemui.coroutines.collectValues import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository import com.android.systemui.keyguard.shared.model.KeyguardState @@ -227,4 +228,50 @@ class KeyguardSurfaceBehindInteractorTest : SysuiTestCase() { { it == KeyguardSurfaceBehindModel(alpha = 0f) }, ) } + + @Test + fun notificationLaunchFromLockscreen_isAnimatingSurfaceTrue() = + testScope.runTest { + val isAnimatingSurface by collectLastValue(underTest.isAnimatingSurface) + transitionRepository.sendTransitionStep( + TransitionStep( + from = KeyguardState.GONE, + to = KeyguardState.LOCKSCREEN, + transitionState = TransitionState.STARTED, + ) + ) + transitionRepository.sendTransitionStep( + TransitionStep( + from = KeyguardState.GONE, + to = KeyguardState.LOCKSCREEN, + transitionState = TransitionState.FINISHED, + ) + ) + kosmos.notificationLaunchAnimationInteractor.setIsLaunchAnimationRunning(true) + runCurrent() + assertThat(isAnimatingSurface).isTrue() + } + + @Test + fun notificationLaunchFromGone_isAnimatingSurfaceFalse() = + testScope.runTest { + val isAnimatingSurface by collectLastValue(underTest.isAnimatingSurface) + transitionRepository.sendTransitionStep( + TransitionStep( + from = KeyguardState.LOCKSCREEN, + to = KeyguardState.GONE, + transitionState = TransitionState.STARTED, + ) + ) + transitionRepository.sendTransitionStep( + TransitionStep( + from = KeyguardState.LOCKSCREEN, + to = KeyguardState.GONE, + transitionState = TransitionState.FINISHED, + ) + ) + kosmos.notificationLaunchAnimationInteractor.setIsLaunchAnimationRunning(true) + runCurrent() + assertThat(isAnimatingSurface).isFalse() + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinderTest.kt index 66aa572dbc48..5e3a142dc348 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinderTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/binder/KeyguardClockViewBinderTest.kt @@ -113,7 +113,8 @@ class KeyguardClockViewBinderTest : SysuiTestCase() { id = "WEATHER_CLOCK", name = "", description = "", - useAlternateSmartspaceAODTransition = true + useAlternateSmartspaceAODTransition = true, + useCustomClockScene = true ) whenever(clock.config).thenReturn(clockConfig) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelTest.kt index d410dac1b2e4..f1c93c4652c3 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelTest.kt @@ -32,7 +32,6 @@ import com.android.systemui.util.mockito.any import com.google.common.collect.Range import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Test import org.junit.runner.RunWith @@ -67,7 +66,7 @@ class AlternateBouncerViewModelTest : SysuiTestCase() { fun transitionToAlternateBouncer_scrimAlphaUpdate() = testScope.runTest { val scrimAlphas by collectValues(underTest.scrimAlpha) - runCurrent() + assertThat(scrimAlphas.size).isEqualTo(1) // initial value is 0f transitionRepository.sendTransitionSteps( listOf( @@ -79,7 +78,7 @@ class AlternateBouncerViewModelTest : SysuiTestCase() { testScope, ) - assertThat(scrimAlphas.size).isEqualTo(4) + assertThat(scrimAlphas.size).isEqualTo(5) scrimAlphas.forEach { assertThat(it).isIn(Range.closed(0f, 1f)) } } @@ -87,7 +86,7 @@ class AlternateBouncerViewModelTest : SysuiTestCase() { fun transitionFromAlternateBouncer_scrimAlphaUpdate() = testScope.runTest { val scrimAlphas by collectValues(underTest.scrimAlpha) - runCurrent() + assertThat(scrimAlphas.size).isEqualTo(1) // initial value is 0f transitionRepository.sendTransitionSteps( listOf( @@ -98,7 +97,7 @@ class AlternateBouncerViewModelTest : SysuiTestCase() { ), testScope, ) - assertThat(scrimAlphas.size).isEqualTo(4) + assertThat(scrimAlphas.size).isEqualTo(5) scrimAlphas.forEach { assertThat(it).isIn(Range.closed(0f, 1f)) } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImplTest.kt index 4d20f55d7df2..8f73811199ba 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/domain/pipeline/MediaDataFilterImplTest.kt @@ -50,6 +50,7 @@ import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith +import org.mockito.ArgumentMatchers.anyBoolean import org.mockito.ArgumentMatchers.anyInt import org.mockito.ArgumentMatchers.anyLong import org.mockito.Mock @@ -76,9 +77,10 @@ private val SMARTSPACE_INSTANCE_ID = InstanceId.fakeInstanceId(456)!! @TestableLooper.RunWithLooper class MediaDataFilterImplTest : SysuiTestCase() { + @Mock private lateinit var listener: MediaDataProcessor.Listener @Mock private lateinit var userTracker: UserTracker @Mock private lateinit var broadcastSender: BroadcastSender - @Mock private lateinit var mediaDataManager: MediaDataManager + @Mock private lateinit var mediaDataProcessor: MediaDataProcessor @Mock private lateinit var lockscreenUserManager: NotificationLockscreenUserManager @Mock private lateinit var executor: Executor @Mock private lateinit var smartspaceData: SmartspaceMediaData @@ -114,7 +116,8 @@ class MediaDataFilterImplTest : SysuiTestCase() { mediaFlags, repository, ) - mediaDataFilter.mediaDataManager = mediaDataManager + mediaDataFilter.mediaDataProcessor = mediaDataProcessor + mediaDataFilter.addListener(listener) // Start all tests as main user setUser(USER_MAIN) @@ -167,6 +170,8 @@ class MediaDataFilterImplTest : SysuiTestCase() { mediaDataFilter.onMediaDataLoaded(KEY, null, dataMain) + verify(listener) + .onMediaDataLoaded(eq(KEY), eq(null), eq(dataMain), eq(true), eq(0), eq(false)) assertThat(mediaDataLoadedStates).isEqualTo(mediaDataLoadingModel) } @@ -178,6 +183,8 @@ class MediaDataFilterImplTest : SysuiTestCase() { mediaDataFilter.onMediaDataLoaded(KEY, null, dataGuest) + verify(listener, never()) + .onMediaDataLoaded(any(), any(), any(), anyBoolean(), anyInt(), anyBoolean()) assertThat(mediaDataLoadedStates).isNotEqualTo(mediaLoadedStatesModel) } @@ -196,6 +203,7 @@ class MediaDataFilterImplTest : SysuiTestCase() { mediaLoadedStatesModel.remove(MediaDataLoadingModel.Loaded(dataMain.instanceId)) mediaDataFilter.onMediaDataRemoved(KEY) + verify(listener).onMediaDataRemoved(eq(KEY)) assertThat(mediaDataLoadedStates).isEqualTo(mediaLoadedStatesModel) } @@ -208,6 +216,7 @@ class MediaDataFilterImplTest : SysuiTestCase() { mediaDataFilter.onMediaDataLoaded(KEY, null, dataGuest) mediaDataFilter.onMediaDataRemoved(KEY) + verify(listener, never()).onMediaDataRemoved(eq(KEY)) assertThat(mediaDataLoadedStates).isEmpty() } @@ -226,6 +235,7 @@ class MediaDataFilterImplTest : SysuiTestCase() { setUser(USER_GUEST) // THEN we should remove the main user's media + verify(listener).onMediaDataRemoved(eq(KEY)) assertThat(mediaDataLoadedStates).isEmpty() } @@ -243,6 +253,20 @@ class MediaDataFilterImplTest : SysuiTestCase() { // and we switch to guest user setUser(USER_GUEST) + // THEN we should add back the guest user media + verify(listener) + .onMediaDataLoaded(eq(KEY_ALT), eq(null), eq(dataGuest), eq(true), eq(0), eq(false)) + + // but not the main user's + verify(listener, never()) + .onMediaDataLoaded( + eq(KEY), + any(), + eq(dataMain), + anyBoolean(), + anyInt(), + anyBoolean() + ) assertThat(mediaDataLoadedStates).isEqualTo(guestLoadedStatesModel) assertThat(mediaDataLoadedStates).isNotEqualTo(mainLoadedStatesModel) } @@ -261,6 +285,7 @@ class MediaDataFilterImplTest : SysuiTestCase() { val mediaLoadedStatesModel = listOf(MediaDataLoadingModel.Loaded(dataMain.instanceId)) // THEN we should remove the private profile media + verify(listener).onMediaDataRemoved(eq(KEY_ALT)) assertThat(mediaDataLoadedStates).isEqualTo(mediaLoadedStatesModel) } @@ -481,7 +506,7 @@ class MediaDataFilterImplTest : SysuiTestCase() { mediaDataFilter.onMediaDataLoaded(KEY, null, dataMain) mediaDataFilter.onSwipeToDismiss() - verify(mediaDataManager).setInactive(eq(KEY), eq(true), eq(true)) + verify(mediaDataProcessor).setInactive(eq(KEY), eq(true), eq(true)) } @Test @@ -507,6 +532,8 @@ class MediaDataFilterImplTest : SysuiTestCase() { ) .isTrue() assertThat(hasActiveMedia(selectedUserEntries)).isFalse() + verify(listener) + .onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData), eq(true)) verify(logger).logRecommendationAdded(SMARTSPACE_PACKAGE, SMARTSPACE_INSTANCE_ID) verify(logger, never()).logRecommendationActivated(any(), any(), any()) } @@ -534,6 +561,9 @@ class MediaDataFilterImplTest : SysuiTestCase() { ) .isFalse() assertThat(hasActiveMedia(selectedUserEntries)).isFalse() + verify(listener, never()) + .onMediaDataLoaded(any(), any(), any(), anyBoolean(), anyInt(), anyBoolean()) + verify(listener, never()).onSmartspaceMediaDataLoaded(any(), any(), anyBoolean()) verify(logger, never()).logRecommendationAdded(any(), any()) verify(logger, never()).logRecommendationActivated(any(), any(), any()) } @@ -563,6 +593,8 @@ class MediaDataFilterImplTest : SysuiTestCase() { ) .isTrue() assertThat(hasActiveMedia(selectedUserEntries)).isFalse() + verify(listener) + .onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData), eq(true)) verify(logger).logRecommendationAdded(SMARTSPACE_PACKAGE, SMARTSPACE_INSTANCE_ID) verify(logger, never()).logRecommendationActivated(any(), any(), any()) } @@ -592,6 +624,7 @@ class MediaDataFilterImplTest : SysuiTestCase() { ) .isFalse() assertThat(hasActiveMedia(selectedUserEntries)).isFalse() + verify(listener, never()).onSmartspaceMediaDataLoaded(any(), any(), anyBoolean()) verify(logger, never()).logRecommendationAdded(any(), any()) verify(logger, never()).logRecommendationActivated(any(), any(), any()) } @@ -614,6 +647,8 @@ class MediaDataFilterImplTest : SysuiTestCase() { mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent) assertThat(mediaDataLoadedStates).isEqualTo(mediaLoadedStatesModel) + verify(listener) + .onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true), eq(0), eq(false)) // AND we get a smartspace signal mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData) @@ -629,6 +664,9 @@ class MediaDataFilterImplTest : SysuiTestCase() { ) .isFalse() assertThat(hasActiveMedia(selectedUserEntries)).isFalse() + verify(listener, never()) + .onMediaDataLoaded(eq(KEY), eq(KEY), any(), anyBoolean(), anyInt(), anyBoolean()) + verify(listener, never()).onSmartspaceMediaDataLoaded(any(), any(), anyBoolean()) verify(logger, never()).logRecommendationAdded(any(), any()) verify(logger, never()).logRecommendationActivated(any(), any(), any()) } @@ -649,12 +687,15 @@ class MediaDataFilterImplTest : SysuiTestCase() { val mediaLoadedStatesModel = listOf(MediaDataLoadingModel.Loaded(dataMain.instanceId)) mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent) assertThat(mediaDataLoadedStates).isEqualTo(mediaLoadedStatesModel) + verify(listener) + .onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true), eq(0), eq(false)) // AND we get a smartspace signal runCurrent() mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData) // THEN we should treat the media as active instead + val dataCurrentAndActive = dataCurrent.copy(active = true) assertThat(mediaDataLoadedStates).isEqualTo(mediaLoadedStatesModel) assertThat( hasActiveMediaOrRecommendation( @@ -664,8 +705,18 @@ class MediaDataFilterImplTest : SysuiTestCase() { ) ) .isTrue() + verify(listener) + .onMediaDataLoaded( + eq(KEY), + eq(KEY), + eq(dataCurrentAndActive), + eq(true), + eq(100), + eq(true) + ) // Smartspace update shouldn't be propagated for the empty rec list. assertThat(recommendationsLoadingState).isEqualTo(SmartspaceMediaLoadingModel.Unknown) + verify(listener, never()).onSmartspaceMediaDataLoaded(any(), any(), anyBoolean()) verify(logger, never()).logRecommendationAdded(any(), any()) verify(logger).logRecommendationActivated(eq(APP_UID), eq(PACKAGE), eq(INSTANCE_ID)) } @@ -687,12 +738,24 @@ class MediaDataFilterImplTest : SysuiTestCase() { mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent) assertThat(mediaDataLoadedStates).isEqualTo(mediaDataLoadingModel) + verify(listener) + .onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true), eq(0), eq(false)) // AND we get a smartspace signal runCurrent() mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData) // THEN we should treat the media as active instead + val dataCurrentAndActive = dataCurrent.copy(active = true) + verify(listener) + .onMediaDataLoaded( + eq(KEY), + eq(KEY), + eq(dataCurrentAndActive), + eq(true), + eq(100), + eq(true) + ) assertThat(mediaDataLoadedStates).isEqualTo(mediaDataLoadingModel) assertThat( hasActiveMediaOrRecommendation( @@ -704,6 +767,8 @@ class MediaDataFilterImplTest : SysuiTestCase() { .isTrue() // Smartspace update should also be propagated but not prioritized. assertThat(recommendationsLoadingState).isEqualTo(recommendationsLoadingModel) + verify(listener) + .onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData), eq(false)) verify(logger).logRecommendationAdded(SMARTSPACE_PACKAGE, SMARTSPACE_INSTANCE_ID) verify(logger).logRecommendationActivated(eq(APP_UID), eq(PACKAGE), eq(INSTANCE_ID)) } @@ -721,6 +786,7 @@ class MediaDataFilterImplTest : SysuiTestCase() { mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData) mediaDataFilter.onSmartspaceMediaDataRemoved(SMARTSPACE_KEY) + verify(listener).onSmartspaceMediaDataRemoved(SMARTSPACE_KEY) assertThat(recommendationsLoadingState).isEqualTo(recommendationsLoadingModel) assertThat( hasActiveMediaOrRecommendation( @@ -746,15 +812,29 @@ class MediaDataFilterImplTest : SysuiTestCase() { val mediaLoadedStatesModel = listOf(MediaDataLoadingModel.Loaded(dataMain.instanceId)) val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime()) mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent) + assertThat(mediaDataLoadedStates).isEqualTo(mediaLoadedStatesModel) + verify(listener) + .onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true), eq(0), eq(false)) runCurrent() mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData) + val dataCurrentAndActive = dataCurrent.copy(active = true) + verify(listener) + .onMediaDataLoaded( + eq(KEY), + eq(KEY), + eq(dataCurrentAndActive), + eq(true), + eq(100), + eq(true) + ) assertThat(mediaDataLoadedStates).isEqualTo(mediaLoadedStatesModel) mediaDataFilter.onSmartspaceMediaDataRemoved(SMARTSPACE_KEY) + verify(listener).onSmartspaceMediaDataRemoved(SMARTSPACE_KEY) assertThat(recommendationsLoadingState).isEqualTo(recommendationsLoadingModel) assertThat( hasActiveMediaOrRecommendation( @@ -781,6 +861,8 @@ class MediaDataFilterImplTest : SysuiTestCase() { mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData) + verify(listener) + .onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData), eq(false)) assertThat(recommendationsLoadingState).isEqualTo(recommendationsLoadingModel) assertThat( hasActiveMediaOrRecommendation( @@ -813,12 +895,18 @@ class MediaDataFilterImplTest : SysuiTestCase() { val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime()) mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent) + verify(listener) + .onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true), eq(0), eq(false)) assertThat(mediaDataLoadedStates).isEqualTo(mediaLoadedStatesModel) // And an inactive recommendation is loaded mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData) // Smartspace is loaded but the media stays inactive + verify(listener) + .onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData), eq(false)) + verify(listener, never()) + .onMediaDataLoaded(any(), any(), any(), anyBoolean(), anyInt(), anyBoolean()) assertThat(recommendationsLoadingState).isEqualTo(recommendationsLoadingModel) assertThat( hasActiveMediaOrRecommendation( @@ -846,8 +934,8 @@ class MediaDataFilterImplTest : SysuiTestCase() { mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, data) mediaDataFilter.onSwipeToDismiss() - verify(mediaDataManager).setRecommendationInactive(eq(SMARTSPACE_KEY)) - verify(mediaDataManager, never()) + verify(mediaDataProcessor).setRecommendationInactive(eq(SMARTSPACE_KEY)) + verify(mediaDataProcessor, never()) .dismissSmartspaceRecommendation(eq(SMARTSPACE_KEY), anyLong()) } @@ -866,6 +954,8 @@ class MediaDataFilterImplTest : SysuiTestCase() { val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime()) mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent) + verify(listener) + .onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true), eq(0), eq(false)) assertThat(mediaDataLoadedStates).isEqualTo(mediaLoadedStatesModel) // AND we get a smartspace signal with extra to trigger resume @@ -875,6 +965,16 @@ class MediaDataFilterImplTest : SysuiTestCase() { mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData) // THEN we should treat the media as active instead + val dataCurrentAndActive = dataCurrent.copy(active = true) + verify(listener) + .onMediaDataLoaded( + eq(KEY), + eq(KEY), + eq(dataCurrentAndActive), + eq(true), + eq(100), + eq(true) + ) assertThat(mediaDataLoadedStates).isEqualTo(mediaLoadedStatesModel) assertThat( hasActiveMediaOrRecommendation( @@ -886,6 +986,8 @@ class MediaDataFilterImplTest : SysuiTestCase() { .isTrue() // And update the smartspace data state, but not prioritized assertThat(recommendationsLoadingState).isEqualTo(recommendationsLoadingModel) + verify(listener) + .onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData), eq(false)) } @Test @@ -901,6 +1003,8 @@ class MediaDataFilterImplTest : SysuiTestCase() { val dataCurrent = dataMain.copy(active = false, lastActive = clock.elapsedRealtime()) mediaDataFilter.onMediaDataLoaded(KEY, null, dataCurrent) + verify(listener) + .onMediaDataLoaded(eq(KEY), eq(null), eq(dataCurrent), eq(true), eq(0), eq(false)) assertThat(mediaDataLoadedStates).isEqualTo(mediaLoadedStatesModel) // AND we get a smartspace signal with extra to not trigger resume @@ -908,7 +1012,12 @@ class MediaDataFilterImplTest : SysuiTestCase() { whenever(cardAction.extras).thenReturn(extras) mediaDataFilter.onSmartspaceMediaDataLoaded(SMARTSPACE_KEY, smartspaceData) + // THEN listeners are not updated to show media + verify(listener, never()) + .onMediaDataLoaded(eq(KEY), eq(KEY), any(), eq(true), eq(100), eq(true)) // But the smartspace update is still propagated + verify(listener) + .onSmartspaceMediaDataLoaded(eq(SMARTSPACE_KEY), eq(smartspaceData), eq(false)) assertThat(recommendationsLoadingState).isEqualTo(recommendationsLoadingModel) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaViewControllerTest.kt index a73bb2cdf79a..e5d3082bb245 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaViewControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/controller/MediaViewControllerTest.kt @@ -16,29 +16,54 @@ package com.android.systemui.media.controls.ui.controller +import android.animation.AnimatorSet +import android.content.Context import android.content.res.Configuration import android.content.res.Configuration.ORIENTATION_LANDSCAPE +import android.graphics.drawable.Drawable +import android.graphics.drawable.GradientDrawable +import android.graphics.drawable.RippleDrawable import android.testing.AndroidTestingRunner import android.testing.TestableLooper import android.view.View +import android.view.ViewGroup +import android.view.animation.Interpolator +import android.widget.FrameLayout +import android.widget.ImageButton +import android.widget.ImageView +import android.widget.SeekBar +import android.widget.TextView import androidx.constraintlayout.widget.ConstraintSet +import androidx.lifecycle.LiveData import androidx.test.filters.SmallTest +import com.android.internal.widget.CachingIconView import com.android.systemui.SysuiTestCase +import com.android.systemui.media.controls.ui.view.GutsViewHolder import com.android.systemui.media.controls.ui.view.MediaHost import com.android.systemui.media.controls.ui.view.MediaViewHolder import com.android.systemui.media.controls.ui.view.RecommendationViewHolder +import com.android.systemui.media.controls.ui.viewmodel.SeekBarViewModel import com.android.systemui.media.controls.util.MediaFlags import com.android.systemui.res.R +import com.android.systemui.surfaceeffects.loadingeffect.LoadingEffectView +import com.android.systemui.surfaceeffects.ripple.MultiRippleView +import com.android.systemui.surfaceeffects.turbulencenoise.TurbulenceNoiseView import com.android.systemui.util.animation.MeasurementInput import com.android.systemui.util.animation.TransitionLayout import com.android.systemui.util.animation.TransitionViewState import com.android.systemui.util.animation.WidgetState +import com.android.systemui.util.concurrency.FakeExecutor +import com.android.systemui.util.mockito.withArgCaptor +import com.android.systemui.util.settings.GlobalSettings +import com.android.systemui.util.time.FakeSystemClock +import com.google.common.truth.Truth.assertThat import junit.framework.Assert.assertTrue import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentMatchers.floatThat import org.mockito.Mock +import org.mockito.Mockito import org.mockito.Mockito.never import org.mockito.Mockito.times import org.mockito.Mockito.verify @@ -55,6 +80,31 @@ class MediaViewControllerTest : SysuiTestCase() { com.android.systemui.statusbar.phone.ConfigurationControllerImpl(context) private var player = TransitionLayout(context, /* attrs */ null, /* defStyleAttr */ 0) private var recommendation = TransitionLayout(context, /* attrs */ null, /* defStyleAttr */ 0) + private val clock = FakeSystemClock() + private lateinit var mainExecutor: FakeExecutor + private lateinit var seekBar: SeekBar + private lateinit var multiRippleView: MultiRippleView + private lateinit var turbulenceNoiseView: TurbulenceNoiseView + private lateinit var loadingEffectView: LoadingEffectView + private lateinit var settings: ImageButton + private lateinit var cancel: View + private lateinit var cancelText: TextView + private lateinit var dismiss: FrameLayout + private lateinit var dismissText: TextView + private lateinit var titleText: TextView + private lateinit var artistText: TextView + private lateinit var explicitIndicator: CachingIconView + private lateinit var seamless: ViewGroup + private lateinit var seamlessButton: View + private lateinit var seamlessIcon: ImageView + private lateinit var seamlessText: TextView + private lateinit var scrubbingElapsedTimeView: TextView + private lateinit var scrubbingTotalTimeView: TextView + private lateinit var actionPlayPause: ImageButton + private lateinit var actionNext: ImageButton + private lateinit var actionPrev: ImageButton + @Mock private lateinit var seamlessBackground: RippleDrawable + @Mock private lateinit var albumView: ImageView @Mock lateinit var logger: MediaViewLogger @Mock private lateinit var mockViewState: TransitionViewState @Mock private lateinit var mockCopiedState: TransitionViewState @@ -64,6 +114,14 @@ class MediaViewControllerTest : SysuiTestCase() { @Mock private lateinit var mediaSubTitleWidgetState: WidgetState @Mock private lateinit var mediaContainerWidgetState: WidgetState @Mock private lateinit var mediaFlags: MediaFlags + @Mock private lateinit var seekBarViewModel: SeekBarViewModel + @Mock private lateinit var seekBarData: LiveData<SeekBarViewModel.Progress> + @Mock private lateinit var globalSettings: GlobalSettings + @Mock private lateinit var viewHolder: MediaViewHolder + @Mock private lateinit var view: TransitionLayout + @Mock private lateinit var mockAnimator: AnimatorSet + @Mock private lateinit var gutsViewHolder: GutsViewHolder + @Mock private lateinit var gutsText: TextView private val delta = 0.1F @@ -72,14 +130,30 @@ class MediaViewControllerTest : SysuiTestCase() { @Before fun setup() { MockitoAnnotations.initMocks(this) + mainExecutor = FakeExecutor(clock) mediaViewController = - MediaViewController( - context, - configurationController, - mediaHostStatesManager, - logger, - mediaFlags, - ) + object : + MediaViewController( + context, + configurationController, + mediaHostStatesManager, + logger, + seekBarViewModel, + mainExecutor, + mediaFlags, + globalSettings, + ) { + override fun loadAnimator( + context: Context, + animId: Int, + motionInterpolator: Interpolator?, + vararg targets: View? + ): AnimatorSet { + return mockAnimator + } + } + initGutsViewHolderMocks() + initMediaViewHolderMocks() } @Test @@ -299,4 +373,270 @@ class MediaViewControllerTest : SysuiTestCase() { verify(mediaTitleWidgetState).alpha = floatThat { kotlin.math.abs(it - 1.0F) < delta } verify(mediaSubTitleWidgetState).alpha = floatThat { kotlin.math.abs(it - 1.0F) < delta } } + + @Test + fun attachPlayer_seekBarDisabled_seekBarVisibilityIsSetToInvisible() { + whenever(mediaFlags.isMediaControlsRefactorEnabled()).thenReturn(true) + + mediaViewController.attachPlayer(viewHolder) + getEnabledChangeListener().onEnabledChanged(enabled = true) + getEnabledChangeListener().onEnabledChanged(enabled = false) + + assertThat(mediaViewController.expandedLayout.getVisibility(R.id.media_progress_bar)) + .isEqualTo(ConstraintSet.INVISIBLE) + } + + @Test + fun attachPlayer_seekBarEnabled_seekBarVisible() { + whenever(mediaFlags.isMediaControlsRefactorEnabled()).thenReturn(true) + + mediaViewController.attachPlayer(viewHolder) + getEnabledChangeListener().onEnabledChanged(enabled = true) + + assertThat(mediaViewController.expandedLayout.getVisibility(R.id.media_progress_bar)) + .isEqualTo(ConstraintSet.VISIBLE) + } + + @Test + fun attachPlayer_seekBarStatusUpdate_seekBarVisibilityChanges() { + whenever(mediaFlags.isMediaControlsRefactorEnabled()).thenReturn(true) + + mediaViewController.attachPlayer(viewHolder) + getEnabledChangeListener().onEnabledChanged(enabled = true) + + assertThat(mediaViewController.expandedLayout.getVisibility(R.id.media_progress_bar)) + .isEqualTo(ConstraintSet.VISIBLE) + + getEnabledChangeListener().onEnabledChanged(enabled = false) + + assertThat(mediaViewController.expandedLayout.getVisibility(R.id.media_progress_bar)) + .isEqualTo(ConstraintSet.INVISIBLE) + } + + @Test + fun attachPlayer_notScrubbing_scrubbingViewsGone() { + whenever(mediaFlags.isMediaControlsRefactorEnabled()).thenReturn(true) + + mediaViewController.attachPlayer(viewHolder) + mediaViewController.canShowScrubbingTime = true + getScrubbingChangeListener().onScrubbingChanged(true) + getScrubbingChangeListener().onScrubbingChanged(false) + mainExecutor.runAllReady() + + assertThat( + mediaViewController.expandedLayout.getVisibility(R.id.media_scrubbing_elapsed_time) + ) + .isEqualTo(ConstraintSet.GONE) + assertThat( + mediaViewController.expandedLayout.getVisibility(R.id.media_scrubbing_total_time) + ) + .isEqualTo(ConstraintSet.GONE) + } + + @Test + fun setIsScrubbing_noSemanticActions_scrubbingViewsGone() { + whenever(mediaFlags.isMediaControlsRefactorEnabled()).thenReturn(true) + + mediaViewController.attachPlayer(viewHolder) + mediaViewController.canShowScrubbingTime = false + getScrubbingChangeListener().onScrubbingChanged(true) + mainExecutor.runAllReady() + + assertThat( + mediaViewController.expandedLayout.getVisibility(R.id.media_scrubbing_elapsed_time) + ) + .isEqualTo(ConstraintSet.GONE) + assertThat( + mediaViewController.expandedLayout.getVisibility(R.id.media_scrubbing_total_time) + ) + .isEqualTo(ConstraintSet.GONE) + } + + @Test + fun setIsScrubbing_noPrevButton_scrubbingTimesNotShown() { + whenever(mediaFlags.isMediaControlsRefactorEnabled()).thenReturn(true) + + mediaViewController.attachPlayer(viewHolder) + mediaViewController.setUpNextButtonInfo(true) + mediaViewController.setUpPrevButtonInfo(false) + getScrubbingChangeListener().onScrubbingChanged(true) + mainExecutor.runAllReady() + + assertThat(mediaViewController.expandedLayout.getVisibility(R.id.actionNext)) + .isEqualTo(ConstraintSet.VISIBLE) + assertThat( + mediaViewController.expandedLayout.getVisibility(R.id.media_scrubbing_elapsed_time) + ) + .isEqualTo(ConstraintSet.GONE) + assertThat( + mediaViewController.expandedLayout.getVisibility(R.id.media_scrubbing_total_time) + ) + .isEqualTo(ConstraintSet.GONE) + } + + @Test + fun setIsScrubbing_noNextButton_scrubbingTimesNotShown() { + whenever(mediaFlags.isMediaControlsRefactorEnabled()).thenReturn(true) + + mediaViewController.attachPlayer(viewHolder) + mediaViewController.setUpNextButtonInfo(false) + mediaViewController.setUpPrevButtonInfo(true) + getScrubbingChangeListener().onScrubbingChanged(true) + mainExecutor.runAllReady() + + assertThat(mediaViewController.expandedLayout.getVisibility(R.id.actionPrev)) + .isEqualTo(ConstraintSet.VISIBLE) + assertThat( + mediaViewController.expandedLayout.getVisibility(R.id.media_scrubbing_elapsed_time) + ) + .isEqualTo(ConstraintSet.GONE) + assertThat( + mediaViewController.expandedLayout.getVisibility(R.id.media_scrubbing_total_time) + ) + .isEqualTo(ConstraintSet.GONE) + } + + @Test + fun setIsScrubbing_scrubbingViewsShownAndPrevNextHiddenOnlyInExpanded() { + whenever(mediaFlags.isMediaControlsRefactorEnabled()).thenReturn(true) + + mediaViewController.attachPlayer(viewHolder) + mediaViewController.setUpNextButtonInfo(true) + mediaViewController.setUpPrevButtonInfo(true) + mediaViewController.canShowScrubbingTime = true + getScrubbingChangeListener().onScrubbingChanged(true) + mainExecutor.runAllReady() + + // Only in expanded, we should show the scrubbing times and hide prev+next + assertThat(mediaViewController.expandedLayout.getVisibility(R.id.actionPrev)) + .isEqualTo(ConstraintSet.GONE) + assertThat(mediaViewController.expandedLayout.getVisibility(R.id.actionNext)) + .isEqualTo(ConstraintSet.GONE) + assertThat( + mediaViewController.expandedLayout.getVisibility(R.id.media_scrubbing_elapsed_time) + ) + .isEqualTo(ConstraintSet.VISIBLE) + assertThat( + mediaViewController.expandedLayout.getVisibility(R.id.media_scrubbing_total_time) + ) + .isEqualTo(ConstraintSet.VISIBLE) + } + + @Test + fun setIsScrubbing_trueThenFalse_reservePrevAndNextButtons() { + whenever(mediaFlags.isMediaControlsRefactorEnabled()).thenReturn(true) + + mediaViewController.attachPlayer(viewHolder) + mediaViewController.setUpNextButtonInfo(true, ConstraintSet.INVISIBLE) + mediaViewController.setUpPrevButtonInfo(true, ConstraintSet.INVISIBLE) + mediaViewController.canShowScrubbingTime = true + + getScrubbingChangeListener().onScrubbingChanged(true) + mainExecutor.runAllReady() + + assertThat(mediaViewController.expandedLayout.getVisibility(R.id.actionPrev)) + .isEqualTo(ConstraintSet.INVISIBLE) + assertThat(mediaViewController.expandedLayout.getVisibility(R.id.actionNext)) + .isEqualTo(ConstraintSet.INVISIBLE) + assertThat( + mediaViewController.expandedLayout.getVisibility(R.id.media_scrubbing_elapsed_time) + ) + .isEqualTo(ConstraintSet.VISIBLE) + assertThat( + mediaViewController.expandedLayout.getVisibility(R.id.media_scrubbing_total_time) + ) + .isEqualTo(ConstraintSet.VISIBLE) + + getScrubbingChangeListener().onScrubbingChanged(false) + mainExecutor.runAllReady() + + // Only in expanded, we should hide the scrubbing times and show prev+next + assertThat(mediaViewController.expandedLayout.getVisibility(R.id.actionPrev)) + .isEqualTo(ConstraintSet.VISIBLE) + assertThat(mediaViewController.expandedLayout.getVisibility(R.id.actionNext)) + .isEqualTo(ConstraintSet.VISIBLE) + assertThat( + mediaViewController.expandedLayout.getVisibility(R.id.media_scrubbing_elapsed_time) + ) + .isEqualTo(ConstraintSet.GONE) + assertThat( + mediaViewController.expandedLayout.getVisibility(R.id.media_scrubbing_total_time) + ) + .isEqualTo(ConstraintSet.GONE) + } + + private fun initGutsViewHolderMocks() { + settings = ImageButton(context) + cancel = View(context) + cancelText = TextView(context) + dismiss = FrameLayout(context) + dismissText = TextView(context) + whenever(gutsViewHolder.gutsText).thenReturn(gutsText) + whenever(gutsViewHolder.settings).thenReturn(settings) + whenever(gutsViewHolder.cancel).thenReturn(cancel) + whenever(gutsViewHolder.cancelText).thenReturn(cancelText) + whenever(gutsViewHolder.dismiss).thenReturn(dismiss) + whenever(gutsViewHolder.dismissText).thenReturn(dismissText) + } + + private fun initMediaViewHolderMocks() { + titleText = TextView(context) + artistText = TextView(context) + explicitIndicator = CachingIconView(context).also { it.id = R.id.media_explicit_indicator } + seamless = FrameLayout(context) + seamless.foreground = seamlessBackground + seamlessButton = View(context) + seamlessIcon = ImageView(context) + seamlessText = TextView(context) + seekBar = SeekBar(context).also { it.id = R.id.media_progress_bar } + + actionPlayPause = ImageButton(context).also { it.id = R.id.actionPlayPause } + actionPrev = ImageButton(context).also { it.id = R.id.actionPrev } + actionNext = ImageButton(context).also { it.id = R.id.actionNext } + scrubbingElapsedTimeView = + TextView(context).also { it.id = R.id.media_scrubbing_elapsed_time } + scrubbingTotalTimeView = TextView(context).also { it.id = R.id.media_scrubbing_total_time } + + multiRippleView = MultiRippleView(context, null) + turbulenceNoiseView = TurbulenceNoiseView(context, null) + loadingEffectView = LoadingEffectView(context, null) + + whenever(viewHolder.player).thenReturn(view) + whenever(view.context).thenReturn(context) + whenever(viewHolder.albumView).thenReturn(albumView) + whenever(albumView.foreground).thenReturn(Mockito.mock(Drawable::class.java)) + whenever(viewHolder.titleText).thenReturn(titleText) + whenever(viewHolder.artistText).thenReturn(artistText) + whenever(viewHolder.explicitIndicator).thenReturn(explicitIndicator) + whenever(seamlessBackground.getDrawable(0)) + .thenReturn(Mockito.mock(GradientDrawable::class.java)) + whenever(viewHolder.seamless).thenReturn(seamless) + whenever(viewHolder.seamlessButton).thenReturn(seamlessButton) + whenever(viewHolder.seamlessIcon).thenReturn(seamlessIcon) + whenever(viewHolder.seamlessText).thenReturn(seamlessText) + whenever(viewHolder.seekBar).thenReturn(seekBar) + whenever(viewHolder.scrubbingElapsedTimeView).thenReturn(scrubbingElapsedTimeView) + whenever(viewHolder.scrubbingTotalTimeView).thenReturn(scrubbingTotalTimeView) + whenever(viewHolder.gutsViewHolder).thenReturn(gutsViewHolder) + whenever(seekBarViewModel.progress).thenReturn(seekBarData) + + // Action buttons + whenever(viewHolder.actionPlayPause).thenReturn(actionPlayPause) + whenever(viewHolder.getAction(R.id.actionNext)).thenReturn(actionNext) + whenever(viewHolder.getAction(R.id.actionPrev)).thenReturn(actionPrev) + whenever(viewHolder.getAction(R.id.actionPlayPause)).thenReturn(actionPlayPause) + + whenever(viewHolder.multiRippleView).thenReturn(multiRippleView) + whenever(viewHolder.turbulenceNoiseView).thenReturn(turbulenceNoiseView) + whenever(viewHolder.loadingEffectView).thenReturn(loadingEffectView) + } + + private fun getScrubbingChangeListener(): SeekBarViewModel.ScrubbingChangeListener = + withArgCaptor { + verify(seekBarViewModel).setScrubbingChangeListener(capture()) + } + + private fun getEnabledChangeListener(): SeekBarViewModel.EnabledChangeListener = withArgCaptor { + verify(seekBarViewModel).setEnabledChangeListener(capture()) + } } 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 b04503b8e031..8c5a4d0bbcae 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt @@ -578,6 +578,14 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() { assertEquals(keyEvent, falsingCollector.lastKeyEvent) } + @Test + fun cancelCurrentTouch_callsDragDownHelper() { + mSetFlagsRule.enableFlags(Flags.FLAG_MIGRATE_CLOCKS_TO_BLUEPRINT) + underTest.cancelCurrentTouch() + + verify(dragDownHelper).stopDragging() + } + companion object { private val DOWN_EVENT = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_DOWN, 0f, 0f, 0) private val MOVE_EVENT = MotionEvent.obtain(0L, 0L, MotionEvent.ACTION_MOVE, 0f, 0f, 0) diff --git a/packages/SystemUI/tests/src/com/android/systemui/shadow/DoubleShadowTextClockTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shadow/DoubleShadowTextClockTest.kt index eb418fd05b5f..4679a58645d9 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shadow/DoubleShadowTextClockTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shadow/DoubleShadowTextClockTest.kt @@ -19,11 +19,12 @@ package com.android.systemui.shadow import android.content.Context import android.content.res.Resources import android.content.res.TypedArray +import android.platform.test.annotations.PlatinumTest import android.testing.AndroidTestingRunner import android.util.AttributeSet import androidx.test.filters.SmallTest -import com.android.systemui.shared.R import com.android.systemui.SysuiTestCase +import com.android.systemui.shared.R import com.android.systemui.shared.shadow.DoubleShadowTextClock import com.android.systemui.util.mockito.whenever import junit.framework.Assert.assertTrue @@ -36,6 +37,7 @@ import org.mockito.MockitoAnnotations import org.mockito.junit.MockitoJUnit import org.mockito.junit.MockitoRule +@PlatinumTest(focusArea = "sysui") @SmallTest @RunWith(AndroidTestingRunner::class) class DoubleShadowTextClockTest : SysuiTestCase() { 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 56fc0c74dafa..a05a23bb8bb1 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 @@ -21,7 +21,7 @@ import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator.Direction import com.android.systemui.shared.animation.UnfoldConstantTranslateAnimator.ViewIdToTranslate -import com.android.systemui.unfold.TestUnfoldTransitionProvider +import com.android.systemui.unfold.FakeUnfoldTransitionProvider import org.junit.Assert.assertEquals import org.junit.Before import org.junit.Test @@ -34,21 +34,19 @@ import org.mockito.MockitoAnnotations @RunWith(AndroidTestingRunner::class) class UnfoldConstantTranslateAnimatorTest : SysuiTestCase() { - private val progressProvider = TestUnfoldTransitionProvider() + private val progressProvider = FakeUnfoldTransitionProvider() - @Mock - private lateinit var parent: ViewGroup + @Mock private lateinit var parent: ViewGroup - @Mock - private lateinit var shouldBeAnimated: () -> Boolean + @Mock private lateinit var shouldBeAnimated: () -> Boolean private lateinit var animator: UnfoldConstantTranslateAnimator private val viewsIdToRegister get() = setOf( - ViewIdToTranslate(START_VIEW_ID, Direction.START, shouldBeAnimated), - ViewIdToTranslate(END_VIEW_ID, Direction.END, shouldBeAnimated) + ViewIdToTranslate(START_VIEW_ID, Direction.START, shouldBeAnimated), + ViewIdToTranslate(END_VIEW_ID, Direction.END, shouldBeAnimated) ) @Before @@ -122,11 +120,12 @@ class UnfoldConstantTranslateAnimatorTest : SysuiTestCase() { progressProvider.onTransitionStarted() progressProvider.onTransitionProgress(0f) - val rtlMultiplier = if (layoutDirection == View.LAYOUT_DIRECTION_LTR) { - 1 - } else { - -1 - } + val rtlMultiplier = + if (layoutDirection == View.LAYOUT_DIRECTION_LTR) { + 1 + } else { + -1 + } list.forEach { (view, direction) -> assertEquals( (-MAX_TRANSLATION * direction * rtlMultiplier).toInt(), diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicChildBindControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicChildBindControllerTest.java index eb692eb1b4ac..0e24ed49fea0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicChildBindControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/DynamicChildBindControllerTest.java @@ -40,6 +40,9 @@ import com.android.systemui.statusbar.notification.collection.NotificationEntryB import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; import com.android.systemui.statusbar.notification.row.RowContentBindParams; import com.android.systemui.statusbar.notification.row.RowContentBindStage; +import com.android.systemui.statusbar.notification.row.RowInflaterTask; +import com.android.systemui.statusbar.notification.row.RowInflaterTaskLogger; +import com.android.systemui.util.time.FakeSystemClock; import org.junit.Before; import org.junit.Test; @@ -114,20 +117,25 @@ public class DynamicChildBindControllerTest extends SysuiTestCase { private NotificationEntry addGroup(int size) { NotificationEntry summary = new NotificationEntryBuilder().build(); - summary.setRow(createRow()); + summary.setRow(createRow(summary)); ArrayList<NotificationEntry> children = new ArrayList<>(); for (int i = 0; i < size; i++) { NotificationEntry child = new NotificationEntryBuilder().build(); - child.setRow(createRow()); + child.setRow(createRow(child)); children.add(child); } mGroupNotifs.put(summary, children); return summary; } - private ExpandableNotificationRow createRow() { + private ExpandableNotificationRow createRow(NotificationEntry entry) { + LayoutInflater inflater = LayoutInflater.from(mContext); + inflater.setFactory2( + new RowInflaterTask.RowAsyncLayoutInflater(entry, new FakeSystemClock(), mock( + RowInflaterTaskLogger.class))); + ExpandableNotificationRow row = (ExpandableNotificationRow) - LayoutInflater.from(mContext).inflate(R.layout.status_bar_notification_row, null); + inflater.inflate(R.layout.status_bar_notification_row, null); row.getPrivateLayout().setContractedChild(new View(mContext)); row.getPrivateLayout().setExpandedChild(new View(mContext)); return row; diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java index 01492f629fe8..aa79c23bdf74 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowTest.java @@ -772,8 +772,7 @@ public class ExpandableNotificationRowTest extends SysuiTestCase { row.setUserExpanded(true); row.setOnKeyguard(false); row.setSensitive(/* sensitive= */true, /* hideSensitive= */false); - row.setHideSensitive(/* hideSensitive= */true, /* animated= */false, - /* delay= */0L, /* duration= */0L); + row.setHideSensitiveForIntrinsicHeight(/* hideSensitive= */true); // THEN assertThat(row.isExpanded()).isFalse(); @@ -787,8 +786,7 @@ public class ExpandableNotificationRowTest extends SysuiTestCase { row.setUserExpanded(true); row.setOnKeyguard(false); row.setSensitive(/* sensitive= */true, /* hideSensitive= */false); - row.setHideSensitive(/* hideSensitive= */false, /* animated= */false, - /* delay= */0L, /* duration= */0L); + row.setHideSensitiveForIntrinsicHeight(/* hideSensitive= */false); // THEN assertThat(row.isExpanded()).isTrue(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.kt index 91e46668ed44..7332bc34b030 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationContentViewTest.kt @@ -79,10 +79,11 @@ class NotificationContentViewTest : SysuiTestCase() { initMocks(this) fakeParent = spy(FrameLayout(mContext, /* attrs= */ null).also { it.visibility = View.GONE }) + val mockEntry = createMockNotificationEntry() row = spy( - ExpandableNotificationRow(mContext, /* attrs= */ null).apply { - entry = createMockNotificationEntry() + ExpandableNotificationRow(mContext, /* attrs= */ null, mockEntry).apply { + entry = mockEntry } ) ViewUtils.attachView(fakeParent) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt index 9a7b8ec2ec07..745d20dd686b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt @@ -1,5 +1,6 @@ package com.android.systemui.statusbar.notification.stack +import android.service.notification.StatusBarNotification import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper import android.view.LayoutInflater @@ -14,6 +15,7 @@ import com.android.systemui.res.R import com.android.systemui.shade.transition.LargeScreenShadeInterpolator import com.android.systemui.statusbar.NotificationShelf import com.android.systemui.statusbar.StatusBarIconView +import com.android.systemui.statusbar.notification.collection.NotificationEntry import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow import com.android.systemui.statusbar.notification.row.ExpandableView import com.android.systemui.statusbar.notification.shared.NotificationIconContainerRefactor @@ -457,8 +459,13 @@ open class NotificationShelfTest : SysuiTestCase() { expansionFraction: Float, expectedAlpha: Float ) { + val sbnMock: StatusBarNotification = mock() + val mockEntry = mock<NotificationEntry>().apply { + whenever(this.sbn).thenReturn(sbnMock) + } + val row = ExpandableNotificationRow(mContext, null, mockEntry) whenever(ambientState.lastVisibleBackgroundChild) - .thenReturn(ExpandableNotificationRow(mContext, null)) + .thenReturn(row) whenever(ambientState.isExpansionChanging).thenReturn(true) whenever(ambientState.expansionFraction).thenReturn(expansionFraction) whenever(hostLayoutController.speedBumpIndex).thenReturn(0) diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/HideNotificationsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/HideNotificationsInteractorTest.kt index 4bfd7e3bef83..e2ac2038be32 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/HideNotificationsInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/domain/interactor/HideNotificationsInteractorTest.kt @@ -31,9 +31,9 @@ import com.android.systemui.power.domain.interactor.PowerInteractor import com.android.systemui.power.shared.model.ScreenPowerState.SCREEN_ON import com.android.systemui.power.shared.model.WakefulnessState.STARTING_TO_SLEEP import com.android.systemui.statusbar.policy.FakeConfigurationController -import com.android.systemui.unfold.TestUnfoldTransitionProvider +import com.android.systemui.unfold.FakeUnfoldTransitionProvider import com.android.systemui.unfold.data.repository.UnfoldTransitionRepositoryImpl -import com.android.systemui.unfold.domain.interactor.UnfoldTransitionInteractorImpl +import com.android.systemui.unfold.domain.interactor.UnfoldTransitionInteractor import com.android.systemui.util.animation.data.repository.FakeAnimationStatusRepository import com.android.systemui.util.mockito.mock import com.google.common.truth.Truth.assertThat @@ -59,7 +59,7 @@ open class HideNotificationsInteractorTest : SysuiTestCase() { private val animationStatus = FakeAnimationStatusRepository() private val configurationController = FakeConfigurationController() - private val unfoldTransitionProgressProvider = TestUnfoldTransitionProvider() + private val unfoldTransitionProgressProvider = FakeUnfoldTransitionProvider() private val powerRepository = FakePowerRepository() private val powerInteractor = PowerInteractor( @@ -69,11 +69,6 @@ open class HideNotificationsInteractorTest : SysuiTestCase() { statusBarStateController = mock() ) - private val unfoldTransitionRepository = - UnfoldTransitionRepositoryImpl(Optional.of(unfoldTransitionProgressProvider)) - private val unfoldTransitionInteractor = - UnfoldTransitionInteractorImpl(unfoldTransitionRepository) - private val configurationRepository = ConfigurationRepositoryImpl( configurationController, @@ -83,6 +78,11 @@ open class HideNotificationsInteractorTest : SysuiTestCase() { ) private val configurationInteractor = ConfigurationInteractor(configurationRepository) + private val unfoldTransitionRepository = + UnfoldTransitionRepositoryImpl(Optional.of(unfoldTransitionProgressProvider)) + private val unfoldTransitionInteractor = + UnfoldTransitionInteractor(unfoldTransitionRepository, configurationInteractor) + private lateinit var configuration: Configuration private lateinit var underTest: HideNotificationsInteractor diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarMoveFromCenterAnimationControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarMoveFromCenterAnimationControllerTest.kt index feff046bb708..1ec1765e2e57 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarMoveFromCenterAnimationControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarMoveFromCenterAnimationControllerTest.kt @@ -8,7 +8,7 @@ import android.view.View import android.view.WindowManager import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase -import com.android.systemui.unfold.TestUnfoldTransitionProvider +import com.android.systemui.unfold.FakeUnfoldTransitionProvider import com.android.systemui.unfold.util.CurrentActivityTypeProvider import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider import com.android.systemui.util.mockito.whenever @@ -23,17 +23,14 @@ import org.mockito.MockitoAnnotations @TestableLooper.RunWithLooper class StatusBarMoveFromCenterAnimationControllerTest : SysuiTestCase() { - @Mock - private lateinit var windowManager: WindowManager + @Mock private lateinit var windowManager: WindowManager - @Mock - private lateinit var display: Display + @Mock private lateinit var display: Display - @Mock - private lateinit var currentActivityTypeProvider: CurrentActivityTypeProvider + @Mock private lateinit var currentActivityTypeProvider: CurrentActivityTypeProvider private val view: View = View(context) - private val progressProvider = TestUnfoldTransitionProvider() + private val progressProvider = FakeUnfoldTransitionProvider() private val scopedProvider = ScopedUnfoldTransitionProgressProvider(progressProvider) private lateinit var controller: StatusBarMoveFromCenterAnimationController diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt index 42bbe3e6fa1d..c4ab943c09df 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/domain/interactor/DeviceBasedSatelliteInteractorTest.kt @@ -26,6 +26,10 @@ import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.FakeMobi import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy import com.android.systemui.statusbar.pipeline.satellite.data.prod.FakeDeviceBasedSatelliteRepository import com.android.systemui.statusbar.pipeline.satellite.shared.model.SatelliteConnectionState +import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository +import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository +import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractorImpl +import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepository import com.android.systemui.statusbar.policy.domain.interactor.DeviceProvisioningInteractor import com.android.systemui.util.mockito.mock @@ -53,6 +57,10 @@ class DeviceBasedSatelliteInteractorTest : SysuiTestCase() { private val deviceProvisionedRepository = FakeDeviceProvisioningRepository() private val deviceProvisioningInteractor = DeviceProvisioningInteractor(deviceProvisionedRepository) + private val connectivityRepository = FakeConnectivityRepository() + private val wifiRepository = FakeWifiRepository() + private val wifiInteractor = + WifiInteractorImpl(connectivityRepository, wifiRepository, testScope.backgroundScope) @Before fun setUp() { @@ -61,6 +69,7 @@ class DeviceBasedSatelliteInteractorTest : SysuiTestCase() { repo, iconsInteractor, deviceProvisioningInteractor, + wifiInteractor, testScope.backgroundScope, ) } @@ -103,6 +112,7 @@ class DeviceBasedSatelliteInteractorTest : SysuiTestCase() { repo, iconsInteractor, deviceProvisioningInteractor, + wifiInteractor, testScope.backgroundScope, ) @@ -150,6 +160,7 @@ class DeviceBasedSatelliteInteractorTest : SysuiTestCase() { repo, iconsInteractor, deviceProvisioningInteractor, + wifiInteractor, testScope.backgroundScope, ) @@ -205,6 +216,7 @@ class DeviceBasedSatelliteInteractorTest : SysuiTestCase() { repo, iconsInteractor, deviceProvisioningInteractor, + wifiInteractor, testScope.backgroundScope, ) @@ -337,6 +349,7 @@ class DeviceBasedSatelliteInteractorTest : SysuiTestCase() { repo, iconsInteractor, deviceProvisioningInteractor, + wifiInteractor, testScope.backgroundScope, ) @@ -353,4 +366,28 @@ class DeviceBasedSatelliteInteractorTest : SysuiTestCase() { // THEN the value is still false, because the flag is off assertThat(latest).isFalse() } + + @Test + fun isWifiActive_falseWhenWifiNotActive() = + testScope.runTest { + val latest by collectLastValue(underTest.isWifiActive) + + // WHEN wifi is not active + wifiRepository.setWifiNetwork(WifiNetworkModel.Invalid("test")) + + // THEN the interactor returns false due to the wifi network not being active + assertThat(latest).isFalse() + } + + @Test + fun isWifiActive_trueWhenWifiIsActive() = + testScope.runTest { + val latest by collectLastValue(underTest.isWifiActive) + + // WHEN wifi is active + wifiRepository.setWifiNetwork(WifiNetworkModel.Active(networkId = 0, level = 1)) + + // THEN the interactor returns true due to the wifi network being active + assertThat(latest).isTrue() + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt index 1d6cd3774449..64f19b6c60ad 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt @@ -26,6 +26,10 @@ import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.FakeMobi import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy import com.android.systemui.statusbar.pipeline.satellite.data.prod.FakeDeviceBasedSatelliteRepository import com.android.systemui.statusbar.pipeline.satellite.domain.interactor.DeviceBasedSatelliteInteractor +import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository +import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository +import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractorImpl +import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel import com.android.systemui.statusbar.policy.data.repository.FakeDeviceProvisioningRepository import com.android.systemui.statusbar.policy.domain.interactor.DeviceProvisioningInteractor import com.android.systemui.util.mockito.mock @@ -44,14 +48,18 @@ class DeviceBasedSatelliteViewModelTest : SysuiTestCase() { private lateinit var underTest: DeviceBasedSatelliteViewModel private lateinit var interactor: DeviceBasedSatelliteInteractor private lateinit var airplaneModeRepository: FakeAirplaneModeRepository - private val repo = FakeDeviceBasedSatelliteRepository() + private val testScope = TestScope() + private val mobileIconsInteractor = FakeMobileIconsInteractor(FakeMobileMappingsProxy(), mock()) + private val deviceProvisionedRepository = FakeDeviceProvisioningRepository() private val deviceProvisioningInteractor = DeviceProvisioningInteractor(deviceProvisionedRepository) - - private val testScope = TestScope() + private val connectivityRepository = FakeConnectivityRepository() + private val wifiRepository = FakeWifiRepository() + private val wifiInteractor = + WifiInteractorImpl(connectivityRepository, wifiRepository, testScope.backgroundScope) @Before fun setUp() { @@ -63,6 +71,7 @@ class DeviceBasedSatelliteViewModelTest : SysuiTestCase() { repo, mobileIconsInteractor, deviceProvisioningInteractor, + wifiInteractor, testScope.backgroundScope, ) @@ -253,4 +262,40 @@ class DeviceBasedSatelliteViewModelTest : SysuiTestCase() { // THEN icon is null because the device is not provisioned assertThat(latest).isInstanceOf(Icon::class.java) } + + @OptIn(ExperimentalCoroutinesApi::class) + @Test + fun icon_wifiIsActive() = + testScope.runTest { + val latest by collectLastValue(underTest.icon) + + // GIVEN satellite is allowed + repo.isSatelliteAllowedForCurrentLocation.value = true + + // GIVEN all icons are OOS + val i1 = mobileIconsInteractor.getMobileConnectionInteractorForSubId(1) + i1.isInService.value = false + i1.isEmergencyOnly.value = false + + // GIVEN apm is disabled + airplaneModeRepository.setIsAirplaneMode(false) + + // GIVEN device is provisioned + deviceProvisionedRepository.setDeviceProvisioned(true) + + // GIVEN wifi network is active + wifiRepository.setWifiNetwork(WifiNetworkModel.Active(networkId = 0, level = 1)) + + // THEN icon is null because the device is connected to wifi + assertThat(latest).isNull() + + // GIVEN device loses wifi connection + wifiRepository.setWifiNetwork(WifiNetworkModel.Invalid("test")) + + // Wait for delay to be completed + advanceTimeBy(10.seconds) + + // THEN icon is set because the device lost wifi connection + assertThat(latest).isInstanceOf(Icon::class.java) + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt index 28adbceda847..2cdc8d8ffc84 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/DisplaySwitchLatencyTrackerTest.kt @@ -22,6 +22,8 @@ import android.testing.AndroidTestingRunner import androidx.test.filters.SmallTest import com.android.internal.R import com.android.systemui.SysuiTestCase +import com.android.systemui.common.ui.data.repository.ConfigurationRepositoryImpl +import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor import com.android.systemui.display.data.repository.DeviceStateRepository import com.android.systemui.display.data.repository.DeviceStateRepository.DeviceState import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor @@ -31,11 +33,12 @@ import com.android.systemui.power.shared.model.WakeSleepReason import com.android.systemui.power.shared.model.WakefulnessModel import com.android.systemui.power.shared.model.WakefulnessState import com.android.systemui.shared.system.SysUiStatsLog +import com.android.systemui.statusbar.policy.FakeConfigurationController import com.android.systemui.unfold.DisplaySwitchLatencyTracker.Companion.FOLDABLE_DEVICE_STATE_CLOSED import com.android.systemui.unfold.DisplaySwitchLatencyTracker.Companion.FOLDABLE_DEVICE_STATE_HALF_OPEN import com.android.systemui.unfold.DisplaySwitchLatencyTracker.DisplaySwitchLatencyEvent import com.android.systemui.unfold.data.repository.UnfoldTransitionRepositoryImpl -import com.android.systemui.unfold.domain.interactor.UnfoldTransitionInteractorImpl +import com.android.systemui.unfold.domain.interactor.UnfoldTransitionInteractor import com.android.systemui.util.animation.data.repository.AnimationStatusRepository import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.capture @@ -86,11 +89,20 @@ class DisplaySwitchLatencyTrackerTest : SysuiTestCase() { private val areAnimationEnabled = MutableStateFlow(true) private val lastWakefulnessEvent = MutableStateFlow(WakefulnessModel()) private val systemClock = FakeSystemClock() - private val unfoldTransitionProgressProvider = TestUnfoldTransitionProvider() + private val configurationController = FakeConfigurationController() + private val configurationRepository = + ConfigurationRepositoryImpl( + configurationController, + context, + testScope.backgroundScope, + mock() + ) + private val configurationInteractor = ConfigurationInteractor(configurationRepository) + private val unfoldTransitionProgressProvider = FakeUnfoldTransitionProvider() private val unfoldTransitionRepository = UnfoldTransitionRepositoryImpl(Optional.of(unfoldTransitionProgressProvider)) private val unfoldTransitionInteractor = - UnfoldTransitionInteractorImpl(unfoldTransitionRepository) + UnfoldTransitionInteractor(unfoldTransitionRepository, configurationInteractor) @Before fun setup() { @@ -155,7 +167,10 @@ class DisplaySwitchLatencyTrackerTest : SysuiTestCase() { fun unfold_progressUnavailable_logsLatencyTillScreenTurnedOn() { testScope.runTest { val unfoldTransitionInteractorWithEmptyProgressProvider = - UnfoldTransitionInteractorImpl(UnfoldTransitionRepositoryImpl(Optional.empty())) + UnfoldTransitionInteractor( + UnfoldTransitionRepositoryImpl(Optional.empty()), + configurationInteractor, + ) displaySwitchLatencyTracker = DisplaySwitchLatencyTracker( mockContext, diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldHapticsPlayerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldHapticsPlayerTest.kt index b9c7e6133669..fd513c9c9235 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldHapticsPlayerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldHapticsPlayerTest.kt @@ -35,7 +35,7 @@ import org.mockito.Mockito.verify @SmallTest class UnfoldHapticsPlayerTest : SysuiTestCase() { - private val progressProvider = TestUnfoldTransitionProvider() + private val progressProvider = FakeUnfoldTransitionProvider() private val vibrator: Vibrator = mock() private val testFoldProvider = TestFoldProvider() diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt index ba72716997e3..2955384c0bbd 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldLatencyTrackerTest.kt @@ -27,6 +27,7 @@ import com.android.systemui.keyguard.ScreenLifecycle import com.android.systemui.unfold.util.FoldableDeviceStates import com.android.systemui.unfold.util.FoldableTestUtils import com.android.systemui.util.mockito.any +import java.util.Optional import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @@ -37,45 +38,41 @@ import org.mockito.Mockito.never import org.mockito.Mockito.verify import org.mockito.Mockito.verifyNoMoreInteractions import org.mockito.MockitoAnnotations -import java.util.Optional @RunWith(AndroidTestingRunner::class) @SmallTest class UnfoldLatencyTrackerTest : SysuiTestCase() { - @Mock - lateinit var latencyTracker: LatencyTracker + @Mock lateinit var latencyTracker: LatencyTracker - @Mock - lateinit var deviceStateManager: DeviceStateManager + @Mock lateinit var deviceStateManager: DeviceStateManager - @Mock - lateinit var screenLifecycle: ScreenLifecycle + @Mock lateinit var screenLifecycle: ScreenLifecycle - @Captor - private lateinit var foldStateListenerCaptor: ArgumentCaptor<FoldStateListener> + @Captor private lateinit var foldStateListenerCaptor: ArgumentCaptor<FoldStateListener> - @Captor - private lateinit var screenLifecycleCaptor: ArgumentCaptor<ScreenLifecycle.Observer> + @Captor private lateinit var screenLifecycleCaptor: ArgumentCaptor<ScreenLifecycle.Observer> private lateinit var deviceStates: FoldableDeviceStates private lateinit var unfoldLatencyTracker: UnfoldLatencyTracker - private val transitionProgressProvider = TestUnfoldTransitionProvider() + private val transitionProgressProvider = FakeUnfoldTransitionProvider() @Before fun setUp() { MockitoAnnotations.initMocks(this) - unfoldLatencyTracker = UnfoldLatencyTracker( - latencyTracker, - deviceStateManager, - Optional.of(transitionProgressProvider), - context.mainExecutor, - context, - context.contentResolver, - screenLifecycle - ).apply { init() } + unfoldLatencyTracker = + UnfoldLatencyTracker( + latencyTracker, + deviceStateManager, + Optional.of(transitionProgressProvider), + context.mainExecutor, + context, + context.contentResolver, + screenLifecycle + ) + .apply { init() } deviceStates = FoldableTestUtils.findDeviceStates(context) verify(deviceStateManager).registerCallback(any(), foldStateListenerCaptor.capture()) @@ -107,7 +104,7 @@ class UnfoldLatencyTrackerTest : SysuiTestCase() { } @Test - fun unfold_firstFoldEventAnimationsEnabledOnScreenTurnedOnAndTransitionStarted_eventNotPropagated() { + fun firstFoldEventAnimationsEnabledOnScreenTurnedOnAndTransitionStarted_eventNotPropagated() { setAnimationsEnabled(true) sendFoldEvent(folded = false) @@ -118,7 +115,7 @@ class UnfoldLatencyTrackerTest : SysuiTestCase() { } @Test - fun unfold_secondFoldEventAnimationsEnabledOnScreenTurnedOnAndTransitionStarted_eventPropagated() { + fun secondFoldEventAnimationsEnabledOnScreenTurnedOnAndTransitionStarted_eventPropagated() { setAnimationsEnabled(true) sendFoldEvent(folded = true) sendFoldEvent(folded = false) @@ -131,7 +128,7 @@ class UnfoldLatencyTrackerTest : SysuiTestCase() { } @Test - fun unfold_unfoldFoldUnfoldAnimationsEnabledOnScreenTurnedOnAndTransitionStarted_eventPropagated() { + fun unfoldFoldUnfoldAnimationsEnabledOnScreenTurnedOnAndTransitionStarted_eventPropagated() { setAnimationsEnabled(true) sendFoldEvent(folded = false) sendFoldEvent(folded = true) @@ -196,4 +193,4 @@ class UnfoldLatencyTrackerTest : SysuiTestCase() { durationScale.toString() ) } -}
\ No newline at end of file +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldTransitionWallpaperControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldTransitionWallpaperControllerTest.kt index 6ec0251d41a5..0c452eb9d461 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldTransitionWallpaperControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/UnfoldTransitionWallpaperControllerTest.kt @@ -17,21 +17,18 @@ import org.mockito.junit.MockitoJUnit @SmallTest class UnfoldTransitionWallpaperControllerTest : SysuiTestCase() { - @Mock - private lateinit var wallpaperController: WallpaperController + @Mock private lateinit var wallpaperController: WallpaperController - private val progressProvider = TestUnfoldTransitionProvider() + private val progressProvider = FakeUnfoldTransitionProvider() - @JvmField - @Rule - val mockitoRule = MockitoJUnit.rule() + @JvmField @Rule val mockitoRule = MockitoJUnit.rule() private lateinit var unfoldWallpaperController: UnfoldTransitionWallpaperController @Before fun setup() { - unfoldWallpaperController = UnfoldTransitionWallpaperController(progressProvider, - wallpaperController) + unfoldWallpaperController = + UnfoldTransitionWallpaperController(progressProvider, wallpaperController) unfoldWallpaperController.init() } diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/domain/interactor/UnfoldTransitionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/domain/interactor/UnfoldTransitionInteractorTest.kt deleted file mode 100644 index 6a801e01a4a5..000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/unfold/domain/interactor/UnfoldTransitionInteractorTest.kt +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright (C) 2023 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ -package com.android.systemui.unfold.domain.interactor - -import android.testing.AndroidTestingRunner -import androidx.test.filters.SmallTest -import com.android.systemui.SysuiTestCase -import com.android.systemui.unfold.TestUnfoldTransitionProvider -import com.android.systemui.unfold.data.repository.UnfoldTransitionRepositoryImpl -import com.google.common.truth.Truth.assertThat -import java.util.Optional -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.async -import kotlinx.coroutines.test.TestScope -import kotlinx.coroutines.test.runCurrent -import kotlinx.coroutines.test.runTest -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.mockito.MockitoAnnotations - -@OptIn(ExperimentalCoroutinesApi::class) -@SmallTest -@RunWith(AndroidTestingRunner::class) -open class UnfoldTransitionInteractorTest : SysuiTestCase() { - - private val testScope = TestScope() - - private val unfoldTransitionProgressProvider = TestUnfoldTransitionProvider() - private val unfoldTransitionRepository = - UnfoldTransitionRepositoryImpl(Optional.of(unfoldTransitionProgressProvider)) - - private lateinit var underTest: UnfoldTransitionInteractor - - @Before - fun setUp() { - MockitoAnnotations.initMocks(this) - - underTest = UnfoldTransitionInteractorImpl(unfoldTransitionRepository) - } - - @Test - fun waitForTransitionFinish_noEvents_doesNotComplete() = - testScope.runTest { - val deferred = async { underTest.waitForTransitionFinish() } - - runCurrent() - - assertThat(deferred.isCompleted).isFalse() - deferred.cancel() - } - - @Test - fun waitForTransitionFinish_finishEvent_completes() = - testScope.runTest { - val deferred = async { underTest.waitForTransitionFinish() } - - runCurrent() - unfoldTransitionProgressProvider.onTransitionFinished() - runCurrent() - - assertThat(deferred.isCompleted).isTrue() - deferred.cancel() - } - - @Test - fun waitForTransitionFinish_otherEvent_doesNotComplete() = - testScope.runTest { - val deferred = async { underTest.waitForTransitionFinish() } - - runCurrent() - unfoldTransitionProgressProvider.onTransitionStarted() - runCurrent() - - assertThat(deferred.isCompleted).isFalse() - deferred.cancel() - } -} diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/MainThreadUnfoldTransitionProgressProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/MainThreadUnfoldTransitionProgressProviderTest.kt index 2bc05fcc8166..e5f619b299a1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/MainThreadUnfoldTransitionProgressProviderTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/progress/MainThreadUnfoldTransitionProgressProviderTest.kt @@ -23,7 +23,7 @@ import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase -import com.android.systemui.unfold.TestUnfoldTransitionProvider +import com.android.systemui.unfold.FakeUnfoldTransitionProvider import com.android.systemui.utils.os.FakeHandler import kotlin.test.Test import kotlinx.coroutines.test.runTest @@ -34,7 +34,7 @@ import org.junit.runner.RunWith @RunWithLooper(setAsMainLooper = true) class MainThreadUnfoldTransitionProgressProviderTest : SysuiTestCase() { - private val wrappedProgressProvider = TestUnfoldTransitionProvider() + private val wrappedProgressProvider = FakeUnfoldTransitionProvider() private val fakeHandler = FakeHandler(Looper.getMainLooper()) private val listener = TestUnfoldProgressListener() diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProviderTest.kt index d864d53fea32..70ec050afe1d 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProviderTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/NaturalRotationUnfoldProgressProviderTest.kt @@ -20,7 +20,7 @@ import android.testing.TestableLooper import android.view.Surface import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase -import com.android.systemui.unfold.TestUnfoldTransitionProvider +import com.android.systemui.unfold.FakeUnfoldTransitionProvider import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener import com.android.systemui.unfold.updates.RotationChangeProvider import com.android.systemui.unfold.updates.RotationChangeProvider.RotationListener @@ -43,14 +43,14 @@ class NaturalRotationUnfoldProgressProviderTest : SysuiTestCase() { @Mock lateinit var rotationChangeProvider: RotationChangeProvider - private val sourceProvider = TestUnfoldTransitionProvider() + private val sourceProvider = FakeUnfoldTransitionProvider() @Mock lateinit var transitionListener: TransitionProgressListener @Captor private lateinit var rotationListenerCaptor: ArgumentCaptor<RotationListener> lateinit var progressProvider: NaturalRotationUnfoldProgressProvider - private lateinit var testableLooper : TestableLooper + private lateinit var testableLooper: TestableLooper @Before fun setUp() { diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScaleAwareUnfoldProgressProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScaleAwareUnfoldProgressProviderTest.kt index 2f29b3bdd3b5..451bd24dd83f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScaleAwareUnfoldProgressProviderTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScaleAwareUnfoldProgressProviderTest.kt @@ -22,7 +22,7 @@ import android.testing.AndroidTestingRunner import android.testing.TestableLooper import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase -import com.android.systemui.unfold.TestUnfoldTransitionProvider +import com.android.systemui.unfold.FakeUnfoldTransitionProvider import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener import com.android.systemui.util.mockito.any import org.junit.Before @@ -42,7 +42,7 @@ class ScaleAwareUnfoldProgressProviderTest : SysuiTestCase() { @Mock lateinit var sinkProvider: TransitionProgressListener - private val sourceProvider = TestUnfoldTransitionProvider() + private val sourceProvider = FakeUnfoldTransitionProvider() private lateinit var contentResolver: ContentResolver private lateinit var progressProvider: ScaleAwareTransitionProgressProvider @@ -132,6 +132,6 @@ class ScaleAwareUnfoldProgressProviderTest : SysuiTestCase() { durationScale.toString() ) - animatorDurationScaleListenerCaptor.value.dispatchChange(/* selfChange= */false) + animatorDurationScaleListenerCaptor.value.dispatchChange(/* selfChange= */ false) } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProviderTest.kt index 95c934e988e7..4486402f43ec 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProviderTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/ScopedUnfoldTransitionProgressProviderTest.kt @@ -23,7 +23,7 @@ import android.testing.AndroidTestingRunner import android.testing.TestableLooper.RunWithLooper import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase -import com.android.systemui.unfold.TestUnfoldTransitionProvider +import com.android.systemui.unfold.FakeUnfoldTransitionProvider import com.android.systemui.unfold.progress.TestUnfoldProgressListener import com.google.common.truth.Truth.assertThat import kotlin.time.Duration.Companion.seconds @@ -43,7 +43,7 @@ import org.junit.runner.RunWith @RunWithLooper class ScopedUnfoldTransitionProgressProviderTest : SysuiTestCase() { - private val rootProvider = TestUnfoldTransitionProvider() + private val rootProvider = FakeUnfoldTransitionProvider() private val listener = TestUnfoldProgressListener() private val testScope = TestScope(UnconfinedTestDispatcher()) private val bgThread = diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/UnfoldOnlyProgressProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/UnfoldOnlyProgressProviderTest.kt index f484ea04bb4f..cd4d7b54916e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/unfold/util/UnfoldOnlyProgressProviderTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/unfold/util/UnfoldOnlyProgressProviderTest.kt @@ -19,7 +19,7 @@ import android.testing.AndroidTestingRunner import android.testing.TestableLooper import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase -import com.android.systemui.unfold.TestUnfoldTransitionProvider +import com.android.systemui.unfold.FakeUnfoldTransitionProvider import com.android.systemui.unfold.progress.TestUnfoldProgressListener import com.google.common.util.concurrent.MoreExecutors import org.junit.Before @@ -32,7 +32,7 @@ import org.junit.runner.RunWith class UnfoldOnlyProgressProviderTest : SysuiTestCase() { private val listener = TestUnfoldProgressListener() - private val sourceProvider = TestUnfoldTransitionProvider() + private val sourceProvider = FakeUnfoldTransitionProvider() private val foldProvider = TestFoldProvider() diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelKosmos.kt index b4f1218617a2..bdd4afa3da7d 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/ui/viewmodel/AlternateBouncerViewModelKosmos.kt @@ -18,7 +18,7 @@ package com.android.systemui.keyguard.ui.viewmodel -import com.android.systemui.keyguard.ui.keyguardTransitionAnimationFlow +import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture import com.android.systemui.statusbar.phone.statusBarKeyguardViewManager @@ -27,6 +27,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi val Kosmos.alternateBouncerViewModel by Fixture { AlternateBouncerViewModel( statusBarKeyguardViewManager = statusBarKeyguardViewManager, - animationFlow = keyguardTransitionAnimationFlow, + keyguardTransitionInteractor = keyguardTransitionInteractor, ) } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModelKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModelKosmos.kt index da2170c85fe7..2f3d3c3e0489 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModelKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/media/controls/ui/viewmodel/MediaControlViewModelKosmos.kt @@ -17,6 +17,7 @@ package com.android.systemui.media.controls.ui.viewmodel import android.content.applicationContext +import com.android.systemui.concurrency.fakeExecutor import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.testDispatcher import com.android.systemui.media.controls.domain.pipeline.interactor.mediaControlInteractor @@ -27,6 +28,7 @@ val Kosmos.mediaControlViewModel by MediaControlViewModel( applicationContext = applicationContext, backgroundDispatcher = testDispatcher, + backgroundExecutor = fakeExecutor, interactor = mediaControlInteractor, logger = mediaUiEventLogger, ) 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 45b28b1d8842..cbba80baee7f 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 @@ -46,6 +46,7 @@ import com.android.systemui.kosmos.applicationCoroutineScope import com.android.systemui.shade.domain.interactor.shadeInteractor import com.android.systemui.statusbar.notification.stack.domain.interactor.notificationStackAppearanceInteractor import com.android.systemui.statusbar.notification.stack.domain.interactor.sharedNotificationContainerInteractor +import com.android.systemui.unfold.domain.interactor.unfoldTransitionInteractor import kotlinx.coroutines.ExperimentalCoroutinesApi @OptIn(ExperimentalCoroutinesApi::class) @@ -81,5 +82,6 @@ val Kosmos.sharedNotificationContainerViewModel by Fixture { primaryBouncerToLockscreenTransitionViewModel = primaryBouncerToLockscreenTransitionViewModel, aodBurnInViewModel = aodBurnInViewModel, + unfoldTransitionInteractor = unfoldTransitionInteractor, ) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/unfold/TestUnfoldTransitionProvider.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/unfold/FakeUnfoldTransitionProvider.kt index 56c624565971..94f0c44a51b2 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/unfold/TestUnfoldTransitionProvider.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/unfold/FakeUnfoldTransitionProvider.kt @@ -2,7 +2,7 @@ package com.android.systemui.unfold import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener -class TestUnfoldTransitionProvider : UnfoldTransitionProgressProvider, TransitionProgressListener { +class FakeUnfoldTransitionProvider : UnfoldTransitionProgressProvider, TransitionProgressListener { private val listeners = mutableListOf<TransitionProgressListener>() diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/unfold/UnfoldTransitionProgressProviderKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/unfold/UnfoldTransitionProgressProviderKosmos.kt index 7c54a5707bf4..a0f5b58c1cd0 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/unfold/UnfoldTransitionProgressProviderKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/unfold/UnfoldTransitionProgressProviderKosmos.kt @@ -18,6 +18,8 @@ package com.android.systemui.unfold import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture -import com.android.systemui.util.mockito.mock -var Kosmos.unfoldTransitionProgressProvider by Fixture { mock<UnfoldTransitionProgressProvider>() } +val Kosmos.fakeUnfoldTransitionProgressProvider by Fixture { FakeUnfoldTransitionProvider() } + +val Kosmos.unfoldTransitionProgressProvider by + Fixture<UnfoldTransitionProgressProvider> { fakeUnfoldTransitionProgressProvider } diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/unfold/domain/interactor/UnfoldTransitionInteractorKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/unfold/domain/interactor/UnfoldTransitionInteractorKosmos.kt index d03616ab98e8..6faa3b480704 100644 --- a/packages/SystemUI/tests/utils/src/com/android/systemui/unfold/domain/interactor/UnfoldTransitionInteractorKosmos.kt +++ b/packages/SystemUI/tests/utils/src/com/android/systemui/unfold/domain/interactor/UnfoldTransitionInteractorKosmos.kt @@ -16,10 +16,14 @@ package com.android.systemui.unfold.domain.interactor +import com.android.systemui.common.ui.domain.interactor.configurationInteractor import com.android.systemui.kosmos.Kosmos import com.android.systemui.kosmos.Kosmos.Fixture import com.android.systemui.unfold.data.repository.unfoldTransitionRepository val Kosmos.unfoldTransitionInteractor by Fixture { - UnfoldTransitionInteractorImpl(repository = unfoldTransitionRepository) + UnfoldTransitionInteractor( + repository = unfoldTransitionRepository, + configurationInteractor = configurationInteractor, + ) } diff --git a/packages/SystemUI/utils/Android.bp b/packages/SystemUI/utils/Android.bp new file mode 100644 index 000000000000..1ef381617c20 --- /dev/null +++ b/packages/SystemUI/utils/Android.bp @@ -0,0 +1,32 @@ +// +// Copyright (C) 2024 The Android Open Source Project +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// + +package { + default_team: "trendy_team_system_ui_please_use_a_more_specific_subteam_if_possible_", + default_applicable_licenses: ["frameworks_base_packages_SystemUI_license"], +} + +java_library { + name: "SystemUI-shared-utils", + srcs: [ + "src/**/*.java", + "src/**/*.kt", + ], + static_libs: [ + "kotlin-stdlib", + "kotlinx_coroutines", + ], +} diff --git a/packages/SystemUI/utils/src/com/android/systemui/utils/coroutines/flow/FlowConflated.kt b/packages/SystemUI/utils/src/com/android/systemui/utils/coroutines/flow/FlowConflated.kt new file mode 100644 index 000000000000..ed97c600adec --- /dev/null +++ b/packages/SystemUI/utils/src/com/android/systemui/utils/coroutines/flow/FlowConflated.kt @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@file:OptIn(ExperimentalTypeInference::class) + +package com.android.systemui.utils.coroutines.flow + +import kotlin.experimental.ExperimentalTypeInference +import kotlinx.coroutines.channels.ProducerScope +import kotlinx.coroutines.channels.SendChannel +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.buffer +import kotlinx.coroutines.flow.callbackFlow +import kotlinx.coroutines.flow.channelFlow +import kotlinx.coroutines.flow.conflate +import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.produceIn +import kotlinx.coroutines.suspendCancellableCoroutine + +/** + * Creates an instance of a _cold_ [Flow] with elements that are sent to a [SendChannel] provided to + * the builder's [block] of code via [ProducerScope]. It allows elements to be produced by code that + * is running in a different context or concurrently. + * + * The resulting flow is _cold_, which means that [block] is called every time a terminal operator + * is applied to the resulting flow. + * + * This builder ensures thread-safety and context preservation, thus the provided [ProducerScope] + * can be used from any context, e.g. from a callback-based API. The resulting flow completes as + * soon as the code in the [block] completes. [awaitClose] should be used to keep the flow running, + * otherwise the channel will be closed immediately when block completes. [awaitClose] argument is + * called either when a flow consumer cancels the flow collection or when a callback-based API + * invokes [SendChannel.close] manually and is typically used to cleanup the resources after the + * completion, e.g. unregister a callback. Using [awaitClose] is mandatory in order to prevent + * memory leaks when the flow collection is cancelled, otherwise the callback may keep running even + * when the flow collector is already completed. To avoid such leaks, this method throws + * [IllegalStateException] if block returns, but the channel is not closed yet. + * + * A [conflated][conflate] channel is used. Use the [buffer] operator on the resulting flow to + * specify a user-defined value and to control what happens when data is produced faster than + * consumed, i.e. to control the back-pressure behavior. + * + * Adjacent applications of [callbackFlow], [flowOn], [buffer], and [produceIn] are always fused so + * that only one properly configured channel is used for execution. + * + * Example of usage that converts a multi-shot callback API to a flow. For single-shot callbacks use + * [suspendCancellableCoroutine]. + * + * ``` + * fun flowFrom(api: CallbackBasedApi): Flow<T> = callbackFlow { + * val callback = object : Callback { // Implementation of some callback interface + * override fun onNextValue(value: T) { + * // To avoid blocking you can configure channel capacity using + * // either buffer(Channel.CONFLATED) or buffer(Channel.UNLIMITED) to avoid overfill + * trySendBlocking(value) + * .onFailure { throwable -> + * // Downstream has been cancelled or failed, can log here + * } + * } + * override fun onApiError(cause: Throwable) { + * cancel(CancellationException("API Error", cause)) + * } + * override fun onCompleted() = channel.close() + * } + * api.register(callback) + * /* + * * Suspends until either 'onCompleted'/'onApiError' from the callback is invoked + * * or flow collector is cancelled (e.g. by 'take(1)' or because a collector's coroutine was cancelled). + * * In both cases, callback will be properly unregistered. + * */ + * awaitClose { api.unregister(callback) } + * } + * ``` + * > The callback `register`/`unregister` methods provided by an external API must be thread-safe, + * > because `awaitClose` block can be called at any time due to asynchronous nature of + * > cancellation, even concurrently with the call of the callback. + * + * This builder is to be preferred over [callbackFlow], due to the latter's default configuration of + * using an internal buffer, negatively impacting system health. + * + * @see callbackFlow + */ +fun <T> conflatedCallbackFlow( + @BuilderInference block: suspend ProducerScope<T>.() -> Unit, +): Flow<T> = callbackFlow(block).conflate() + +/** + * Creates an instance of a _cold_ [Flow] with elements that are sent to a [SendChannel] provided to + * the builder's [block] of code via [ProducerScope]. It allows elements to be produced by code that + * is running in a different context or concurrently. The resulting flow is _cold_, which means that + * [block] is called every time a terminal operator is applied to the resulting flow. + * + * This builder ensures thread-safety and context preservation, thus the provided [ProducerScope] + * can be used concurrently from different contexts. The resulting flow completes as soon as the + * code in the [block] and all its children completes. Use [awaitClose] as the last statement to + * keep it running. A more detailed example is provided in the documentation of [callbackFlow]. + * + * A [conflated][conflate] channel is used. Use the [buffer] operator on the resulting flow to + * specify a user-defined value and to control what happens when data is produced faster than + * consumed, i.e. to control the back-pressure behavior. + * + * Adjacent applications of [channelFlow], [flowOn], [buffer], and [produceIn] are always fused so + * that only one properly configured channel is used for execution. + * + * Examples of usage: + * ``` + * fun <T> Flow<T>.merge(other: Flow<T>): Flow<T> = channelFlow { + * // collect from one coroutine and send it + * launch { + * collect { send(it) } + * } + * // collect and send from this coroutine, too, concurrently + * other.collect { send(it) } + * } + * + * fun <T> contextualFlow(): Flow<T> = channelFlow { + * // send from one coroutine + * launch(Dispatchers.IO) { + * send(computeIoValue()) + * } + * // send from another coroutine, concurrently + * launch(Dispatchers.Default) { + * send(computeCpuValue()) + * } + * } + * ``` + * + * This builder is to be preferred over [channelFlow], due to the latter's default configuration of + * using an internal buffer, negatively impacting system health. + * + * @see channelFlow + */ +fun <T> conflatedChannelFlow( + @BuilderInference block: suspend ProducerScope<T>.() -> Unit, +): Flow<T> = channelFlow(block).conflate() diff --git a/packages/SystemUI/utils/src/com/android/systemui/utils/coroutines/flow/LatestConflated.kt b/packages/SystemUI/utils/src/com/android/systemui/utils/coroutines/flow/LatestConflated.kt new file mode 100644 index 000000000000..5f8c66078483 --- /dev/null +++ b/packages/SystemUI/utils/src/com/android/systemui/utils/coroutines/flow/LatestConflated.kt @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@file:OptIn(ExperimentalCoroutinesApi::class, ExperimentalTypeInference::class) + +package com.android.systemui.utils.coroutines.flow + +import kotlin.experimental.ExperimentalTypeInference +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.FlowCollector +import kotlinx.coroutines.flow.conflate +import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.mapLatest +import kotlinx.coroutines.flow.transformLatest + +/** + * Returns a flow that emits elements from the original flow transformed by [transform] function. + * When the original flow emits a new value, computation of the [transform] block for previous value + * is cancelled. + * + * For example, the following flow: + * ``` + * flow { + * emit("a") + * delay(100) + * emit("b") + * }.mapLatest { value -> + * println("Started computing $value") + * delay(200) + * "Computed $value" + * } + * ``` + * + * will print "Started computing a" and "Started computing b", but the resulting flow will contain + * only "Computed b" value. + * + * This operator is [conflated][conflate] by default, and as such should be preferred over usage of + * [mapLatest], due to the latter's default configuration of using an internal buffer, negatively + * impacting system health. + * + * @see mapLatest + */ +fun <T, R> Flow<T>.mapLatestConflated(@BuilderInference transform: suspend (T) -> R): Flow<R> = + mapLatest(transform).conflate() + +/** + * Returns a flow that switches to a new flow produced by [transform] function every time the + * original flow emits a value. When the original flow emits a new value, the previous flow produced + * by `transform` block is cancelled. + * + * For example, the following flow: + * ``` + * flow { + * emit("a") + * delay(100) + * emit("b") + * }.flatMapLatest { value -> + * flow { + * emit(value) + * delay(200) + * emit(value + "_last") + * } + * } + * ``` + * + * produces `a b b_last` + * + * This operator is [conflated][conflate] by default, and as such should be preferred over usage of + * [flatMapLatest], due to the latter's default configuration of using an internal buffer, + * negatively impacting system health. + * + * @see flatMapLatest + */ +fun <T, R> Flow<T>.flatMapLatestConflated( + @BuilderInference transform: suspend (T) -> Flow<R>, +): Flow<R> = flatMapLatest(transform).conflate() + +/** + * Returns a flow that produces element by [transform] function every time the original flow emits a + * value. When the original flow emits a new value, the previous `transform` block is cancelled, + * thus the name `transformLatest`. + * + * For example, the following flow: + * ``` + * flow { + * emit("a") + * delay(100) + * emit("b") + * }.transformLatest { value -> + * emit(value) + * delay(200) + * emit(value + "_last") + * } + * ``` + * + * produces `a b b_last`. + * + * This operator is [conflated][conflate] by default, and as such should be preferred over usage of + * [transformLatest], due to the latter's default configuration of using an internal buffer, + * negatively impacting system health. + * + * @see transformLatest + */ +fun <T, R> Flow<T>.transformLatestConflated( + @BuilderInference transform: suspend FlowCollector<R>.(T) -> Unit, +): Flow<R> = transformLatest(transform).conflate() diff --git a/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java b/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java index 23e269a67283..cbbce1a74e94 100644 --- a/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java +++ b/packages/WallpaperBackup/src/com/android/wallpaperbackup/WallpaperBackupAgent.java @@ -19,6 +19,7 @@ package com.android.wallpaperbackup; import static android.app.WallpaperManager.FLAG_LOCK; import static android.app.WallpaperManager.FLAG_SYSTEM; import static android.app.WallpaperManager.ORIENTATION_UNKNOWN; +import static android.os.ParcelFileDescriptor.MODE_READ_ONLY; import static com.android.wallpaperbackup.WallpaperEventLogger.ERROR_INELIGIBLE; import static com.android.wallpaperbackup.WallpaperEventLogger.ERROR_NO_METADATA; @@ -39,6 +40,7 @@ import android.content.Context; import android.content.SharedPreferences; import android.content.pm.IPackageManager; import android.content.pm.PackageInfo; +import android.graphics.BitmapFactory; import android.graphics.Point; import android.graphics.Rect; import android.hardware.display.DisplayManager; @@ -109,22 +111,16 @@ public class WallpaperBackupAgent extends BackupAgent { static final String LOCK_WALLPAPER_STAGE = "wallpaper-lock-stage"; @VisibleForTesting static final String WALLPAPER_INFO_STAGE = "wallpaper-info-stage"; - @VisibleForTesting static final String WALLPAPER_BACKUP_DEVICE_INFO_STAGE = "wallpaper-backup-device-info-stage"; - static final String EMPTY_SENTINEL = "empty"; static final String QUOTA_SENTINEL = "quota"; - // Shared preferences constants. static final String PREFS_NAME = "wbprefs.xml"; static final String SYSTEM_GENERATION = "system_gen"; static final String LOCK_GENERATION = "lock_gen"; - /** - * An approximate area threshold to compare device dimension similarity - */ - static final int AREA_THRESHOLD = 50; // TODO (b/327637867): determine appropriate threshold + static final float DEFAULT_ACCEPTABLE_PARALLAX = 0.2f; // If this file exists, it means we exceeded our quota last time private File mQuotaFile; @@ -336,7 +332,6 @@ public class WallpaperBackupAgent extends BackupAgent { mEventLogger.onSystemImageWallpaperBackupFailed(error); } - private void backupLockWallpaperFileIfItExists(SharedPreferences sharedPrefs, boolean lockChanged, int lockGeneration, FullBackupDataOutput data) throws IOException { final File lockImageStage = new File(getFilesDir(), LOCK_WALLPAPER_STAGE); @@ -409,6 +404,16 @@ public class WallpaperBackupAgent extends BackupAgent { } } + private static String readText(TypedXmlPullParser parser) + throws IOException, XmlPullParserException { + String result = ""; + if (parser.next() == XmlPullParser.TEXT) { + result = parser.getText(); + parser.nextTag(); + } + return result; + } + @VisibleForTesting // fullBackupFile is final, so we intercept backups here in tests. protected void backupFile(File file, FullBackupDataOutput data) { @@ -438,18 +443,10 @@ public class WallpaperBackupAgent extends BackupAgent { boolean lockImageStageExists = lockImageStage.exists(); try { - // Parse the device dimensions of the source device and compare with target to - // to identify whether we need to skip the remainder of the restore process + // Parse the device dimensions of the source device Pair<Point, Point> sourceDeviceDimensions = parseDeviceDimensions( deviceDimensionsStage); - Point targetDeviceDimensions = getScreenDimensions(); - if (sourceDeviceDimensions != null && targetDeviceDimensions != null - && isSourceDeviceSignificantlySmallerThanTarget(sourceDeviceDimensions.first, - targetDeviceDimensions)) { - Slog.d(TAG, "The source device is significantly smaller than target"); - } - // First parse the live component name so that we know for logging if we care about // logging errors with the image restore. ComponentName wpService = parseWallpaperComponent(infoStage, "wp"); @@ -466,9 +463,10 @@ public class WallpaperBackupAgent extends BackupAgent { // to back up the original image on the source device, or there was no user-supplied // wallpaper image present. if (lockImageStageExists) { - restoreFromStage(lockImageStage, infoStage, "kwp", FLAG_LOCK); + restoreFromStage(lockImageStage, infoStage, "kwp", FLAG_LOCK, + sourceDeviceDimensions); } - restoreFromStage(imageStage, infoStage, "wp", sysWhich); + restoreFromStage(imageStage, infoStage, "wp", sysWhich, sourceDeviceDimensions); // And reset to the wallpaper service we should be using if (mLockHasLiveComponent) { @@ -543,16 +541,6 @@ public class WallpaperBackupAgent extends BackupAgent { } } - private static String readText(TypedXmlPullParser parser) - throws IOException, XmlPullParserException { - String result = ""; - if (parser.next() == XmlPullParser.TEXT) { - result = parser.getText(); - parser.nextTag(); - } - return result; - } - @VisibleForTesting void updateWallpaperComponent(ComponentName wpService, int which) throws IOException { @@ -578,10 +566,13 @@ public class WallpaperBackupAgent extends BackupAgent { } } - private void restoreFromStage(File stage, File info, String hintTag, int which) + private void restoreFromStage(File stage, File info, String hintTag, int which, + Pair<Point, Point> sourceDeviceDimensions) throws IOException { if (stage.exists()) { if (multiCrop()) { + // TODO(b/332937943): implement offset adjustment by manually adjusting crop to + // adhere to device aspect ratio SparseArray<Rect> cropHints = parseCropHints(info, hintTag); if (cropHints != null) { Slog.i(TAG, "Got restored wallpaper; applying which=" + which @@ -601,7 +592,6 @@ public class WallpaperBackupAgent extends BackupAgent { } return; } - // Parse the restored info file to find the crop hint. Note that this currently // relies on a priori knowledge of the wallpaper info file schema. Rect cropHint = parseCropHint(info, hintTag); @@ -609,8 +599,33 @@ public class WallpaperBackupAgent extends BackupAgent { Slog.i(TAG, "Got restored wallpaper; applying which=" + which + "; cropHint = " + cropHint); try (FileInputStream in = new FileInputStream(stage)) { - mWallpaperManager.setStream(in, cropHint.isEmpty() ? null : cropHint, true, - which); + + if (sourceDeviceDimensions != null && sourceDeviceDimensions.first != null) { + BitmapFactory.Options options = new BitmapFactory.Options(); + options.inJustDecodeBounds = true; + ParcelFileDescriptor pdf = ParcelFileDescriptor.open(stage, MODE_READ_ONLY); + BitmapFactory.decodeFileDescriptor(pdf.getFileDescriptor(), + null, options); + Point bitmapSize = new Point(options.outWidth, options.outHeight); + Point sourceDeviceSize = new Point(sourceDeviceDimensions.first.x, + sourceDeviceDimensions.first.y); + Point targetDeviceDimensions = getScreenDimensions(); + + // TODO: for now we handle only the case where the target device has smaller + // aspect ratio than the source device i.e. the target device is more narrow + // than the source device + if (isTargetMoreNarrowThanSource(targetDeviceDimensions, + sourceDeviceSize)) { + Rect adjustedCrop = findNewCropfromOldCrop(cropHint, + sourceDeviceDimensions.first, true, targetDeviceDimensions, + bitmapSize, true); + + cropHint.set(adjustedCrop); + } + } + + mWallpaperManager.setStream(in, cropHint.isEmpty() ? null : cropHint, + true, which); // And log the success if ((which & FLAG_SYSTEM) > 0) { @@ -629,6 +644,209 @@ public class WallpaperBackupAgent extends BackupAgent { } } + /** + * This method computes the crop of the stored wallpaper to preserve its center point as the + * user had set it in the previous device. + * + * The algorithm involves first computing the original crop of the user (without parallax). Then + * manually adjusting the user's original crop to respect the current device's aspect ratio + * (thereby preserving the center point). Then finally, adding any leftover image real-estate + * (i.e. space left over on the horizontal axis) to add parallax effect. Parallax is only added + * if was present in the old device's settings. + * + */ + private Rect findNewCropfromOldCrop(Rect oldCrop, Point oldDisplaySize, boolean oldRtl, + Point newDisplaySize, Point bitmapSize, boolean newRtl) { + Rect cropWithoutParallax = withoutParallax(oldCrop, oldDisplaySize, oldRtl, bitmapSize); + oldCrop = oldCrop.isEmpty() ? new Rect(0, 0, bitmapSize.x, bitmapSize.y) : oldCrop; + float oldParallaxAmount = ((float) oldCrop.width() / cropWithoutParallax.width()) - 1; + + Rect newCropWithSameCenterWithoutParallax = sameCenter(newDisplaySize, bitmapSize, + cropWithoutParallax); + + Rect newCrop = newCropWithSameCenterWithoutParallax; + + // calculate the amount of left-over space there is in the image after adjusting the crop + // from the above operation i.e. in a rtl configuration, this is the remaining space in the + // image after subtracting the new crop's right edge coordinate from the image itself, and + // for ltr, its just the new crop's left edge coordinate (as it's the distance from the + // beginning of the image) + int widthAvailableForParallaxOnTheNewDevice = + (newRtl) ? newCrop.left : bitmapSize.x - newCrop.right; + + // calculate relatively how much this available space is as a fraction of the total cropped + // image + float availableParallaxAmount = + (float) widthAvailableForParallaxOnTheNewDevice / newCrop.width(); + + float minAcceptableParallax = Math.min(DEFAULT_ACCEPTABLE_PARALLAX, oldParallaxAmount); + + if (DEBUG) { + Slog.d(TAG, "- cropWithoutParallax: " + cropWithoutParallax); + Slog.d(TAG, "- oldParallaxAmount: " + oldParallaxAmount); + Slog.d(TAG, "- newCropWithSameCenterWithoutParallax: " + + newCropWithSameCenterWithoutParallax); + Slog.d(TAG, "- widthAvailableForParallaxOnTheNewDevice: " + + widthAvailableForParallaxOnTheNewDevice); + Slog.d(TAG, "- availableParallaxAmount: " + availableParallaxAmount); + Slog.d(TAG, "- minAcceptableParallax: " + minAcceptableParallax); + Slog.d(TAG, "- oldCrop: " + oldCrop); + Slog.d(TAG, "- oldDisplaySize: " + oldDisplaySize); + Slog.d(TAG, "- oldRtl: " + oldRtl); + Slog.d(TAG, "- newDisplaySize: " + newDisplaySize); + Slog.d(TAG, "- bitmapSize: " + bitmapSize); + Slog.d(TAG, "- newRtl: " + newRtl); + } + if (availableParallaxAmount >= minAcceptableParallax) { + // but in any case, don't put more parallax than the amount of the old device + float parallaxToAdd = Math.min(availableParallaxAmount, oldParallaxAmount); + + int widthToAddForParallax = (int) (newCrop.width() * parallaxToAdd); + if (DEBUG) { + Slog.d(TAG, "- parallaxToAdd: " + parallaxToAdd); + Slog.d(TAG, "- widthToAddForParallax: " + widthToAddForParallax); + } + if (newRtl) { + newCrop.left -= widthToAddForParallax; + } else { + newCrop.right += widthToAddForParallax; + } + } + return newCrop; + } + + /** + * This method computes the original crop of the user without parallax. + * + * NOTE: When the user sets the wallpaper with a specific crop, there may additional image added + * to the crop to support parallax. In order to determine the user's actual crop the parallax + * must be removed if it exists. + */ + Rect withoutParallax(Rect crop, Point displaySize, boolean rtl, Point bitmapSize) { + // in the case an image's crop is not set, we assume the image itself is cropped + if (crop.isEmpty()) { + crop = new Rect(0, 0, bitmapSize.x, bitmapSize.y); + } + + if (DEBUG) { + Slog.w(TAG, "- crop: " + crop); + } + + Rect adjustedCrop = new Rect(crop); + float suggestedDisplayRatio = (float) displaySize.x / displaySize.y; + + // here we calculate the width of the wallpaper image such that it has the same aspect ratio + // as the given display i.e. the width of the image on a single page of the device without + // parallax (i.e. displaySize will correspond to the display the crop was originally set on) + int wallpaperWidthWithoutParallax = (int) (0.5f + (float) displaySize.x * crop.height() + / displaySize.y); + // subtracting wallpaperWidthWithoutParallax from the wallpaper crop gives the amount of + // parallax added + int widthToRemove = Math.max(0, crop.width() - wallpaperWidthWithoutParallax); + + if (DEBUG) { + Slog.d(TAG, "- adjustedCrop: " + adjustedCrop); + Slog.d(TAG, "- suggestedDisplayRatio: " + suggestedDisplayRatio); + Slog.d(TAG, "- wallpaperWidthWithoutParallax: " + wallpaperWidthWithoutParallax); + Slog.d(TAG, "- widthToRemove: " + widthToRemove); + } + if (rtl) { + adjustedCrop.left += widthToRemove; + } else { + adjustedCrop.right -= widthToRemove; + } + + if (DEBUG) { + Slog.d(TAG, "- adjustedCrop: " + crop); + } + return adjustedCrop; + } + + /** + * This method computes a new crop based on the given crop in order to preserve the center point + * of the given crop on the provided displaySize. This is only for the case where the device + * displaySize has a smaller aspect ratio than the cropped image. + * + * NOTE: If the width to height ratio is less in the device display than cropped image + * this means the aspect ratios are off and there will be distortions in the image + * if the image is applied to the current display (i.e. the image will be skewed -> + * pixels in the image will not align correctly with the same pixels in the image that are + * above them) + */ + Rect sameCenter(Point displaySize, Point bitmapSize, Rect crop) { + + // in the case an image's crop is not set, we assume the image itself is cropped + if (crop.isEmpty()) { + crop = new Rect(0, 0, bitmapSize.x, bitmapSize.y); + } + + float screenRatio = (float) displaySize.x / displaySize.y; + float cropRatio = (float) crop.width() / crop.height(); + + Rect adjustedCrop = new Rect(crop); + + if (screenRatio < cropRatio) { + // the screen is more narrow than the image, and as such, the image will need to be + // zoomed in till it fits in the vertical axis. Due to this, we need to manually adjust + // the image's crop in order for it to fit into the screen without having the framework + // do it (since the framework left aligns the image after zooming) + + // Calculate the height of the adjusted wallpaper crop so it respects the aspect ratio + // of the device. To calculate the height, we will use the width of the current crop. + // This is so we find the largest height possible which also respects the device aspect + // ratio. + int heightToAdd = (int) (0.5f + crop.width() / screenRatio - crop.height()); + + // Calculate how much extra image space available that can be used to adjust + // the crop. If this amount is less than heightToAdd, from above, then that means we + // can't use heightToAdd. Instead we will need to use the maximum possible height, which + // is the height of the original bitmap. NOTE: the bitmap height may be different than + // the crop. + // since there is no guarantee to have height available on both sides + // (e.g. the available height might be fully at the bottom), grab the minimum + int availableHeight = 2 * Math.min(crop.top, bitmapSize.y - crop.bottom); + int actualHeightToAdd = Math.min(heightToAdd, availableHeight); + + // half of the additional height is added to the top and bottom of the crop + adjustedCrop.top -= actualHeightToAdd / 2 + actualHeightToAdd % 2; + adjustedCrop.bottom += actualHeightToAdd / 2; + + // Calculate the width of the adjusted crop. Initially we used the fixed width of the + // crop to calculate the heightToAdd, but since this height may be invalid (based on + // the calculation above) we calculate the width again instead of using the fixed width, + // using the adjustedCrop's updated height. + int widthToRemove = (int) (0.5f + crop.width() - adjustedCrop.height() * screenRatio); + + // half of the additional width is subtracted from the left and right side of the crop + int widthToRemoveLeft = widthToRemove / 2; + int widthToRemoveRight = widthToRemove / 2 + widthToRemove % 2; + + adjustedCrop.left += widthToRemoveLeft; + adjustedCrop.right -= widthToRemoveRight; + + if (DEBUG) { + Slog.d(TAG, "cropRatio: " + cropRatio); + Slog.d(TAG, "screenRatio: " + screenRatio); + Slog.d(TAG, "heightToAdd: " + heightToAdd); + Slog.d(TAG, "actualHeightToAdd: " + actualHeightToAdd); + Slog.d(TAG, "availableHeight: " + availableHeight); + Slog.d(TAG, "widthToRemove: " + widthToRemove); + Slog.d(TAG, "adjustedCrop: " + adjustedCrop); + } + + return adjustedCrop; + } + + return adjustedCrop; + } + + private boolean isTargetMoreNarrowThanSource(Point targetDisplaySize, Point srcDisplaySize) { + float targetScreenRatio = (float) targetDisplaySize.x / targetDisplaySize.y; + float srcScreenRatio = (float) srcDisplaySize.x / srcDisplaySize.y; + + return (targetScreenRatio < srcScreenRatio); + } + private void logRestoreErrorIfNoLiveComponent(int which, String error) { if (mSystemHasLiveComponent) { return; @@ -644,6 +862,7 @@ public class WallpaperBackupAgent extends BackupAgent { mEventLogger.onLockImageWallpaperRestoreFailed(error); } } + private Rect parseCropHint(File wallpaperInfo, String sectionTag) { Rect cropHint = new Rect(); try (FileInputStream stream = new FileInputStream(wallpaperInfo)) { @@ -681,7 +900,7 @@ public class WallpaperBackupAgent extends BackupAgent { if (type != XmlPullParser.START_TAG) continue; String tag = parser.getName(); if (!sectionTag.equals(tag)) continue; - for (Pair<Integer, String> pair: List.of( + for (Pair<Integer, String> pair : List.of( new Pair<>(WallpaperManager.PORTRAIT, "Portrait"), new Pair<>(WallpaperManager.LANDSCAPE, "Landscape"), new Pair<>(WallpaperManager.SQUARE_PORTRAIT, "SquarePortrait"), @@ -907,22 +1126,6 @@ public class WallpaperBackupAgent extends BackupAgent { return internalDisplays; } - /** - * This method compares the source and target dimensions, and returns true if there is a - * significant difference in area between them and the source dimensions are smaller than the - * target dimensions. - * - * @param sourceDimensions is the dimensions of the source device - * @param targetDimensions is the dimensions of the target device - */ - @VisibleForTesting - boolean isSourceDeviceSignificantlySmallerThanTarget(Point sourceDimensions, - Point targetDimensions) { - int rawAreaDelta = (targetDimensions.x * targetDimensions.y) - - (sourceDimensions.x * sourceDimensions.y); - return rawAreaDelta > AREA_THRESHOLD; - } - @VisibleForTesting boolean isDeviceInRestore() { try { diff --git a/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java b/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java index ec9223c7d667..3ecdf3f101a5 100644 --- a/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java +++ b/packages/WallpaperBackup/test/src/com/android/wallpaperbackup/WallpaperBackupAgentTest.java @@ -59,7 +59,6 @@ import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; -import android.graphics.Point; import android.graphics.Rect; import android.os.FileUtils; import android.os.ParcelFileDescriptor; @@ -841,26 +840,6 @@ public class WallpaperBackupAgentTest { testParseCropHints(testMap); } - @Test - public void test_sourceDimensionsAreLargerThanTarget() { - // source device is larger than target, expecting to get false - Point sourceDimensions = new Point(2208, 1840); - Point targetDimensions = new Point(1080, 2092); - boolean isSourceSmaller = mWallpaperBackupAgent - .isSourceDeviceSignificantlySmallerThanTarget(sourceDimensions, targetDimensions); - assertThat(isSourceSmaller).isEqualTo(false); - } - - @Test - public void test_sourceDimensionsMuchSmallerThanTarget() { - // source device is smaller than target, expecting to get true - Point sourceDimensions = new Point(1080, 2092); - Point targetDimensions = new Point(2208, 1840); - boolean isSourceSmaller = mWallpaperBackupAgent - .isSourceDeviceSignificantlySmallerThanTarget(sourceDimensions, targetDimensions); - assertThat(isSourceSmaller).isEqualTo(true); - } - private void testParseCropHints(Map<Integer, Rect> testMap) throws Exception { assumeTrue(multiCrop()); mockRestoredStaticWallpaperFile(testMap); diff --git a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java index be2ad21ee020..d279bd553970 100644 --- a/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java +++ b/services/accessibility/java/com/android/server/accessibility/gestures/TouchExplorer.java @@ -852,11 +852,9 @@ public class TouchExplorer extends BaseEventStreamTransformation final int pointerIdBits = (1 << pointerId); if (mSendHoverEnterAndMoveDelayed.isPending()) { // If we have not delivered the enter schedule an exit. - if (Flags.resetHoverEventTimerOnActionUp()) { - // We cancel first to reset the time window so that the user has the full amount of - // time to do a multi tap. - mSendHoverEnterAndMoveDelayed.repost(); - } + // We cancel first to reset the time window so that the user has the full amount of + // time to do a multi tap. + mSendHoverEnterAndMoveDelayed.repost(); mSendHoverExitDelayed.post(event, rawEvent, pointerIdBits, policyFlags); } else { // The user is touch exploring so we send events for end. @@ -1601,7 +1599,7 @@ public class TouchExplorer extends BaseEventStreamTransformation + " pointers down."); return; } - if (Flags.resetHoverEventTimerOnActionUp() && mEvents.size() == 0) { + if (mEvents.size() == 0) { return; } // Send an accessibility event to announce the touch exploration start. diff --git a/services/autofill/bugfixes.aconfig b/services/autofill/bugfixes.aconfig index ced10fbeff0c..70ecc055929c 100644 --- a/services/autofill/bugfixes.aconfig +++ b/services/autofill/bugfixes.aconfig @@ -41,3 +41,10 @@ flag { description: "Use weak reference to address binder leak problem" bug: "307972253" } + +flag { + name: "include_last_focused_id_and_session_id_in_client_state" + namespace: "autofill" + description: "Include the current view id and session id into the FillEventHistory as part of ClientState" + bug: "334141398" +} diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index 3f3ff4a46edf..3a384065217e 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -5188,11 +5188,13 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState String[] exception = resultData.getStringArray( CredentialProviderService.EXTRA_GET_CREDENTIAL_EXCEPTION); if (exception != null && exception.length >= 2) { + String errType = exception[0]; + String errMsg = exception[1]; Slog.w(TAG, "Credman bottom sheet from pinned " - + "entry failed with: + " + exception[0] + " , " - + exception[1]); + + "entry failed with: + " + errType + " , " + + errMsg); sendCredentialManagerResponseToApp(/*response=*/ null, - new GetCredentialException(exception[0], exception[1]), + new GetCredentialException(errType, errMsg), mAutofillId); } } else { 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 cfb7f337242b..af49df69a979 100644 --- a/services/companion/java/com/android/server/companion/devicepresence/DevicePresenceProcessor.java +++ b/services/companion/java/com/android/server/companion/devicepresence/DevicePresenceProcessor.java @@ -56,6 +56,7 @@ import android.util.SparseArray; import android.util.SparseBooleanArray; import com.android.internal.annotations.GuardedBy; +import com.android.internal.util.CollectionUtils; import com.android.server.companion.association.AssociationStore; import java.io.PrintWriter; @@ -1031,6 +1032,9 @@ public class DevicePresenceProcessor implements AssociationStore.OnChangeListene public void sendDevicePresenceEventOnUnlocked(int userId) { final List<DevicePresenceEvent> deviceEvents = getPendingDevicePresenceEventsByUserId( userId); + if (CollectionUtils.isEmpty(deviceEvents)) { + return; + } final List<ObservableUuid> observableUuids = mObservableUuidStore.getObservableUuidsForUser(userId); // Notify and bind the app after the phone is unlocked. @@ -1068,7 +1072,7 @@ public class DevicePresenceProcessor implements AssociationStore.OnChangeListene } } - clearPendingDevicePresenceEventsByUserId(userId); + removePendingDevicePresenceEventsByUserId(userId); } private List<DevicePresenceEvent> getPendingDevicePresenceEventsByUserId(int userId) { @@ -1077,9 +1081,11 @@ public class DevicePresenceProcessor implements AssociationStore.OnChangeListene } } - private void clearPendingDevicePresenceEventsByUserId(int userId) { + private void removePendingDevicePresenceEventsByUserId(int userId) { synchronized (mPendingDevicePresenceEvents) { - mPendingDevicePresenceEvents.get(userId).clear(); + if (mPendingDevicePresenceEvents.contains(userId)) { + mPendingDevicePresenceEvents.remove(userId); + } } } diff --git a/services/core/java/com/android/server/SensitiveContentProtectionManagerService.java b/services/core/java/com/android/server/SensitiveContentProtectionManagerService.java index 4694e9fd44bc..2067ad470743 100644 --- a/services/core/java/com/android/server/SensitiveContentProtectionManagerService.java +++ b/services/core/java/com/android/server/SensitiveContentProtectionManagerService.java @@ -306,6 +306,7 @@ public final class SensitiveContentProtectionManagerService extends SystemServic } } + @GuardedBy("mSensitiveContentProtectionLock") private void updateAppsThatShouldBlockScreenCapture() { RankingMap rankingMap; try { @@ -318,6 +319,7 @@ public final class SensitiveContentProtectionManagerService extends SystemServic updateAppsThatShouldBlockScreenCapture(rankingMap); } + @GuardedBy("mSensitiveContentProtectionLock") private void updateAppsThatShouldBlockScreenCapture(RankingMap rankingMap) { StatusBarNotification[] notifications; try { diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 5ec6b7200191..ee5d49b693ac 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -9189,6 +9189,11 @@ public class ActivityManagerService extends IActivityManager.Stub private class MyBinderProxyCountEventListener implements BinderProxyCountEventListener { @Override public void onLimitReached(int uid) { + // Spawn a new thread for the dump as it'll take long time. + new Thread(() -> handleLimitReached(uid), "BinderProxy Dump: " + uid).start(); + } + + private void handleLimitReached(int uid) { Slog.wtf(TAG, "Uid " + uid + " sent too many Binders to uid " + Process.myUid()); BinderProxy.dumpProxyDebugInfo(); diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java index 0e22ef1eb741..c11fbe19a5d0 100644 --- a/services/core/java/com/android/server/audio/AudioService.java +++ b/services/core/java/com/android/server/audio/AudioService.java @@ -7910,6 +7910,7 @@ public class AudioService extends IAudioService.Stub DEVICE_MEDIA_UNMUTED_ON_PLUG_SET.add(AudioSystem.DEVICE_OUT_WIRED_HEADSET); DEVICE_MEDIA_UNMUTED_ON_PLUG_SET.add(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE); DEVICE_MEDIA_UNMUTED_ON_PLUG_SET.add(AudioSystem.DEVICE_OUT_LINE); + DEVICE_MEDIA_UNMUTED_ON_PLUG_SET.add(AudioSystem.DEVICE_OUT_HEARING_AID); DEVICE_MEDIA_UNMUTED_ON_PLUG_SET.addAll(AudioSystem.DEVICE_OUT_ALL_A2DP_SET); DEVICE_MEDIA_UNMUTED_ON_PLUG_SET.addAll(AudioSystem.DEVICE_OUT_ALL_BLE_SET); DEVICE_MEDIA_UNMUTED_ON_PLUG_SET.addAll(AudioSystem.DEVICE_OUT_ALL_USB_SET); diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java index 1c169a055078..5c9318165358 100644 --- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java +++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java @@ -1766,7 +1766,8 @@ public class DisplayDeviceConfig { loadDensityMapping(config); loadBrightnessDefaultFromDdcXml(config); loadBrightnessConstraintsFromConfigXml(); - if (mFlags.isEvenDimmerEnabled()) { + if (mFlags.isEvenDimmerEnabled() && mContext.getResources().getBoolean( + com.android.internal.R.bool.config_evenDimmerEnabled)) { mEvenDimmerBrightnessData = EvenDimmerBrightnessData.loadConfig(config); } loadBrightnessMap(config); diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java index 8f1277bfe507..4e709a7fd95d 100644 --- a/services/core/java/com/android/server/display/DisplayManagerService.java +++ b/services/core/java/com/android/server/display/DisplayManagerService.java @@ -5271,5 +5271,13 @@ public final class DisplayManagerService extends SystemService { public ExternalDisplayStatsService getExternalDisplayStatsService() { return mExternalDisplayStatsService; } + + /** + * Called on external display is ready to be enabled. + */ + @Override + public void onExternalDisplayReadyToBeEnabled(int displayId) { + mDisplayModeDirector.onExternalDisplayReadyToBeEnabled(displayId); + } } } diff --git a/services/core/java/com/android/server/display/ExternalDisplayPolicy.java b/services/core/java/com/android/server/display/ExternalDisplayPolicy.java index b24caf4ced76..3c2918f833ba 100644 --- a/services/core/java/com/android/server/display/ExternalDisplayPolicy.java +++ b/services/core/java/com/android/server/display/ExternalDisplayPolicy.java @@ -91,6 +91,8 @@ class ExternalDisplayPolicy { @NonNull ExternalDisplayStatsService getExternalDisplayStatsService(); + + void onExternalDisplayReadyToBeEnabled(int displayId); } @NonNull @@ -185,6 +187,10 @@ class ExternalDisplayPolicy { return; } + if (enabled) { + mInjector.onExternalDisplayReadyToBeEnabled(logicalDisplay.getDisplayIdLocked()); + } + mLogicalDisplayMapper.setDisplayEnabledLocked(logicalDisplay, enabled); } @@ -217,6 +223,7 @@ class ExternalDisplayPolicy { if ((Build.IS_ENG || Build.IS_USERDEBUG) && SystemProperties.getBoolean(ENABLE_ON_CONNECT, false)) { Slog.w(TAG, "External display is enabled by default, bypassing user consent."); + mInjector.onExternalDisplayReadyToBeEnabled(logicalDisplay.getDisplayIdLocked()); mInjector.sendExternalDisplayEventLocked(logicalDisplay, EVENT_DISPLAY_CONNECTED); return; } else { diff --git a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java index a862b6e8f8f4..9064763c423a 100644 --- a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java +++ b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java @@ -694,6 +694,13 @@ public class DisplayModeDirector { } /** + * Called when external display is ready to be enabled. + */ + public void onExternalDisplayReadyToBeEnabled(int displayId) { + mDisplayObserver.onExternalDisplayReadyToBeEnabled(displayId); + } + + /** * Listens for changes refresh rate coordination. */ public interface DesiredDisplayModeSpecsListener { @@ -1379,6 +1386,13 @@ public class DisplayModeDirector { } } + + void onExternalDisplayReadyToBeEnabled(int displayId) { + DisplayInfo displayInfo = getDisplayInfo(displayId); + updateDisplaysPeakRefreshRateAndResolution(displayInfo); + addDisplaysSynchronizedPeakRefreshRate(displayInfo); + } + @Override public void onDisplayAdded(int displayId) { updateVrrStatus(displayId); @@ -1386,8 +1400,6 @@ public class DisplayModeDirector { updateDisplayModes(displayId, displayInfo); updateLayoutLimitedFrameRate(displayId, displayInfo); updateUserSettingDisplayPreferredSize(displayInfo); - updateDisplaysPeakRefreshRateAndResolution(displayInfo); - addDisplaysSynchronizedPeakRefreshRate(displayInfo); } @Override diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 6b33199ec230..2583d73046ce 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -284,12 +284,11 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. final Resources mRes; private final Handler mHandler; - /** - * TODO(b/329163064): Remove this field. - */ - @NonNull @MultiUserUnawareField - private InputMethodSettings mSettings; + @UserIdInt + @GuardedBy("ImfLock.class") + private int mCurrentUserId; + @MultiUserUnawareField final SettingsObserver mSettingsObserver; final WindowManagerInternal mWindowManagerInternal; @@ -494,7 +493,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. @GuardedBy("ImfLock.class") @Nullable InputMethodInfo queryInputMethodForCurrentUserLocked(@NonNull String imeId) { - return mSettings.getMethodMap().get(imeId); + return InputMethodSettingsRepository.get(mCurrentUserId).getMethodMap().get(imeId); } /** @@ -815,7 +814,8 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. InputMethodManager.invalidateLocalStylusHandwritingAvailabilityCaches(); } else { boolean enabledChanged = false; - String newEnabled = mSettings.getEnabledInputMethodsStr(); + String newEnabled = InputMethodSettingsRepository.get(mCurrentUserId) + .getEnabledInputMethodsStr(); if (!mLastEnabled.equals(newEnabled)) { mLastEnabled = newEnabled; enabledChanged = true; @@ -847,9 +847,11 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. // sender userId can be a real user ID or USER_ALL. final int senderUserId = pendingResult.getSendingUserId(); if (senderUserId != UserHandle.USER_ALL) { - if (senderUserId != mSettings.getUserId()) { - // A background user is trying to hide the dialog. Ignore. - return; + synchronized (ImfLock.class) { + if (senderUserId != mCurrentUserId) { + // A background user is trying to hide the dialog. Ignore. + return; + } } } mMenuController.hideInputMethodMenu(); @@ -880,9 +882,6 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. AdditionalSubtypeMapRepository.get(userId), DirectBootAwareness.AUTO); InputMethodSettingsRepository.put(userId, settings); - if (userId == mSettings.getUserId()) { - mSettings = settings; - } } postInputMethodSettingUpdatedLocked(true /* resetDefaultEnabledIme */); // If the locale is changed, needs to reset the default ime @@ -944,7 +943,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. @GuardedBy("ImfLock.class") private boolean isChangingPackagesOfCurrentUserLocked() { final int userId = getChangingUserId(); - final boolean retval = userId == mSettings.getUserId(); + final boolean retval = userId == mCurrentUserId; if (DEBUG) { if (!retval) { Slog.d(TAG, "--- ignore this call back from a background user: " + userId); @@ -959,8 +958,10 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. if (!isChangingPackagesOfCurrentUserLocked()) { return false; } - String curInputMethodId = mSettings.getSelectedInputMethod(); - final List<InputMethodInfo> methodList = mSettings.getMethodList(); + final InputMethodSettings settings = + InputMethodSettingsRepository.get(mCurrentUserId); + String curInputMethodId = settings.getSelectedInputMethod(); + final List<InputMethodInfo> methodList = settings.getMethodList(); final int numImes = methodList.size(); if (curInputMethodId != null) { for (int i = 0; i < numImes; i++) { @@ -1077,7 +1078,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. private void onFinishPackageChangesInternal() { synchronized (ImfLock.class) { final int userId = getChangingUserId(); - final boolean isCurrentUser = (userId == mSettings.getUserId()); + final boolean isCurrentUser = (userId == mCurrentUserId); final AdditionalSubtypeMap additionalSubtypeMap = AdditionalSubtypeMapRepository.get(userId); final InputMethodSettings settings = InputMethodSettingsRepository.get(userId); @@ -1135,8 +1136,6 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. if (!isCurrentUser) { return; } - mSettings = queryInputMethodServicesInternal(mContext, userId, - newAdditionalSubtypeMap, DirectBootAwareness.AUTO); postInputMethodSettingUpdatedLocked(false /* resetDefaultEnabledIme */); boolean changed = false; @@ -1291,8 +1290,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. void onUnlockUser(@UserIdInt int userId) { synchronized (ImfLock.class) { if (DEBUG) { - Slog.d(TAG, "onUnlockUser: userId=" + userId + " curUserId=" - + mSettings.getUserId()); + Slog.d(TAG, "onUnlockUser: userId=" + userId + " curUserId=" + mCurrentUserId); } if (!mSystemReady) { return; @@ -1300,8 +1298,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. final InputMethodSettings newSettings = queryInputMethodServicesInternal(mContext, userId, AdditionalSubtypeMapRepository.get(userId), DirectBootAwareness.AUTO); InputMethodSettingsRepository.put(userId, newSettings); - if (mSettings.getUserId() == userId) { - mSettings = newSettings; + if (mCurrentUserId == userId) { // We need to rebuild IMEs. postInputMethodSettingUpdatedLocked(false /* resetDefaultEnabledIme */); updateInputMethodsFromSettingsLocked(true /* enabledChanged */); @@ -1375,16 +1372,16 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. InputMethodSettingsRepository.initialize(mHandler, mContext); AdditionalSubtypeMapRepository.initialize(mHandler, mContext); - final int userId = mActivityManagerInternal.getCurrentUserId(); + mCurrentUserId = mActivityManagerInternal.getCurrentUserId(); - mSettings = InputMethodSettingsRepository.get(userId); + final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId); mSwitchingController = InputMethodSubtypeSwitchingController.createInstanceLocked(context, - mSettings.getMethodMap(), userId); + settings.getMethodMap(), settings.getUserId()); mHardwareKeyboardShortcutController = - new HardwareKeyboardShortcutController(mSettings.getMethodMap(), - mSettings.getUserId()); + new HardwareKeyboardShortcutController(settings.getMethodMap(), + settings.getUserId()); mMenuController = new InputMethodMenuController(this); mBindingController = bindingControllerForTesting != null @@ -1416,7 +1413,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. @GuardedBy("ImfLock.class") @UserIdInt int getCurrentImeUserIdLocked() { - return mSettings.getUserId(); + return mCurrentUserId; } private final class InkWindowInitializer implements Runnable { @@ -1452,12 +1449,13 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. private void resetDefaultImeLocked(Context context) { // Do not reset the default (current) IME when it is a 3rd-party IME String selectedMethodId = getSelectedMethodIdLocked(); + final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId); if (selectedMethodId != null - && !mSettings.getMethodMap().get(selectedMethodId).isSystem()) { + && !settings.getMethodMap().get(selectedMethodId).isSystem()) { return; } final List<InputMethodInfo> suitableImes = InputMethodInfoUtils.getDefaultEnabledImes( - context, mSettings.getEnabledInputMethodList()); + context, settings.getEnabledInputMethodList()); if (suitableImes.isEmpty()) { Slog.i(TAG, "No default found"); return; @@ -1513,7 +1511,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. IInputMethodClientInvoker clientToBeReset) { if (DEBUG) { Slog.d(TAG, "Switching user stage 1/3. newUserId=" + newUserId - + " currentUserId=" + mSettings.getUserId()); + + " currentUserId=" + mCurrentUserId); } maybeInitImeNavbarConfigLocked(newUserId); @@ -1521,8 +1519,9 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. // ContentObserver should be registered again when the user is changed mSettingsObserver.registerContentObserverLocked(newUserId); - mSettings = InputMethodSettings.createEmptyMap(newUserId); - final String defaultImiId = mSettings.getSelectedInputMethod(); + mCurrentUserId = newUserId; + final String defaultImiId = SecureSettingsWrapper.getString( + Settings.Secure.DEFAULT_INPUT_METHOD, null, newUserId); if (DEBUG) { Slog.d(TAG, "Switching user stage 2/3. newUserId=" + newUserId @@ -1540,12 +1539,9 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. // and user switch would not happen at that time. resetCurrentMethodAndClientLocked(UnbindReason.SWITCH_USER); - final InputMethodSettings newSettings = queryInputMethodServicesInternal(mContext, - newUserId, AdditionalSubtypeMapRepository.get(newUserId), DirectBootAwareness.AUTO); - InputMethodSettingsRepository.put(newUserId, newSettings); - mSettings = newSettings; + final InputMethodSettings newSettings = InputMethodSettingsRepository.get(newUserId); postInputMethodSettingUpdatedLocked(initialUserSwitch /* resetDefaultEnabledIme */); - if (TextUtils.isEmpty(mSettings.getSelectedInputMethod())) { + if (TextUtils.isEmpty(newSettings.getSelectedInputMethod())) { // This is the first time of the user switch and // set the current ime to the proper one. resetDefaultImeLocked(mContext); @@ -1555,12 +1551,12 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. if (initialUserSwitch) { InputMethodUtils.setNonSelectedSystemImesDisabledUntilUsed( getPackageManagerForUser(mContext, newUserId), - mSettings.getEnabledInputMethodList()); + newSettings.getEnabledInputMethodList()); } if (DEBUG) { Slog.d(TAG, "Switching user stage 3/3. newUserId=" + newUserId - + " selectedIme=" + mSettings.getSelectedInputMethod()); + + " selectedIme=" + newSettings.getSelectedInputMethod()); } if (mIsInteractive && clientToBeReset != null) { @@ -1583,7 +1579,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. } if (!mSystemReady) { mSystemReady = true; - final int currentUserId = mSettings.getUserId(); + final int currentUserId = mCurrentUserId; mStatusBarManagerInternal = LocalServices.getService(StatusBarManagerInternal.class); hideStatusBarIconLocked(); @@ -1604,7 +1600,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. // the "mImeDrawsImeNavBarResLazyInitFuture" field. synchronized (ImfLock.class) { mImeDrawsImeNavBarResLazyInitFuture = null; - if (currentUserId != mSettings.getUserId()) { + if (currentUserId != mCurrentUserId) { // This means that the current user is already switched to other user // before the background task is executed. In this scenario the relevant // field should already be initialized. @@ -1623,19 +1619,19 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. UserHandle.ALL, broadcastFilterForAllUsers, null, null, Context.RECEIVER_EXPORTED); - final String defaultImiId = mSettings.getSelectedInputMethod(); + final String defaultImiId = SecureSettingsWrapper.getString( + Settings.Secure.DEFAULT_INPUT_METHOD, null, currentUserId); final boolean imeSelectedOnBoot = !TextUtils.isEmpty(defaultImiId); final InputMethodSettings newSettings = queryInputMethodServicesInternal(mContext, currentUserId, AdditionalSubtypeMapRepository.get(currentUserId), DirectBootAwareness.AUTO); InputMethodSettingsRepository.put(currentUserId, newSettings); - mSettings = newSettings; postInputMethodSettingUpdatedLocked( !imeSelectedOnBoot /* resetDefaultEnabledIme */); updateFromSettingsLocked(true); InputMethodUtils.setNonSelectedSystemImesDisabledUntilUsed( getPackageManagerForUser(mContext, currentUserId), - mSettings.getEnabledInputMethodList()); + newSettings.getEnabledInputMethodList()); } } } @@ -1682,7 +1678,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. } synchronized (ImfLock.class) { final int[] resolvedUserIds = InputMethodUtils.resolveUserId(userId, - mSettings.getUserId(), null); + mCurrentUserId, null); if (resolvedUserIds.length != 1) { return Collections.emptyList(); } @@ -1705,7 +1701,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. } synchronized (ImfLock.class) { final int[] resolvedUserIds = InputMethodUtils.resolveUserId(userId, - mSettings.getUserId(), null); + mCurrentUserId, null); if (resolvedUserIds.length != 1) { return Collections.emptyList(); } @@ -1733,7 +1729,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. } // Check if selected IME of current user supports handwriting. - if (userId == mSettings.getUserId()) { + if (userId == mCurrentUserId) { return mBindingController.supportsStylusHandwriting() && (!connectionless || mBindingController.supportsConnectionlessStylusHandwriting()); @@ -1781,15 +1777,8 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. @GuardedBy("ImfLock.class") private List<InputMethodInfo> getEnabledInputMethodListLocked(@UserIdInt int userId, int callingUid) { - final ArrayList<InputMethodInfo> methodList; - final InputMethodSettings settings; - if (userId == mSettings.getUserId()) { - methodList = mSettings.getEnabledInputMethodList(); - settings = mSettings; - } else { - settings = InputMethodSettingsRepository.get(userId); - methodList = settings.getEnabledInputMethodList(); - } + final InputMethodSettings settings = InputMethodSettingsRepository.get(userId); + final ArrayList<InputMethodInfo> methodList = settings.getEnabledInputMethodList(); // filter caller's access to input methods methodList.removeIf(imi -> !canCallerAccessInputMethod(imi.getPackageName(), callingUid, userId, settings)); @@ -2028,7 +2017,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. final boolean restarting = !initial; final Binder startInputToken = new Binder(); - final StartInputInfo info = new StartInputInfo(mSettings.getUserId(), + final StartInputInfo info = new StartInputInfo(mCurrentUserId, getCurTokenLocked(), mCurTokenDisplayId, getCurIdLocked(), startInputReason, restarting, UserHandle.getUserId(mCurClient.mUid), @@ -2042,9 +2031,9 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. // same-user scenarios. // That said ignoring cross-user scenario will never affect IMEs that do not have // INTERACT_ACROSS_USERS(_FULL) permissions, which is actually almost always the case. - if (mSettings.getUserId() == UserHandle.getUserId( + if (mCurrentUserId == UserHandle.getUserId( mCurClient.mUid)) { - mPackageManagerInternal.grantImplicitAccess(mSettings.getUserId(), + mPackageManagerInternal.grantImplicitAccess(mCurrentUserId, null /* intent */, UserHandle.getAppId(getCurMethodUidLocked()), mCurClient.mUid, true /* direct */); } @@ -2067,7 +2056,8 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. } String curId = getCurIdLocked(); - final InputMethodInfo curInputMethodInfo = mSettings.getMethodMap().get(curId); + final InputMethodInfo curInputMethodInfo = InputMethodSettingsRepository.get(mCurrentUserId) + .getMethodMap().get(curId); final boolean suppressesSpellChecker = curInputMethodInfo != null && curInputMethodInfo.suppressesSpellChecker(); final SparseArray<IAccessibilityInputMethodSession> accessibilityInputMethodSessions = @@ -2255,17 +2245,18 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. return currentMethodId; } + final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId); final int oldDeviceId = mDeviceIdToShowIme; mDeviceIdToShowIme = mVdmInternal.getDeviceIdForDisplayId(mDisplayIdToShowIme); if (mDeviceIdToShowIme == DEVICE_ID_DEFAULT) { if (oldDeviceId == DEVICE_ID_DEFAULT) { return currentMethodId; } - final String defaultDeviceMethodId = mSettings.getSelectedDefaultDeviceInputMethod(); + final String defaultDeviceMethodId = settings.getSelectedDefaultDeviceInputMethod(); if (DEBUG) { Slog.v(TAG, "Restoring default device input method: " + defaultDeviceMethodId); } - mSettings.putSelectedDefaultDeviceInputMethod(null); + settings.putSelectedDefaultDeviceInputMethod(null); return defaultDeviceMethodId; } @@ -2273,7 +2264,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. mVirtualDeviceMethodMap.get(mDeviceIdToShowIme, currentMethodId); if (Objects.equals(deviceMethodId, currentMethodId)) { return currentMethodId; - } else if (!mSettings.getMethodMap().containsKey(deviceMethodId)) { + } else if (!settings.getMethodMap().containsKey(deviceMethodId)) { if (DEBUG) { Slog.v(TAG, "Disabling IME on virtual device with id " + mDeviceIdToShowIme + " because its custom input method is not available: " + deviceMethodId); @@ -2285,7 +2276,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. if (DEBUG) { Slog.v(TAG, "Storing default device input method " + currentMethodId); } - mSettings.putSelectedDefaultDeviceInputMethod(currentMethodId); + settings.putSelectedDefaultDeviceInputMethod(currentMethodId); } if (DEBUG) { Slog.v(TAG, "Switching current input method from " + currentMethodId @@ -2315,7 +2306,8 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. if (isSoftInputModeStateVisibleAllowed(unverifiedTargetSdkVersion, startInputFlags)) { return false; } - final InputMethodInfo imi = mSettings.getMethodMap().get(selectedMethodId); + final InputMethodInfo imi = InputMethodSettingsRepository.get(mCurrentUserId) + .getMethodMap().get(selectedMethodId); if (imi == null) { return false; } @@ -2659,7 +2651,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. } else if (packageName != null) { if (DEBUG) Slog.d(TAG, "show a small icon for the input method"); final PackageManager userAwarePackageManager = - getPackageManagerForUser(mContext, mSettings.getUserId()); + getPackageManagerForUser(mContext, mCurrentUserId); ApplicationInfo applicationInfo = null; try { applicationInfo = userAwarePackageManager.getApplicationInfo(packageName, @@ -2721,7 +2713,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. return false; } if (mWindowManagerInternal.isKeyguardShowingAndNotOccluded() - && mWindowManagerInternal.isKeyguardSecure(mSettings.getUserId())) { + && mWindowManagerInternal.isKeyguardSecure(mCurrentUserId)) { return false; } if ((visibility & InputMethodService.IME_ACTIVE) == 0 @@ -2738,7 +2730,8 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. return false; } - List<InputMethodInfo> imes = mSettings.getEnabledInputMethodListWithFilter( + final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId); + List<InputMethodInfo> imes = settings.getEnabledInputMethodListWithFilter( InputMethodInfo::shouldShowInInputMethodPicker); final int numImes = imes.size(); if (numImes > 2) return true; @@ -2750,7 +2743,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. for (int i = 0; i < numImes; ++i) { final InputMethodInfo imi = imes.get(i); final List<InputMethodSubtype> subtypes = - mSettings.getEnabledInputMethodSubtypeList(imi, true); + settings.getEnabledInputMethodSubtypeList(imi, true); final int subtypeCount = subtypes.size(); if (subtypeCount == 0) { ++nonAuxCount; @@ -2902,11 +2895,12 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. @GuardedBy("ImfLock.class") void updateInputMethodsFromSettingsLocked(boolean enabledMayChange) { + final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId); if (enabledMayChange) { final PackageManager userAwarePackageManager = getPackageManagerForUser(mContext, - mSettings.getUserId()); + settings.getUserId()); - List<InputMethodInfo> enabled = mSettings.getEnabledInputMethodList(); + List<InputMethodInfo> enabled = settings.getEnabledInputMethodList(); for (int i = 0; i < enabled.size(); i++) { // We allow the user to select "disabled until used" apps, so if they // are enabling one of those here we now need to make it enabled. @@ -2933,20 +2927,20 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. if (mDeviceIdToShowIme == DEVICE_ID_DEFAULT) { String ime = SecureSettingsWrapper.getString( - Settings.Secure.DEFAULT_INPUT_METHOD, null, mSettings.getUserId()); + Settings.Secure.DEFAULT_INPUT_METHOD, null, settings.getUserId()); String defaultDeviceIme = SecureSettingsWrapper.getString( - Settings.Secure.DEFAULT_DEVICE_INPUT_METHOD, null, mSettings.getUserId()); + Settings.Secure.DEFAULT_DEVICE_INPUT_METHOD, null, settings.getUserId()); if (defaultDeviceIme != null && !Objects.equals(ime, defaultDeviceIme)) { if (DEBUG) { Slog.v(TAG, "Current input method " + ime + " differs from the stored default" - + " device input method for user " + mSettings.getUserId() + + " device input method for user " + settings.getUserId() + " - restoring " + defaultDeviceIme); } SecureSettingsWrapper.putString( Settings.Secure.DEFAULT_INPUT_METHOD, defaultDeviceIme, - mSettings.getUserId()); + settings.getUserId()); SecureSettingsWrapper.putString( - Settings.Secure.DEFAULT_DEVICE_INPUT_METHOD, null, mSettings.getUserId()); + Settings.Secure.DEFAULT_DEVICE_INPUT_METHOD, null, settings.getUserId()); } } @@ -2954,14 +2948,14 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. // ENABLED_INPUT_METHODS is taking care of keeping them correctly in // sync, so we will never have a DEFAULT_INPUT_METHOD that is not // enabled. - String id = mSettings.getSelectedInputMethod(); + String id = settings.getSelectedInputMethod(); // There is no input method selected, try to choose new applicable input method. if (TextUtils.isEmpty(id) && chooseNewDefaultIMELocked()) { - id = mSettings.getSelectedInputMethod(); + id = settings.getSelectedInputMethod(); } if (!TextUtils.isEmpty(id)) { try { - setInputMethodLocked(id, mSettings.getSelectedInputMethodSubtypeId(id)); + setInputMethodLocked(id, settings.getSelectedInputMethodSubtypeId(id)); } catch (IllegalArgumentException e) { Slog.w(TAG, "Unknown input method from prefs: " + id, e); resetCurrentMethodAndClientLocked(UnbindReason.SWITCH_IME_FAILED); @@ -2972,18 +2966,18 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. } // TODO: Instantiate mSwitchingController for each user. - if (mSettings.getUserId() == mSwitchingController.getUserId()) { - mSwitchingController.resetCircularListLocked(mSettings.getMethodMap()); + if (settings.getUserId() == mSwitchingController.getUserId()) { + mSwitchingController.resetCircularListLocked(settings.getMethodMap()); } else { mSwitchingController = InputMethodSubtypeSwitchingController.createInstanceLocked( - mContext, mSettings.getMethodMap(), mSettings.getUserId()); + mContext, settings.getMethodMap(), settings.getUserId()); } // TODO: Instantiate mHardwareKeyboardShortcutController for each user. - if (mSettings.getUserId() == mHardwareKeyboardShortcutController.getUserId()) { - mHardwareKeyboardShortcutController.reset(mSettings.getMethodMap()); + if (settings.getUserId() == mHardwareKeyboardShortcutController.getUserId()) { + mHardwareKeyboardShortcutController.reset(settings.getMethodMap()); } else { mHardwareKeyboardShortcutController = new HardwareKeyboardShortcutController( - mSettings.getMethodMap(), mSettings.getUserId()); + settings.getMethodMap(), settings.getUserId()); } sendOnNavButtonFlagsChangedLocked(); } @@ -3007,14 +3001,15 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. @GuardedBy("ImfLock.class") void setInputMethodLocked(String id, int subtypeId, int deviceId) { - InputMethodInfo info = mSettings.getMethodMap().get(id); + final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId); + InputMethodInfo info = settings.getMethodMap().get(id); if (info == null) { throw getExceptionForUnknownImeId(id); } // See if we need to notify a subtype change within the same IME. if (id.equals(getSelectedMethodIdLocked())) { - final int userId = mSettings.getUserId(); + final int userId = settings.getUserId(); final int subtypeCount = info.getSubtypeCount(); if (subtypeCount <= 0) { notifyInputMethodSubtypeChangedLocked(userId, info, null); @@ -3055,7 +3050,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. // method is a custom one specific to a virtual device. So only update the settings // entry used to restore the default device input method once we want to show the IME // back on the default device. - mSettings.putSelectedDefaultDeviceInputMethod(id); + settings.putSelectedDefaultDeviceInputMethod(id); return; } IInputMethodInvoker curMethod = getCurMethodLocked(); @@ -3583,7 +3578,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. return InputBindResult.USER_SWITCHING; } final int[] profileIdsWithDisabled = mUserManagerInternal.getProfileIds( - mSettings.getUserId(), false /* enabledOnly */); + mCurrentUserId, false /* enabledOnly */); for (int profileId : profileIdsWithDisabled) { if (profileId == userId) { scheduleSwitchUserTaskLocked(userId, cs.mClient); @@ -3629,10 +3624,10 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. } // Verify if caller is a background user. - final int currentUserId = mSettings.getUserId(); - if (userId != currentUserId) { + if (userId != mCurrentUserId) { if (ArrayUtils.contains( - mUserManagerInternal.getProfileIds(currentUserId, false), userId)) { + mUserManagerInternal.getProfileIds(mCurrentUserId, false), + userId)) { // cross-profile access is always allowed here to allow // profile-switching. scheduleSwitchUserTaskLocked(userId, cs.mClient); @@ -3821,7 +3816,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. && mImeBindingState.mFocusedWindowClient.mClient.asBinder() == client.asBinder()) { return true; } - if (mSettings.getUserId() != UserHandle.getUserId(uid)) { + if (mCurrentUserId != UserHandle.getUserId(uid)) { return false; } if (getCurIntentLocked() != null && InputMethodUtils.checkIfPackageBelongsToUid( @@ -3885,9 +3880,10 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. if (!calledWithValidTokenLocked(token)) { return; } - final InputMethodInfo imi = mSettings.getMethodMap().get(id); + final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId); + final InputMethodInfo imi = settings.getMethodMap().get(id); if (imi == null || !canCallerAccessInputMethod( - imi.getPackageName(), callingUid, userId, mSettings)) { + imi.getPackageName(), callingUid, userId, settings)) { throw getExceptionForUnknownImeId(id); } setInputMethodWithSubtypeIdLocked(token, id, NOT_A_SUBTYPE_ID); @@ -3903,9 +3899,10 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. if (!calledWithValidTokenLocked(token)) { return; } - final InputMethodInfo imi = mSettings.getMethodMap().get(id); + final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId); + final InputMethodInfo imi = settings.getMethodMap().get(id); if (imi == null || !canCallerAccessInputMethod( - imi.getPackageName(), callingUid, userId, mSettings)) { + imi.getPackageName(), callingUid, userId, settings)) { throw getExceptionForUnknownImeId(id); } if (subtype != null) { @@ -3923,10 +3920,11 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. if (!calledWithValidTokenLocked(token)) { return false; } - final Pair<String, String> lastIme = mSettings.getLastInputMethodAndSubtype(); + final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId); + final Pair<String, String> lastIme = settings.getLastInputMethodAndSubtype(); final InputMethodInfo lastImi; if (lastIme != null) { - lastImi = mSettings.getMethodMap().get(lastIme.first); + lastImi = settings.getMethodMap().get(lastIme.first); } else { lastImi = null; } @@ -3950,7 +3948,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. // This is a safety net. If the currentSubtype can't be added to the history // and the framework couldn't find the last ime, we will make the last ime be // the most applicable enabled keyboard subtype of the system imes. - final List<InputMethodInfo> enabled = mSettings.getEnabledInputMethodList(); + final List<InputMethodInfo> enabled = settings.getEnabledInputMethodList(); if (enabled != null) { final int enabledCount = enabled.size(); final String locale; @@ -3958,7 +3956,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. && !TextUtils.isEmpty(mCurrentSubtype.getLocale())) { locale = mCurrentSubtype.getLocale(); } else { - locale = SystemLocaleWrapper.get(mSettings.getUserId()).get(0).toString(); + locale = SystemLocaleWrapper.get(mCurrentUserId).get(0).toString(); } for (int i = 0; i < enabledCount; ++i) { final InputMethodInfo imi = enabled.get(i); @@ -4005,8 +4003,9 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. @GuardedBy("ImfLock.class") private boolean switchToNextInputMethodLocked(@Nullable IBinder token, boolean onlyCurrentIme) { + final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId); final ImeSubtypeListItem nextSubtype = mSwitchingController.getNextInputMethodLocked( - onlyCurrentIme, mSettings.getMethodMap().get(getSelectedMethodIdLocked()), + onlyCurrentIme, settings.getMethodMap().get(getSelectedMethodIdLocked()), mCurrentSubtype); if (nextSubtype == null) { return false; @@ -4022,9 +4021,10 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. if (!calledWithValidTokenLocked(token)) { return false; } + final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId); final ImeSubtypeListItem nextSubtype = mSwitchingController.getNextInputMethodLocked( false /* onlyCurrentIme */, - mSettings.getMethodMap().get(getSelectedMethodIdLocked()), mCurrentSubtype); + settings.getMethodMap().get(getSelectedMethodIdLocked()), mCurrentSubtype); return nextSubtype != null; } } @@ -4036,10 +4036,6 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. Manifest.permission.INTERACT_ACROSS_USERS_FULL, null); } synchronized (ImfLock.class) { - if (mSettings.getUserId() == userId) { - return mSettings.getLastInputMethodSubtype(); - } - return InputMethodSettingsRepository.get(userId).getLastInputMethodSubtype(); } } @@ -4071,7 +4067,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. } final var additionalSubtypeMap = AdditionalSubtypeMapRepository.get(userId); - final boolean isCurrentUser = (mSettings.getUserId() == userId); + final boolean isCurrentUser = (mCurrentUserId == userId); final InputMethodSettings settings = InputMethodSettingsRepository.get(userId); final var newAdditionalSubtypeMap = settings.getNewAdditionalSubtypeMap( imiId, toBeAdded, additionalSubtypeMap, mPackageManagerInternal, callingUid); @@ -4085,7 +4081,6 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. if (isCurrentUser) { final long ident = Binder.clearCallingIdentity(); try { - mSettings = newSettings; postInputMethodSettingUpdatedLocked(false /* resetDefaultEnabledIme */); } finally { Binder.restoreCallingIdentity(ident); @@ -4115,7 +4110,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. final long ident = Binder.clearCallingIdentity(); try { synchronized (ImfLock.class) { - final boolean currentUser = (mSettings.getUserId() == userId); + final boolean currentUser = (mCurrentUserId == userId); final InputMethodSettings settings = InputMethodSettingsRepository.get(userId); if (!settings.setEnabledInputMethodSubtypes(imeId, subtypeHashCodes)) { return; @@ -4458,11 +4453,11 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. } return; } - if (mSettings.getUserId() != mSwitchingController.getUserId()) { + if (mCurrentUserId != mSwitchingController.getUserId()) { return; } - final InputMethodInfo imi = - mSettings.getMethodMap().get(getSelectedMethodIdLocked()); + final InputMethodInfo imi = InputMethodSettingsRepository.get(mCurrentUserId) + .getMethodMap().get(getSelectedMethodIdLocked()); if (imi != null) { mSwitchingController.onUserActionLocked(imi, mCurrentSubtype); } @@ -4522,8 +4517,9 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. return; } else { // Called with current IME's token. - if (mSettings.getMethodMap().get(id) != null - && mSettings.getEnabledInputMethodListWithFilter( + final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId); + if (settings.getMethodMap().get(id) != null + && settings.getEnabledInputMethodListWithFilter( (info) -> info.getId().equals(id)).isEmpty()) { throw new IllegalStateException("Requested IME is not enabled: " + id); } @@ -4702,21 +4698,23 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. return false; } synchronized (ImfLock.class) { + final InputMethodSettings settings = + InputMethodSettingsRepository.get(mCurrentUserId); final boolean isScreenLocked = mWindowManagerInternal.isKeyguardLocked() - && mWindowManagerInternal.isKeyguardSecure(mSettings.getUserId()); - final String lastInputMethodId = mSettings.getSelectedInputMethod(); + && mWindowManagerInternal.isKeyguardSecure(settings.getUserId()); + final String lastInputMethodId = settings.getSelectedInputMethod(); int lastInputMethodSubtypeId = - mSettings.getSelectedInputMethodSubtypeId(lastInputMethodId); + settings.getSelectedInputMethodSubtypeId(lastInputMethodId); final List<ImeSubtypeListItem> imList = InputMethodSubtypeSwitchingController .getSortedInputMethodAndSubtypeList( showAuxSubtypes, isScreenLocked, true /* forImeMenu */, - mContext, mSettings.getMethodMap(), mSettings.getUserId()); + mContext, settings.getMethodMap(), settings.getUserId()); if (imList.isEmpty()) { Slog.w(TAG, "Show switching menu failed, imList is empty," + " showAuxSubtypes: " + showAuxSubtypes + " isScreenLocked: " + isScreenLocked - + " userId: " + mSettings.getUserId()); + + " userId: " + settings.getUserId()); return false; } @@ -4902,8 +4900,9 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. @GuardedBy("ImfLock.class") private boolean chooseNewDefaultIMELocked() { + final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId); final InputMethodInfo imi = InputMethodInfoUtils.getMostApplicableDefaultIME( - mSettings.getEnabledInputMethodList()); + settings.getEnabledInputMethodList()); if (imi != null) { if (DEBUG) { Slog.d(TAG, "New default IME was selected: " + imi.getId()); @@ -5017,6 +5016,8 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. mMethodMapUpdateCount++; mMyPackageMonitor.clearKnownImePackageNamesLocked(); + final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId); + // Construct the set of possible IME packages for onPackageChanged() to avoid false // negatives when the package state remains to be the same but only the component state is // changed. @@ -5027,7 +5028,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. final List<ResolveInfo> allInputMethodServices = mContext.getPackageManager().queryIntentServicesAsUser( new Intent(InputMethod.SERVICE_INTERFACE), - PackageManager.MATCH_DISABLED_COMPONENTS, mSettings.getUserId()); + PackageManager.MATCH_DISABLED_COMPONENTS, settings.getUserId()); final int numImes = allInputMethodServices.size(); for (int i = 0; i < numImes; ++i) { final ServiceInfo si = allInputMethodServices.get(i).serviceInfo; @@ -5042,11 +5043,11 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. if (!resetDefaultEnabledIme) { boolean enabledImeFound = false; boolean enabledNonAuxImeFound = false; - final List<InputMethodInfo> enabledImes = mSettings.getEnabledInputMethodList(); + final List<InputMethodInfo> enabledImes = settings.getEnabledInputMethodList(); final int numImes = enabledImes.size(); for (int i = 0; i < numImes; ++i) { final InputMethodInfo imi = enabledImes.get(i); - if (mSettings.getMethodMap().containsKey(imi.getId())) { + if (settings.getMethodMap().containsKey(imi.getId())) { enabledImeFound = true; if (!imi.isAuxiliaryIme()) { enabledNonAuxImeFound = true; @@ -5070,7 +5071,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. if (resetDefaultEnabledIme || reenableMinimumNonAuxSystemImes) { final ArrayList<InputMethodInfo> defaultEnabledIme = - InputMethodInfoUtils.getDefaultEnabledImes(mContext, mSettings.getMethodList(), + InputMethodInfoUtils.getDefaultEnabledImes(mContext, settings.getMethodList(), reenableMinimumNonAuxSystemImes); final int numImes = defaultEnabledIme.size(); for (int i = 0; i < numImes; ++i) { @@ -5082,9 +5083,9 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. } } - final String defaultImiId = mSettings.getSelectedInputMethod(); + final String defaultImiId = settings.getSelectedInputMethod(); if (!TextUtils.isEmpty(defaultImiId)) { - if (!mSettings.getMethodMap().containsKey(defaultImiId)) { + if (!settings.getMethodMap().containsKey(defaultImiId)) { Slog.w(TAG, "Default IME is uninstalled. Choose new default IME."); if (chooseNewDefaultIMELocked()) { updateInputMethodsFromSettingsLocked(true); @@ -5098,26 +5099,26 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. updateDefaultVoiceImeIfNeededLocked(); // TODO: Instantiate mSwitchingController for each user. - if (mSettings.getUserId() == mSwitchingController.getUserId()) { - mSwitchingController.resetCircularListLocked(mSettings.getMethodMap()); + if (settings.getUserId() == mSwitchingController.getUserId()) { + mSwitchingController.resetCircularListLocked(settings.getMethodMap()); } else { mSwitchingController = InputMethodSubtypeSwitchingController.createInstanceLocked( - mContext, mSettings.getMethodMap(), mSettings.getUserId()); + mContext, settings.getMethodMap(), mCurrentUserId); } // TODO: Instantiate mHardwareKeyboardShortcutController for each user. - if (mSettings.getUserId() == mHardwareKeyboardShortcutController.getUserId()) { - mHardwareKeyboardShortcutController.reset(mSettings.getMethodMap()); + if (settings.getUserId() == mHardwareKeyboardShortcutController.getUserId()) { + mHardwareKeyboardShortcutController.reset(settings.getMethodMap()); } else { mHardwareKeyboardShortcutController = new HardwareKeyboardShortcutController( - mSettings.getMethodMap(), mSettings.getUserId()); + settings.getMethodMap(), settings.getUserId()); } sendOnNavButtonFlagsChangedLocked(); // Notify InputMethodListListeners of the new installed InputMethods. - final List<InputMethodInfo> inputMethodList = mSettings.getMethodList(); + final List<InputMethodInfo> inputMethodList = settings.getMethodList(); mHandler.obtainMessage(MSG_DISPATCH_ON_INPUT_METHOD_LIST_UPDATED, - mSettings.getUserId(), 0 /* unused */, inputMethodList).sendToTarget(); + settings.getUserId(), 0 /* unused */, inputMethodList).sendToTarget(); } @GuardedBy("ImfLock.class") @@ -5132,11 +5133,12 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. @GuardedBy("ImfLock.class") private void updateDefaultVoiceImeIfNeededLocked() { + final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId); final String systemSpeechRecognizer = mContext.getString(com.android.internal.R.string.config_systemSpeechRecognizer); - final String currentDefaultVoiceImeId = mSettings.getDefaultVoiceInputMethod(); + final String currentDefaultVoiceImeId = settings.getDefaultVoiceInputMethod(); final InputMethodInfo newSystemVoiceIme = InputMethodInfoUtils.chooseSystemVoiceIme( - mSettings.getMethodMap(), systemSpeechRecognizer, currentDefaultVoiceImeId); + settings.getMethodMap(), systemSpeechRecognizer, currentDefaultVoiceImeId); if (newSystemVoiceIme == null) { if (DEBUG) { Slog.i(TAG, "Found no valid default Voice IME. If the user is still locked," @@ -5145,7 +5147,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. // Clear DEFAULT_VOICE_INPUT_METHOD when necessary. Note that InputMethodSettings // does not update the actual Secure Settings until the user is unlocked. if (!TextUtils.isEmpty(currentDefaultVoiceImeId)) { - mSettings.putDefaultVoiceInputMethod(""); + settings.putDefaultVoiceInputMethod(""); // We don't support disabling the voice ime when a package is removed from the // config. } @@ -5158,7 +5160,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. Slog.i(TAG, "Enabling the default Voice IME:" + newSystemVoiceIme); } setInputMethodEnabledLocked(newSystemVoiceIme.getId(), true); - mSettings.putDefaultVoiceInputMethod(newSystemVoiceIme.getId()); + settings.putDefaultVoiceInputMethod(newSystemVoiceIme.getId()); } // ---------------------------------------------------------------------- @@ -5173,8 +5175,9 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. */ @GuardedBy("ImfLock.class") private boolean setInputMethodEnabledLocked(String id, boolean enabled) { + final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId); if (enabled) { - final String enabledImeIdsStr = mSettings.getEnabledInputMethodsStr(); + final String enabledImeIdsStr = settings.getEnabledInputMethodsStr(); final String newEnabledImeIdsStr = InputMethodUtils.concatEnabledImeIds( enabledImeIdsStr, id); if (TextUtils.equals(enabledImeIdsStr, newEnabledImeIdsStr)) { @@ -5182,29 +5185,29 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. // Nothing to do. The previous state was enabled. return true; } - mSettings.putEnabledInputMethodsStr(newEnabledImeIdsStr); + settings.putEnabledInputMethodsStr(newEnabledImeIdsStr); // Previous state was disabled. return false; } else { - final List<Pair<String, ArrayList<String>>> enabledInputMethodsList = mSettings + final List<Pair<String, ArrayList<String>>> enabledInputMethodsList = settings .getEnabledInputMethodsAndSubtypeList(); StringBuilder builder = new StringBuilder(); - if (mSettings.buildAndPutEnabledInputMethodsStrRemovingId( + if (settings.buildAndPutEnabledInputMethodsStrRemovingId( builder, enabledInputMethodsList, id)) { if (mDeviceIdToShowIme == DEVICE_ID_DEFAULT) { // Disabled input method is currently selected, switch to another one. - final String selId = mSettings.getSelectedInputMethod(); + final String selId = settings.getSelectedInputMethod(); if (id.equals(selId) && !chooseNewDefaultIMELocked()) { Slog.i(TAG, "Can't find new IME, unsetting the current input method."); resetSelectedInputMethodAndSubtypeLocked(""); } - } else if (id.equals(mSettings.getSelectedDefaultDeviceInputMethod())) { + } else if (id.equals(settings.getSelectedDefaultDeviceInputMethod())) { // Disabled default device IME while using a virtual device one, choose a // new default one but only update the settings. InputMethodInfo newDefaultIme = InputMethodInfoUtils.getMostApplicableDefaultIME( - mSettings.getEnabledInputMethodList()); - mSettings.putSelectedDefaultDeviceInputMethod( + settings.getEnabledInputMethodList()); + settings.putSelectedDefaultDeviceInputMethod( newDefaultIme == null ? null : newDefaultIme.getId()); } // Previous state was enabled. @@ -5220,29 +5223,30 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. @GuardedBy("ImfLock.class") private void setSelectedInputMethodAndSubtypeLocked(InputMethodInfo imi, int subtypeId, boolean setSubtypeOnly) { - mSettings.saveCurrentInputMethodAndSubtypeToHistory(getSelectedMethodIdLocked(), + final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId); + settings.saveCurrentInputMethodAndSubtypeToHistory(getSelectedMethodIdLocked(), mCurrentSubtype); // Set Subtype here if (imi == null || subtypeId < 0) { - mSettings.putSelectedSubtype(NOT_A_SUBTYPE_ID); + settings.putSelectedSubtype(NOT_A_SUBTYPE_ID); mCurrentSubtype = null; } else { if (subtypeId < imi.getSubtypeCount()) { InputMethodSubtype subtype = imi.getSubtypeAt(subtypeId); - mSettings.putSelectedSubtype(subtype.hashCode()); + settings.putSelectedSubtype(subtype.hashCode()); mCurrentSubtype = subtype; } else { - mSettings.putSelectedSubtype(NOT_A_SUBTYPE_ID); + settings.putSelectedSubtype(NOT_A_SUBTYPE_ID); // If the subtype is not specified, choose the most applicable one mCurrentSubtype = getCurrentInputMethodSubtypeLocked(); } } - notifyInputMethodSubtypeChangedLocked(mSettings.getUserId(), imi, mCurrentSubtype); + notifyInputMethodSubtypeChangedLocked(settings.getUserId(), imi, mCurrentSubtype); if (!setSubtypeOnly) { // Set InputMethod here - mSettings.putSelectedInputMethod(imi != null ? imi.getId() : ""); + settings.putSelectedInputMethod(imi != null ? imi.getId() : ""); } } @@ -5250,13 +5254,15 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. private void resetSelectedInputMethodAndSubtypeLocked(String newDefaultIme) { mDeviceIdToShowIme = DEVICE_ID_DEFAULT; mDisplayIdToShowIme = INVALID_DISPLAY; - mSettings.putSelectedDefaultDeviceInputMethod(null); - InputMethodInfo imi = mSettings.getMethodMap().get(newDefaultIme); + final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId); + settings.putSelectedDefaultDeviceInputMethod(null); + + InputMethodInfo imi = settings.getMethodMap().get(newDefaultIme); int lastSubtypeId = NOT_A_SUBTYPE_ID; // newDefaultIme is empty when there is no candidate for the selected IME. if (imi != null && !TextUtils.isEmpty(newDefaultIme)) { - String subtypeHashCode = mSettings.getLastSubtypeForInputMethod(newDefaultIme); + String subtypeHashCode = settings.getLastSubtypeForInputMethod(newDefaultIme); if (subtypeHashCode != null) { try { lastSubtypeId = SubtypeUtils.getSubtypeIdFromHashCode(imi, @@ -5283,7 +5289,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. Manifest.permission.INTERACT_ACROSS_USERS_FULL, null); } synchronized (ImfLock.class) { - if (mSettings.getUserId() == userId) { + if (mCurrentUserId == userId) { return getCurrentInputMethodSubtypeLocked(); } @@ -5308,26 +5314,27 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. if (selectedMethodId == null) { return null; } - final boolean subtypeIsSelected = mSettings.isSubtypeSelected(); - final InputMethodInfo imi = mSettings.getMethodMap().get(selectedMethodId); + final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId); + final boolean subtypeIsSelected = settings.isSubtypeSelected(); + 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 = mSettings.getSelectedInputMethodSubtypeId(selectedMethodId); + 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 = - mSettings.getEnabledInputMethodSubtypeList(imi, true); + 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(mSettings.getUserId()) + final String locale = SystemLocaleWrapper.get(settings.getUserId()) .get(0).toString(); mCurrentSubtype = SubtypeUtils.findLastResortApplicableSubtype( explicitlyOrImplicitlyEnabledSubtypes, @@ -5356,16 +5363,16 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. @GuardedBy("ImfLock.class") private boolean switchToInputMethodLocked(String imeId, @UserIdInt int userId) { - if (userId == mSettings.getUserId()) { - if (!mSettings.getMethodMap().containsKey(imeId) - || !mSettings.getEnabledInputMethodList() - .contains(mSettings.getMethodMap().get(imeId))) { + final InputMethodSettings settings = InputMethodSettingsRepository.get(userId); + if (userId == mCurrentUserId) { + if (!settings.getMethodMap().containsKey(imeId) + || !settings.getEnabledInputMethodList() + .contains(settings.getMethodMap().get(imeId))) { return false; // IME is not found or not enabled. } setInputMethodLocked(imeId, NOT_A_SUBTYPE_ID); return true; } - final InputMethodSettings settings = InputMethodSettingsRepository.get(userId); if (!settings.getMethodMap().containsKey(imeId) || !settings.getEnabledInputMethodList().contains( settings.getMethodMap().get(imeId))) { @@ -5406,8 +5413,9 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. @GuardedBy("ImfLock.class") private void switchKeyboardLayoutLocked(int direction) { - final InputMethodInfo currentImi = mSettings.getMethodMap().get( - getSelectedMethodIdLocked()); + final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId); + + final InputMethodInfo currentImi = settings.getMethodMap().get(getSelectedMethodIdLocked()); if (currentImi == null) { return; } @@ -5419,7 +5427,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. if (nextSubtypeHandle == null) { return; } - final InputMethodInfo nextImi = mSettings.getMethodMap().get(nextSubtypeHandle.getImeId()); + final InputMethodInfo nextImi = settings.getMethodMap().get(nextSubtypeHandle.getImeId()); if (nextImi == null) { return; } @@ -5498,17 +5506,14 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. @Override public boolean setInputMethodEnabled(String imeId, boolean enabled, @UserIdInt int userId) { synchronized (ImfLock.class) { - if (userId == mSettings.getUserId()) { - if (!mSettings.getMethodMap().containsKey(imeId)) { - return false; // IME is not found. - } - setInputMethodEnabledLocked(imeId, enabled); - return true; - } final InputMethodSettings settings = InputMethodSettingsRepository.get(userId); if (!settings.getMethodMap().containsKey(imeId)) { return false; // IME is not found. } + if (userId == mCurrentUserId) { + setInputMethodEnabledLocked(imeId, enabled); + return true; + } if (enabled) { final String enabledImeIdsStr = settings.getEnabledInputMethodsStr(); final String newEnabledImeIdsStr = InputMethodUtils.concatEnabledImeIds( @@ -5835,8 +5840,9 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. final Printer p = new PrintWriterPrinter(pw); synchronized (ImfLock.class) { + final InputMethodSettings settings = InputMethodSettingsRepository.get(mCurrentUserId); p.println("Current Input Method Manager state:"); - final List<InputMethodInfo> methodList = mSettings.getMethodList(); + final List<InputMethodInfo> methodList = settings.getMethodList(); int numImes = methodList.size(); p.println(" Input Methods: mMethodMapUpdateCount=" + mMethodMapUpdateCount); for (int i = 0; i < numImes; i++) { @@ -5860,6 +5866,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. p.println(" curSession=" + c.mCurSession); }; mClientController.forAllClients(clientControllerDump); + p.println(" mCurrentUserId=" + mCurrentUserId); p.println(" mCurMethodId=" + getSelectedMethodIdLocked()); client = mCurClient; p.println(" mCurClient=" + client + " mCurSeq=" + getSequenceNumberLocked()); @@ -5885,8 +5892,6 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. ? Arrays.toString(mStylusIds.toArray()) : "")); p.println(" mSwitchingController:"); mSwitchingController.dump(p); - p.println(" mSettings:"); - mSettings.dump(p, " "); p.println(" mStartInputHistory:"); mStartInputHistory.dump(pw, " "); @@ -6141,7 +6146,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. } synchronized (ImfLock.class) { final int[] userIds = InputMethodUtils.resolveUserId(userIdToBeResolved, - mSettings.getUserId(), shellCommand.getErrPrintWriter()); + mCurrentUserId, shellCommand.getErrPrintWriter()); try (PrintWriter pr = shellCommand.getOutPrintWriter()) { for (int userId : userIds) { final List<InputMethodInfo> methods = all @@ -6186,7 +6191,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. PrintWriter error = shellCommand.getErrPrintWriter()) { synchronized (ImfLock.class) { final int[] userIds = InputMethodUtils.resolveUserId(userIdToBeResolved, - mSettings.getUserId(), shellCommand.getErrPrintWriter()); + mCurrentUserId, shellCommand.getErrPrintWriter()); for (int userId : userIds) { if (!userHasDebugPriv(userId, shellCommand)) { continue; @@ -6245,14 +6250,14 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. PrintWriter error) { boolean failedToEnableUnknownIme = false; boolean previouslyEnabled = false; - if (userId == mSettings.getUserId()) { - if (enabled && !mSettings.getMethodMap().containsKey(imeId)) { + final InputMethodSettings settings = InputMethodSettingsRepository.get(userId); + if (userId == mCurrentUserId) { + if (enabled && !settings.getMethodMap().containsKey(imeId)) { failedToEnableUnknownIme = true; } else { previouslyEnabled = setInputMethodEnabledLocked(imeId, enabled); } } else { - final InputMethodSettings settings = InputMethodSettingsRepository.get(userId); if (enabled) { if (!settings.getMethodMap().containsKey(imeId)) { failedToEnableUnknownIme = true; @@ -6307,7 +6312,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. PrintWriter error = shellCommand.getErrPrintWriter()) { synchronized (ImfLock.class) { final int[] userIds = InputMethodUtils.resolveUserId(userIdToBeResolved, - mSettings.getUserId(), shellCommand.getErrPrintWriter()); + mCurrentUserId, shellCommand.getErrPrintWriter()); for (int userId : userIds) { if (!userHasDebugPriv(userId, shellCommand)) { continue; @@ -6347,7 +6352,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. synchronized (ImfLock.class) { try (PrintWriter out = shellCommand.getOutPrintWriter()) { final int[] userIds = InputMethodUtils.resolveUserId(userIdToBeResolved, - mSettings.getUserId(), shellCommand.getErrPrintWriter()); + mCurrentUserId, shellCommand.getErrPrintWriter()); for (int userId : userIds) { if (!userHasDebugPriv(userId, shellCommand)) { continue; @@ -6359,15 +6364,16 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. } final String nextIme; final List<InputMethodInfo> nextEnabledImes; - if (userId == mSettings.getUserId()) { + final InputMethodSettings settings = InputMethodSettingsRepository.get(userId); + if (userId == mCurrentUserId) { hideCurrentInputLocked(mImeBindingState.mFocusedWindow, 0 /* flags */, SoftInputShowHideReason.HIDE_RESET_SHELL_COMMAND); mBindingController.unbindCurrentMethod(); // Enable default IMEs, disable others - var toDisable = mSettings.getEnabledInputMethodList(); + var toDisable = settings.getEnabledInputMethodList(); var defaultEnabled = InputMethodInfoUtils.getDefaultEnabledImes( - mContext, mSettings.getMethodList()); + mContext, settings.getMethodList()); toDisable.removeAll(defaultEnabled); for (InputMethodInfo info : toDisable) { setInputMethodEnabledLocked(info.getId(), false); @@ -6381,14 +6387,11 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl. } updateInputMethodsFromSettingsLocked(true /* enabledMayChange */); InputMethodUtils.setNonSelectedSystemImesDisabledUntilUsed( - getPackageManagerForUser(mContext, mSettings.getUserId()), - mSettings.getEnabledInputMethodList()); - nextIme = mSettings.getSelectedInputMethod(); - nextEnabledImes = mSettings.getEnabledInputMethodList(); + getPackageManagerForUser(mContext, settings.getUserId()), + settings.getEnabledInputMethodList()); + nextIme = settings.getSelectedInputMethod(); + nextEnabledImes = settings.getEnabledInputMethodList(); } else { - final InputMethodSettings settings = - InputMethodSettingsRepository.get(userId); - nextEnabledImes = InputMethodInfoUtils.getDefaultEnabledImes(mContext, settings.getMethodList()); nextIme = InputMethodInfoUtils.getMostApplicableDefaultIME( diff --git a/services/core/java/com/android/server/notification/NotificationChannelExtractor.java b/services/core/java/com/android/server/notification/NotificationChannelExtractor.java index bd73cb6544f0..1938642ef396 100644 --- a/services/core/java/com/android/server/notification/NotificationChannelExtractor.java +++ b/services/core/java/com/android/server/notification/NotificationChannelExtractor.java @@ -23,9 +23,15 @@ import static android.media.AudioAttributes.USAGE_NOTIFICATION; import android.app.Notification; import android.app.NotificationChannel; +import android.compat.annotation.ChangeId; +import android.compat.annotation.LoggingOnly; import android.content.Context; import android.media.AudioAttributes; +import android.os.Binder; +import android.os.RemoteException; +import android.os.ServiceManager; import android.util.Slog; +import com.android.internal.compat.IPlatformCompat; /** * Stores the latest notification channel information for this notification @@ -34,14 +40,26 @@ public class NotificationChannelExtractor implements NotificationSignalExtractor private static final String TAG = "ChannelExtractor"; private static final boolean DBG = false; + /** + * Corrects audio attributes for notifications based on characteristics of the notifications. + */ + @ChangeId + @LoggingOnly + static final long RESTRICT_AUDIO_ATTRIBUTES = 331793339L; + private RankingConfig mConfig; private Context mContext; + private IPlatformCompat mPlatformCompat; public void initialize(Context ctx, NotificationUsageStats usageStats) { mContext = ctx; if (DBG) Slog.d(TAG, "Initializing " + getClass().getSimpleName() + "."); } + public void setCompatChangeLogger(IPlatformCompat platformCompat) { + mPlatformCompat = platformCompat; + } + public RankingReconsideration process(NotificationRecord record) { if (record == null || record.getNotification() == null) { if (DBG) Slog.d(TAG, "skipping empty notification"); @@ -80,6 +98,7 @@ public class NotificationChannelExtractor implements NotificationSignalExtractor } if (updateAttributes) { + reportAudioAttributesChanged(record.getUid()); NotificationChannel clone = record.getChannel().copy(); clone.setSound(clone.getSound(), new AudioAttributes.Builder(attributes) .setUsage(USAGE_NOTIFICATION) @@ -91,6 +110,17 @@ public class NotificationChannelExtractor implements NotificationSignalExtractor return null; } + private void reportAudioAttributesChanged(int uid) { + final long id = Binder.clearCallingIdentity(); + try { + mPlatformCompat.reportChangeByUid(RESTRICT_AUDIO_ATTRIBUTES, uid); + } catch (RemoteException e) { + Slog.e(TAG, "Unexpected exception while reporting to changecompat", e); + } finally { + Binder.restoreCallingIdentity(id); + } + } + @Override public void setConfig(RankingConfig config) { mConfig = config; diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 8075ae0e4d61..b48cad2406e3 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -220,6 +220,7 @@ import android.content.IntentFilter; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.pm.LauncherApps; +import android.content.pm.ModuleInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.PackageManagerInternal; @@ -675,6 +676,10 @@ public class NotificationManagerService extends SystemService { private static final int DB_VERSION = 1; + + private static final String ADSERVICES_MODULE_PKG_NAME = + "com.android.adservices"; + private static final String TAG_NOTIFICATION_POLICY = "notification-policy"; private static final String ATTR_VERSION = "version"; @@ -736,6 +741,8 @@ public class NotificationManagerService extends SystemService { private AppOpsManager.OnOpChangedListener mAppOpsListener; + private ModuleInfo mAdservicesModuleInfo; + static class Archive { final SparseArray<Boolean> mEnabled; final int mBufferSize; @@ -2508,12 +2515,8 @@ public class NotificationManagerService extends SystemService { mAppOps, mUserProfiles, mShowReviewPermissionsNotification); - mRankingHelper = new RankingHelper(getContext(), - mRankingHandler, - mPreferencesHelper, - mZenModeHelper, - mUsageStats, - extractorNames); + mRankingHelper = new RankingHelper(getContext(), mRankingHandler, mPreferencesHelper, + mZenModeHelper, mUsageStats, extractorNames, mPlatformCompat); mSnoozeHelper = snoozeHelper; mGroupHelper = groupHelper; mHistoryManager = historyManager; @@ -2938,6 +2941,15 @@ public class NotificationManagerService extends SystemService { mZenModeHelper.setDeviceEffectsApplier( new DefaultDeviceEffectsApplier(getContext())); } + List<ModuleInfo> moduleInfoList = + mPackageManagerClient.getInstalledModules( + PackageManager.MATCH_DEBUG_TRIAGED_MISSING); + // Cache adservices module info + for (ModuleInfo mi : moduleInfoList) { + if (Objects.equals(mi.getApexModuleName(), ADSERVICES_MODULE_PKG_NAME)) { + mAdservicesModuleInfo = mi; + } + } } else if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) { mSnoozeHelper.scheduleRepostsForPersistedNotifications(System.currentTimeMillis()); } else if (phase == SystemService.PHASE_DEVICE_SPECIFIC_SERVICES_READY) { @@ -7687,13 +7699,27 @@ public class NotificationManagerService extends SystemService { private boolean canBeNonDismissible(ApplicationInfo ai, Notification notification) { return notification.isMediaNotification() || isEnterpriseExempted(ai) || notification.isStyle(Notification.CallStyle.class) - || isDefaultSearchSelectorPackage(ai.packageName); + || isDefaultSearchSelectorPackage(ai.packageName) + || isDefaultAdservicesPackage(ai.packageName); } private boolean isDefaultSearchSelectorPackage(String pkg) { return Objects.equals(mDefaultSearchSelectorPkg, pkg); } + private boolean isDefaultAdservicesPackage(String pkg) { + if (mAdservicesModuleInfo == null) { + return false; + } + // Handles the special package structure for mainline modules + for (String apkName : mAdservicesModuleInfo.getApkInApexPackageNames()) { + if (Objects.equals(apkName, pkg)) { + return true; + } + } + return false; + } + private boolean isEnterpriseExempted(ApplicationInfo ai) { // Check if the app is an organization admin app // TODO(b/234609037): Replace with new DPM APIs to check if organization admin diff --git a/services/core/java/com/android/server/notification/NotificationSignalExtractor.java b/services/core/java/com/android/server/notification/NotificationSignalExtractor.java index 24c1d5966020..f0358d1e1d8c 100644 --- a/services/core/java/com/android/server/notification/NotificationSignalExtractor.java +++ b/services/core/java/com/android/server/notification/NotificationSignalExtractor.java @@ -17,6 +17,7 @@ package com.android.server.notification; import android.content.Context; +import com.android.internal.compat.IPlatformCompat; /** * Extracts signals that will be useful to the {@link NotificationComparator} and caches them @@ -52,4 +53,6 @@ public interface NotificationSignalExtractor { * DND. */ void setZenHelper(ZenModeHelper helper); + + default void setCompatChangeLogger(IPlatformCompat platformCompat){}; } diff --git a/services/core/java/com/android/server/notification/PreferencesHelper.java b/services/core/java/com/android/server/notification/PreferencesHelper.java index 50ca984dcf57..461bd9c0663b 100644 --- a/services/core/java/com/android/server/notification/PreferencesHelper.java +++ b/services/core/java/com/android/server/notification/PreferencesHelper.java @@ -1387,8 +1387,7 @@ public class PreferencesHelper implements RankingConfig { public void updateFixedImportance(List<UserInfo> users) { for (UserInfo user : users) { List<PackageInfo> packages = mPm.getInstalledPackagesAsUser( - PackageManager.PackageInfoFlags.of(PackageManager.MATCH_SYSTEM_ONLY), - user.getUserHandle().getIdentifier()); + 0, user.getUserHandle().getIdentifier()); for (PackageInfo pi : packages) { boolean fixed = mPermissionHelper.isPermissionFixed( pi.packageName, user.getUserHandle().getIdentifier()); diff --git a/services/core/java/com/android/server/notification/RankingHelper.java b/services/core/java/com/android/server/notification/RankingHelper.java index 68e0eaaf31cd..77568015fe79 100644 --- a/services/core/java/com/android/server/notification/RankingHelper.java +++ b/services/core/java/com/android/server/notification/RankingHelper.java @@ -15,6 +15,9 @@ */ package com.android.server.notification; +import static android.app.Flags.restrictAudioAttributesAlarm; +import static android.app.Flags.restrictAudioAttributesCall; +import static android.app.Flags.restrictAudioAttributesMedia; import static android.app.Flags.sortSectionByTime; import static android.app.NotificationManager.IMPORTANCE_MIN; import static android.text.TextUtils.formatSimple; @@ -27,6 +30,7 @@ import android.util.ArrayMap; import android.util.Slog; import android.util.proto.ProtoOutputStream; +import com.android.internal.compat.IPlatformCompat; import com.android.tools.r8.keepanno.annotations.KeepItemKind; import com.android.tools.r8.keepanno.annotations.KeepTarget; import com.android.tools.r8.keepanno.annotations.UsesReflection; @@ -56,7 +60,8 @@ public class RankingHelper { methodName = "<init>") }) public RankingHelper(Context context, RankingHandler rankingHandler, RankingConfig config, - ZenModeHelper zenHelper, NotificationUsageStats usageStats, String[] extractorNames) { + ZenModeHelper zenHelper, NotificationUsageStats usageStats, String[] extractorNames, + IPlatformCompat platformCompat) { mContext = context; mRankingHandler = rankingHandler; if (sortSectionByTime()) { @@ -75,6 +80,10 @@ public class RankingHelper { extractor.initialize(mContext, usageStats); extractor.setConfig(config); extractor.setZenHelper(zenHelper); + if (restrictAudioAttributesAlarm() || restrictAudioAttributesMedia() + || restrictAudioAttributesCall()) { + extractor.setCompatChangeLogger(platformCompat); + } mSignalExtractors[i] = extractor; } catch (ClassNotFoundException e) { Slog.w(TAG, "Couldn't find extractor " + extractorNames[i] + ".", e); diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java index 4c653f6ce95f..fe9c3f217841 100644 --- a/services/core/java/com/android/server/pm/ShortcutService.java +++ b/services/core/java/com/android/server/pm/ShortcutService.java @@ -50,6 +50,7 @@ import android.content.pm.ComponentInfo; import android.content.pm.IPackageManager; import android.content.pm.IShortcutService; import android.content.pm.LauncherApps; +import android.content.pm.LauncherApps.ShortcutChangeCallback; import android.content.pm.LauncherApps.ShortcutQuery; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; @@ -151,6 +152,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Objects; +import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; import java.util.function.Consumer; @@ -320,12 +322,11 @@ public class ShortcutService extends IShortcutService.Stub { private final Handler mHandler; - @GuardedBy("mLock") - private final ArrayList<ShortcutChangeListener> mListeners = new ArrayList<>(1); + private final CopyOnWriteArrayList<ShortcutChangeListener> mListeners = + new CopyOnWriteArrayList<>(); - @GuardedBy("mLock") - private final ArrayList<LauncherApps.ShortcutChangeCallback> mShortcutChangeCallbacks = - new ArrayList<>(1); + private final CopyOnWriteArrayList<ShortcutChangeCallback> mShortcutChangeCallbacks = + new CopyOnWriteArrayList<>(); private final AtomicLong mRawLastResetTime = new AtomicLong(0); @@ -1841,18 +1842,11 @@ public class ShortcutService extends IShortcutService.Stub { @UserIdInt final int userId) { return () -> { try { - final ArrayList<ShortcutChangeListener> copy; - synchronized (mLock) { - if (!isUserUnlockedL(userId)) { - return; - } - - copy = new ArrayList<>(mListeners); + if (!isUserUnlockedL(userId)) { + return; } // Note onShortcutChanged() needs to be called with the system service permissions. - for (int i = copy.size() - 1; i >= 0; i--) { - copy.get(i).onShortcutChanged(packageName, userId); - } + mListeners.forEach(listener -> listener.onShortcutChanged(packageName, userId)); } catch (Exception ignore) { } }; @@ -1867,22 +1861,17 @@ public class ShortcutService extends IShortcutService.Stub { final UserHandle user = UserHandle.of(userId); injectPostToHandler(() -> { try { - final ArrayList<LauncherApps.ShortcutChangeCallback> copy; - synchronized (mLock) { - if (!isUserUnlockedL(userId)) { - return; - } - - copy = new ArrayList<>(mShortcutChangeCallbacks); + if (!isUserUnlockedL(userId)) { + return; } - for (int i = copy.size() - 1; i >= 0; i--) { + mShortcutChangeCallbacks.forEach(callback -> { if (!CollectionUtils.isEmpty(changedList)) { - copy.get(i).onShortcutsAddedOrUpdated(packageName, changedList, user); + callback.onShortcutsAddedOrUpdated(packageName, changedList, user); } if (!CollectionUtils.isEmpty(removedList)) { - copy.get(i).onShortcutsRemoved(packageName, removedList, user); + callback.onShortcutsRemoved(packageName, removedList, user); } - } + }); } catch (Exception ignore) { } }); @@ -3425,17 +3414,13 @@ public class ShortcutService extends IShortcutService.Stub { @Override public void addListener(@NonNull ShortcutChangeListener listener) { - synchronized (mLock) { - mListeners.add(Objects.requireNonNull(listener)); - } + mListeners.add(Objects.requireNonNull(listener)); } @Override public void addShortcutChangeCallback( @NonNull LauncherApps.ShortcutChangeCallback callback) { - synchronized (mLock) { - mShortcutChangeCallbacks.add(Objects.requireNonNull(callback)); - } + mShortcutChangeCallbacks.add(Objects.requireNonNull(callback)); } @Override diff --git a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java index d060c7ca3034..54cb9c9a9a9b 100644 --- a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java +++ b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java @@ -4885,7 +4885,6 @@ public class BatteryStatsImpl extends BatteryStats { if (type == WAKE_TYPE_PARTIAL) { // Only care about partial wake locks, since full wake locks // will be canceled when the user puts the screen to sleep. - aggregateLastWakeupUptimeLocked(elapsedRealtimeMs, uptimeMs); if (historyName == null) { historyName = name; } @@ -5205,20 +5204,14 @@ public class BatteryStatsImpl extends BatteryStats { } @GuardedBy("this") - void aggregateLastWakeupUptimeLocked(long elapsedRealtimeMs, long uptimeMs) { + public void noteWakeupReasonLocked(String reason, long elapsedRealtimeMs, long uptimeMs) { if (mLastWakeupReason != null) { long deltaUptimeMs = uptimeMs - mLastWakeupUptimeMs; SamplingTimer timer = getWakeupReasonTimerLocked(mLastWakeupReason); timer.add(deltaUptimeMs * 1000, 1, elapsedRealtimeMs); // time in in microseconds mFrameworkStatsLogger.kernelWakeupReported(deltaUptimeMs * 1000, mLastWakeupReason, mLastWakeupElapsedTimeMs); - mLastWakeupReason = null; } - } - - @GuardedBy("this") - public void noteWakeupReasonLocked(String reason, long elapsedRealtimeMs, long uptimeMs) { - aggregateLastWakeupUptimeLocked(elapsedRealtimeMs, uptimeMs); mHistory.recordWakeupEvent(elapsedRealtimeMs, uptimeMs, reason); mLastWakeupReason = reason; mLastWakeupUptimeMs = uptimeMs; diff --git a/services/core/java/com/android/server/security/FileIntegrityService.java b/services/core/java/com/android/server/security/FileIntegrityService.java index bb4876bd5f56..5b501e16d652 100644 --- a/services/core/java/com/android/server/security/FileIntegrityService.java +++ b/services/core/java/com/android/server/security/FileIntegrityService.java @@ -170,6 +170,10 @@ public class FileIntegrityService extends SystemService { @Override public int setupFsverity(android.os.IInstalld.IFsveritySetupAuthToken authToken, String filePath, String packageName) throws RemoteException { + getContext().enforceCallingPermission(android.Manifest.permission.SETUP_FSVERITY, + "Permission android.permission.SETUP_FSVERITY not grantted to access " + + "FileIntegrityManager#setupFsverity"); + Objects.requireNonNull(authToken); Objects.requireNonNull(filePath); Objects.requireNonNull(packageName); diff --git a/services/core/java/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java b/services/core/java/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java index c5d333317013..36192537493a 100644 --- a/services/core/java/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java +++ b/services/core/java/com/android/server/vcn/routeselection/IpSecPacketLossDetector.java @@ -64,12 +64,19 @@ public class IpSecPacketLossDetector extends NetworkMetricMonitor { private static final int PACKET_LOSS_PERCENT_UNAVAILABLE = -1; + // Ignore the packet loss detection result if the expected packet number is smaller than 10. + // Solarwinds NPM uses 10 ICMP echos to calculate packet loss rate (as per + // https://thwack.solarwinds.com/products/network-performance-monitor-npm/f/forum/63829/how-is-packet-loss-calculated) + @VisibleForTesting(visibility = Visibility.PRIVATE) + static final int MIN_VALID_EXPECTED_RX_PACKET_NUM = 10; + @Retention(RetentionPolicy.SOURCE) @IntDef( prefix = {"PACKET_LOSS_"}, value = { PACKET_LOSS_RATE_VALID, PACKET_LOSS_RATE_INVALID, + PACKET_LOSS_UNUSUAL_SEQ_NUM_LEAP, }) @Target({ElementType.TYPE_USE}) private @interface PacketLossResultType {} @@ -84,11 +91,23 @@ public class IpSecPacketLossDetector extends NetworkMetricMonitor { * <ul> * <li>The replay window did not proceed and thus all packets might have been delivered out of * order + * <li>The expected received packet number is too small and thus the detection result is not + * reliable * <li>There are unexpected errors * </ul> */ private static final int PACKET_LOSS_RATE_INVALID = 1; + /** + * The sequence number increase is unusually large and might be caused an intentional leap on + * the server's downlink + * + * <p>Inbound sequence number will not always increase consecutively. During load balancing the + * server might add a big leap on the sequence number intentionally. In such case a high packet + * loss rate does not always indicate a lossy network + */ + private static final int PACKET_LOSS_UNUSUAL_SEQ_NUM_LEAP = 2; + // For VoIP, losses between 5% and 10% of the total packet stream will affect the quality // significantly (as per "Computer Networking for LANS to WANS: Hardware, Software and // Security"). For audio and video streaming, above 10-12% packet loss is unacceptable (as per @@ -98,8 +117,12 @@ public class IpSecPacketLossDetector extends NetworkMetricMonitor { private static final int POLL_IPSEC_STATE_INTERVAL_SECONDS_DEFAULT = 20; + // By default, there's no maximum limit enforced + private static final int MAX_SEQ_NUM_INCREASE_DEFAULT_DISABLED = -1; + private long mPollIpSecStateIntervalMs; - private final int mPacketLossRatePercentThreshold; + private int mPacketLossRatePercentThreshold; + private int mMaxSeqNumIncreasePerSecond; @NonNull private final Handler mHandler; @NonNull private final PowerManager mPowerManager; @@ -138,6 +161,7 @@ public class IpSecPacketLossDetector extends NetworkMetricMonitor { mPollIpSecStateIntervalMs = getPollIpSecStateIntervalMs(carrierConfig); mPacketLossRatePercentThreshold = getPacketLossRatePercentThreshold(carrierConfig); + mMaxSeqNumIncreasePerSecond = getMaxSeqNumIncreasePerSecond(carrierConfig); // Register for system broadcasts to monitor idle mode change final IntentFilter intentFilter = new IntentFilter(); @@ -202,6 +226,24 @@ public class IpSecPacketLossDetector extends NetworkMetricMonitor { return IPSEC_PACKET_LOSS_PERCENT_THRESHOLD_DEFAULT; } + @VisibleForTesting(visibility = Visibility.PRIVATE) + static int getMaxSeqNumIncreasePerSecond(@Nullable PersistableBundleWrapper carrierConfig) { + int maxSeqNumIncrease = MAX_SEQ_NUM_INCREASE_DEFAULT_DISABLED; + if (Flags.handleSeqNumLeap() && carrierConfig != null) { + maxSeqNumIncrease = + carrierConfig.getInt( + VcnManager.VCN_NETWORK_SELECTION_MAX_SEQ_NUM_INCREASE_PER_SECOND_KEY, + MAX_SEQ_NUM_INCREASE_DEFAULT_DISABLED); + } + + if (maxSeqNumIncrease < MAX_SEQ_NUM_INCREASE_DEFAULT_DISABLED) { + logE(TAG, "Invalid value of MAX_SEQ_NUM_INCREASE_PER_SECOND_KEY " + maxSeqNumIncrease); + return MAX_SEQ_NUM_INCREASE_DEFAULT_DISABLED; + } + + return maxSeqNumIncrease; + } + @Override protected void onSelectedUnderlyingNetworkChanged() { if (!isSelectedUnderlyingNetwork()) { @@ -237,6 +279,11 @@ public class IpSecPacketLossDetector extends NetworkMetricMonitor { // The already scheduled event will not be affected. The followup events will be scheduled // with the new interval mPollIpSecStateIntervalMs = getPollIpSecStateIntervalMs(carrierConfig); + + if (Flags.handleSeqNumLeap()) { + mPacketLossRatePercentThreshold = getPacketLossRatePercentThreshold(carrierConfig); + mMaxSeqNumIncreasePerSecond = getMaxSeqNumIncreasePerSecond(carrierConfig); + } } @Override @@ -339,7 +386,10 @@ public class IpSecPacketLossDetector extends NetworkMetricMonitor { final PacketLossCalculationResult calculateResult = mPacketLossCalculator.getPacketLossRatePercentage( - mLastIpSecTransformState, state, getLogPrefix()); + mLastIpSecTransformState, + state, + mMaxSeqNumIncreasePerSecond, + getLogPrefix()); if (calculateResult.getResultType() == PACKET_LOSS_RATE_INVALID) { return; @@ -356,11 +406,18 @@ public class IpSecPacketLossDetector extends NetworkMetricMonitor { mLastIpSecTransformState = state; if (calculateResult.getPacketLossRatePercent() < mPacketLossRatePercentThreshold) { logV(logMsg); + + // In both "valid" or "unusual_seq_num_leap" cases, notify that the network has passed + // the validation onValidationResultReceivedInternal(false /* isFailed */); } else { logInfo(logMsg); - onValidationResultReceivedInternal(true /* isFailed */); + if (calculateResult.getResultType() == PACKET_LOSS_RATE_VALID) { + onValidationResultReceivedInternal(true /* isFailed */); + } + + // In both "valid" or "unusual_seq_num_leap" cases, trigger network validation if (Flags.validateNetworkOnIpsecLoss()) { // Trigger re-validation of the underlying network; if it fails, the VCN will // attempt to migrate away. @@ -376,6 +433,7 @@ public class IpSecPacketLossDetector extends NetworkMetricMonitor { public PacketLossCalculationResult getPacketLossRatePercentage( @NonNull IpSecTransformState oldState, @NonNull IpSecTransformState newState, + int maxSeqNumIncreasePerSecond, String logPrefix) { logVIpSecTransform("oldState", oldState, logPrefix); logVIpSecTransform("newState", newState, logPrefix); @@ -392,6 +450,22 @@ public class IpSecPacketLossDetector extends NetworkMetricMonitor { return PacketLossCalculationResult.invalid(); } + boolean isUnusualSeqNumLeap = false; + + // Handle sequence number leap + if (Flags.handleSeqNumLeap() + && maxSeqNumIncreasePerSecond != MAX_SEQ_NUM_INCREASE_DEFAULT_DISABLED) { + final long timeDiffMillis = + newState.getTimestampMillis() - oldState.getTimestampMillis(); + final long maxSeqNumIncrease = timeDiffMillis * maxSeqNumIncreasePerSecond / 1000; + + // Sequence numbers are unsigned 32-bit values. If maxSeqNumIncrease overflows, + // isUnusualSeqNumLeap can never be true. + if (maxSeqNumIncrease >= 0 && newSeqHi - oldSeqHi >= maxSeqNumIncrease) { + isUnusualSeqNumLeap = true; + } + } + // Get the expected packet count by assuming there is no packet loss. In this case, SA // should receive all packets whose sequence numbers are smaller than the lower bound of // the replay window AND the packets received within the window. @@ -411,6 +485,11 @@ public class IpSecPacketLossDetector extends NetworkMetricMonitor { + " actualPktCntDiff: " + actualPktCntDiff); + if (Flags.handleSeqNumLeap() && expectedPktCntDiff < MIN_VALID_EXPECTED_RX_PACKET_NUM) { + // The sample size is too small to ensure a reliable detection result + return PacketLossCalculationResult.invalid(); + } + if (expectedPktCntDiff < 0 || expectedPktCntDiff == 0 || actualPktCntDiff < 0 @@ -420,7 +499,9 @@ public class IpSecPacketLossDetector extends NetworkMetricMonitor { } final int percent = 100 - (int) (actualPktCntDiff * 100 / expectedPktCntDiff); - return PacketLossCalculationResult.valid(percent); + return isUnusualSeqNumLeap + ? PacketLossCalculationResult.unusualSeqNumLeap(percent) + : PacketLossCalculationResult.valid(percent); } } @@ -462,6 +543,11 @@ public class IpSecPacketLossDetector extends NetworkMetricMonitor { PACKET_LOSS_RATE_INVALID, PACKET_LOSS_PERCENT_UNAVAILABLE); } + /** Construct an instance indicating that there is an unusual sequence number leap */ + public static PacketLossCalculationResult unusualSeqNumLeap(int percent) { + return new PacketLossCalculationResult(PACKET_LOSS_UNUSUAL_SEQ_NUM_LEAP, percent); + } + @PacketLossResultType public int getResultType() { return mResultType; diff --git a/services/core/java/com/android/server/vcn/routeselection/NetworkMetricMonitor.java b/services/core/java/com/android/server/vcn/routeselection/NetworkMetricMonitor.java index a1b212f8d3d7..b9b10606a188 100644 --- a/services/core/java/com/android/server/vcn/routeselection/NetworkMetricMonitor.java +++ b/services/core/java/com/android/server/vcn/routeselection/NetworkMetricMonitor.java @@ -272,6 +272,11 @@ public abstract class NetworkMetricMonitor implements AutoCloseable { } } + protected static void logE(String className, String msgWithPrefix) { + Slog.w(className, msgWithPrefix); + LOCAL_LOG.log("[ERROR ] " + className + msgWithPrefix); + } + protected static void logWtf(String className, String msgWithPrefix) { Slog.wtf(className, msgWithPrefix); LOCAL_LOG.log("[WTF ] " + className + msgWithPrefix); diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java index 87c5b7b8a120..00d42e0eb109 100644 --- a/services/core/java/com/android/server/wm/DisplayContent.java +++ b/services/core/java/com/android/server/wm/DisplayContent.java @@ -2003,7 +2003,12 @@ class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.Disp } // Update directly because the app which will change the orientation of display is ready. if (mDisplayRotation.updateOrientation(getOrientation(), false /* forceUpdate */)) { - sendNewConfiguration(); + // Run rotation change on display thread. See Transition#shouldApplyOnDisplayThread(). + mWmService.mH.post(() -> { + synchronized (mWmService.mGlobalLock) { + sendNewConfiguration(); + } + }); return; } if (mRemoteDisplayChangeController.isWaitingForRemoteDisplayChange()) { diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java index ed88b5a7c449..143605ac7320 100644 --- a/services/core/java/com/android/server/wm/WindowManagerService.java +++ b/services/core/java/com/android/server/wm/WindowManagerService.java @@ -2446,6 +2446,9 @@ public class WindowManagerService extends IWindowManager.Stub ProtoLog.i(WM_DEBUG_SCREEN_ON, "Relayout %s: oldVis=%d newVis=%d. %s", win, oldVisibility, viewVisibility, new RuntimeException().fillInStackTrace()); + if (becameVisible) { + onWindowVisible(win); + } win.setDisplayLayoutNeeded(); win.mGivenInsetsPending = (flags & WindowManagerGlobal.RELAYOUT_INSETS_PENDING) != 0; @@ -10168,7 +10171,7 @@ public class WindowManagerService extends IWindowManager.Stub * Called to notify WMS that the specified window has become visible. This shows a Toast if the * window is deemed to hold sensitive content. */ - void onWindowVisible(@NonNull WindowState w) { + private void onWindowVisible(@NonNull WindowState w) { showToastIfBlockingScreenCapture(w); } diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java index d3baedc6e2a1..2fcee50e6f85 100644 --- a/services/core/java/com/android/server/wm/WindowState.java +++ b/services/core/java/com/android/server/wm/WindowState.java @@ -28,7 +28,6 @@ import static android.graphics.GraphicsProtos.dumpPointProto; import static android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS; import static android.os.PowerManager.DRAW_WAKE_LOCK; import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER; -import static android.permission.flags.Flags.sensitiveContentImprovements; import static android.view.SurfaceControl.Transaction; import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT; import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME; @@ -2139,9 +2138,6 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP } } setDisplayLayoutNeeded(); - if (sensitiveContentImprovements() && visible) { - mWmService.onWindowVisible(this); - } } } diff --git a/services/core/jni/linux/usb/f_accessory.h b/services/core/jni/linux/usb/f_accessory.h new file mode 100644 index 000000000000..abd864cabc5d --- /dev/null +++ b/services/core/jni/linux/usb/f_accessory.h @@ -0,0 +1,34 @@ +/* + * This file is auto-generated. Modifications will be lost. + * + * See https://android.googlesource.com/platform/bionic/+/master/libc/kernel/ + * for more information. + */ +#ifndef _UAPI_LINUX_USB_F_ACCESSORY_H +#define _UAPI_LINUX_USB_F_ACCESSORY_H +#define USB_ACCESSORY_VENDOR_ID 0x18D1 +#define USB_ACCESSORY_PRODUCT_ID 0x2D00 +#define USB_ACCESSORY_ADB_PRODUCT_ID 0x2D01 +#define ACCESSORY_STRING_MANUFACTURER 0 +#define ACCESSORY_STRING_MODEL 1 +#define ACCESSORY_STRING_DESCRIPTION 2 +#define ACCESSORY_STRING_VERSION 3 +#define ACCESSORY_STRING_URI 4 +#define ACCESSORY_STRING_SERIAL 5 +#define ACCESSORY_GET_PROTOCOL 51 +#define ACCESSORY_SEND_STRING 52 +#define ACCESSORY_START 53 +#define ACCESSORY_REGISTER_HID 54 +#define ACCESSORY_UNREGISTER_HID 55 +#define ACCESSORY_SET_HID_REPORT_DESC 56 +#define ACCESSORY_SEND_HID_EVENT 57 +#define ACCESSORY_SET_AUDIO_MODE 58 +#define ACCESSORY_GET_STRING_MANUFACTURER _IOW('M', 1, char[256]) +#define ACCESSORY_GET_STRING_MODEL _IOW('M', 2, char[256]) +#define ACCESSORY_GET_STRING_DESCRIPTION _IOW('M', 3, char[256]) +#define ACCESSORY_GET_STRING_VERSION _IOW('M', 4, char[256]) +#define ACCESSORY_GET_STRING_URI _IOW('M', 5, char[256]) +#define ACCESSORY_GET_STRING_SERIAL _IOW('M', 6, char[256]) +#define ACCESSORY_IS_START_REQUESTED _IO('M', 7) +#define ACCESSORY_GET_AUDIO_MODE _IO('M', 8) +#endif diff --git a/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java b/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java index dedb687cff22..b1673e2c4c3c 100644 --- a/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java +++ b/services/credentials/java/com/android/server/credentials/GetCandidateRequestSession.java @@ -26,6 +26,7 @@ import android.credentials.CredentialManager; import android.credentials.CredentialProviderInfo; import android.credentials.GetCandidateCredentialsException; import android.credentials.GetCandidateCredentialsResponse; +import android.credentials.GetCredentialException; import android.credentials.GetCredentialRequest; import android.credentials.GetCredentialResponse; import android.credentials.IGetCandidateCredentialsCallback; @@ -159,24 +160,26 @@ public class GetCandidateRequestSession extends RequestSession<GetCredentialRequ public void onFinalErrorReceived(ComponentName componentName, String errorType, String message) { Slog.d(TAG, "onFinalErrorReceived"); + if (GetCredentialException.TYPE_USER_CANCELED.equals(errorType)) { + Slog.d(TAG, "User canceled but session is not being terminated"); + return; + } respondToFinalReceiverWithFailureAndFinish(errorType, message); } @Override public void onUiCancellation(boolean isUserCancellation) { - String exception = GetCandidateCredentialsException.TYPE_USER_CANCELED; - String message = "User cancelled the selector"; - if (!isUserCancellation) { - exception = GetCandidateCredentialsException.TYPE_INTERRUPTED; - message = "The UI was interrupted - please try again."; - } - mRequestSessionMetric.collectFrameworkException(exception); - respondToFinalReceiverWithFailureAndFinish(exception, message); + Slog.d(TAG, "User canceled but session is not being terminated"); } private void respondToFinalReceiverWithFailureAndFinish( String exception, String message ) { + if (mRequestSessionStatus == RequestSessionStatus.COMPLETE) { + Slog.w(TAG, "Request has already been completed. This is strange."); + return; + } + if (mAutofillCallback != null) { Bundle resultData = new Bundle(); resultData.putStringArray( @@ -221,6 +224,19 @@ public class GetCandidateRequestSession extends RequestSession<GetCredentialRequ public void onFinalResponseReceived(ComponentName componentName, GetCredentialResponse response) { Slog.d(TAG, "onFinalResponseReceived"); + if (mRequestSessionStatus == RequestSessionStatus.COMPLETE) { + Slog.w(TAG, "Request has already been completed. This is strange."); + return; + } + respondToFinalReceiverWithResponseAndFinish(response); + } + + private void respondToFinalReceiverWithResponseAndFinish(GetCredentialResponse response) { + if (mRequestSessionStatus == RequestSessionStatus.COMPLETE) { + Slog.w(TAG, "Request has already been completed. This is strange."); + return; + } + if (this.mAutofillCallback != null) { Slog.d(TAG, "onFinalResponseReceived sending through final receiver"); Bundle resultData = new Bundle(); diff --git a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java index 5897d76663c5..087714681724 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java @@ -920,6 +920,7 @@ public final class DisplayDeviceConfigTest { @Test public void testEvenDimmer() throws IOException { when(mFlags.isEvenDimmerEnabled()).thenReturn(true); + when(mResources.getBoolean(R.bool.config_evenDimmerEnabled)).thenReturn(true); setupDisplayDeviceConfigFromDisplayConfigFile(getContent(getValidLuxThrottling(), getValidProxSensor(), /* includeIdleMode= */ false, /* enableEvenDimmer */ true)); diff --git a/services/tests/displayservicetests/src/com/android/server/display/ExternalDisplayPolicyTest.java b/services/tests/displayservicetests/src/com/android/server/display/ExternalDisplayPolicyTest.java index ea08be4f1be4..fe7bbe0ecf4b 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/ExternalDisplayPolicyTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/ExternalDisplayPolicyTest.java @@ -157,6 +157,7 @@ public class ExternalDisplayPolicyTest { verify(mMockedLogicalDisplayMapper, never()).setDisplayEnabledLocked(any(), anyBoolean()); verify(mMockedDisplayNotificationManager, times(2)) .onHighTemperatureExternalDisplayNotAllowed(); + verify(mMockedInjector, never()).onExternalDisplayReadyToBeEnabled(anyInt()); } @Test @@ -167,6 +168,7 @@ public class ExternalDisplayPolicyTest { verify(mMockedLogicalDisplayMapper, never()).setDisplayEnabledLocked(any(), anyBoolean()); verify(mMockedDisplayNotificationManager, never()) .onHighTemperatureExternalDisplayNotAllowed(); + verify(mMockedInjector, never()).onExternalDisplayReadyToBeEnabled(anyInt()); } @Test @@ -184,6 +186,7 @@ public class ExternalDisplayPolicyTest { // Expected only 1 invocation, upon critical temperature. verify(mMockedDisplayNotificationManager).onHighTemperatureExternalDisplayNotAllowed(); verify(mMockedExternalDisplayStatsService).onDisplayDisabled(eq(EXTERNAL_DISPLAY_ID)); + verify(mMockedInjector, never()).onExternalDisplayReadyToBeEnabled(anyInt()); } @Test @@ -191,6 +194,7 @@ public class ExternalDisplayPolicyTest { mExternalDisplayPolicy.setExternalDisplayEnabledLocked(mMockedLogicalDisplay, /*enabled=*/ true); assertDisplaySetEnabled(/*enabled=*/ true); + verify(mMockedInjector).onExternalDisplayReadyToBeEnabled(eq(EXTERNAL_DISPLAY_ID)); } @Test diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java index 0efd04657033..d670b138e4bc 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java @@ -1945,7 +1945,7 @@ public class DisplayModeDirectorTest { SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>(); votesByDisplay.put(DISPLAY_ID_2, votes); - director.getDisplayObserver().onDisplayAdded(DISPLAY_ID_2); + director.getDisplayObserver().onExternalDisplayReadyToBeEnabled(DISPLAY_ID_2); director.injectVotesByDisplay(votesByDisplay); var desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID_2); diff --git a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayObserverTest.java b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayObserverTest.java index d0dd9218eb17..1c192efb3dc1 100644 --- a/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayObserverTest.java +++ b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayObserverTest.java @@ -38,7 +38,6 @@ import static org.mockito.Mockito.when; import android.content.Context; import android.content.ContextWrapper; import android.content.res.Resources; -import android.hardware.display.DisplayManager; import android.hardware.display.DisplayManagerInternal; import android.os.Handler; import android.os.Looper; @@ -109,7 +108,7 @@ public class DisplayObserverTest { private Context mContext; private DisplayModeDirector.Injector mInjector; private Handler mHandler; - private DisplayManager.DisplayListener mObserver; + private DisplayModeDirector.DisplayObserver mObserver; private Resources mResources; @Mock private DisplayManagerFlags mDisplayManagerFlags; @@ -161,6 +160,7 @@ public class DisplayObserverTest { .isEqualTo(null); // Testing that the vote is not added when display is added because feature is disabled + mObserver.onExternalDisplayReadyToBeEnabled(EXTERNAL_DISPLAY); mObserver.onDisplayAdded(EXTERNAL_DISPLAY); assertThat(getVote(DEFAULT_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE)) .isEqualTo(null); @@ -194,6 +194,7 @@ public class DisplayObserverTest { init(); assertThat(getVote(EXTERNAL_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE)) .isEqualTo(null); + mObserver.onExternalDisplayReadyToBeEnabled(EXTERNAL_DISPLAY); mObserver.onDisplayAdded(EXTERNAL_DISPLAY); assertThat(getVote(DEFAULT_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE)) .isEqualTo(null); @@ -245,6 +246,7 @@ public class DisplayObserverTest { init(); assertThat(getVote(EXTERNAL_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE)) .isEqualTo(null); + mObserver.onExternalDisplayReadyToBeEnabled(EXTERNAL_DISPLAY); mObserver.onDisplayAdded(EXTERNAL_DISPLAY); assertThat(getVote(DEFAULT_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE)) .isEqualTo(null); @@ -277,6 +279,7 @@ public class DisplayObserverTest { init(); assertThat(getVote(DEFAULT_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE)) .isEqualTo(null); + mObserver.onExternalDisplayReadyToBeEnabled(DEFAULT_DISPLAY); mObserver.onDisplayAdded(DEFAULT_DISPLAY); assertThat(getVote(DEFAULT_DISPLAY, PRIORITY_USER_SETTING_DISPLAY_PREFERRED_SIZE)) .isEqualTo(expectedResolutionVote); @@ -298,6 +301,7 @@ public class DisplayObserverTest { .thenReturn(MAX_HEIGHT); init(); assertThat(getVote(DEFAULT_DISPLAY, PRIORITY_LIMIT_MODE)).isEqualTo(null); + mObserver.onExternalDisplayReadyToBeEnabled(DEFAULT_DISPLAY); mObserver.onDisplayAdded(DEFAULT_DISPLAY); assertThat(getVote(DEFAULT_DISPLAY, PRIORITY_LIMIT_MODE)).isEqualTo(null); assertThat(getVote(EXTERNAL_DISPLAY, PRIORITY_LIMIT_MODE)).isEqualTo(null); @@ -317,6 +321,7 @@ public class DisplayObserverTest { .thenReturn(MAX_HEIGHT); init(); assertThat(getVote(EXTERNAL_DISPLAY, PRIORITY_LIMIT_MODE)).isEqualTo(null); + mObserver.onExternalDisplayReadyToBeEnabled(EXTERNAL_DISPLAY); mObserver.onDisplayAdded(EXTERNAL_DISPLAY); assertThat(getVote(DEFAULT_DISPLAY, PRIORITY_LIMIT_MODE)).isEqualTo(null); assertThat(getVote(EXTERNAL_DISPLAY, PRIORITY_LIMIT_MODE)).isEqualTo( @@ -336,6 +341,7 @@ public class DisplayObserverTest { when(mDisplayManagerFlags.isExternalDisplayLimitModeEnabled()).thenReturn(true); init(); assertThat(getVote(EXTERNAL_DISPLAY, PRIORITY_LIMIT_MODE)).isEqualTo(null); + mObserver.onExternalDisplayReadyToBeEnabled(EXTERNAL_DISPLAY); mObserver.onDisplayAdded(EXTERNAL_DISPLAY); assertThat(getVote(DEFAULT_DISPLAY, PRIORITY_LIMIT_MODE)).isEqualTo(null); assertThat(getVote(EXTERNAL_DISPLAY, PRIORITY_LIMIT_MODE)).isEqualTo(null); @@ -358,6 +364,7 @@ public class DisplayObserverTest { .thenReturn(true); init(); assertThat(getVote(GLOBAL_ID, PRIORITY_SYNCHRONIZED_REFRESH_RATE)).isEqualTo(null); + mObserver.onExternalDisplayReadyToBeEnabled(EXTERNAL_DISPLAY); mObserver.onDisplayAdded(EXTERNAL_DISPLAY); assertThat(getVote(GLOBAL_ID, PRIORITY_SYNCHRONIZED_REFRESH_RATE)).isEqualTo( Vote.forPhysicalRefreshRates( @@ -381,6 +388,7 @@ public class DisplayObserverTest { .thenReturn(true); init(); assertThat(getVote(GLOBAL_ID, PRIORITY_SYNCHRONIZED_REFRESH_RATE)).isEqualTo(null); + mObserver.onExternalDisplayReadyToBeEnabled(EXTERNAL_DISPLAY); mObserver.onDisplayAdded(EXTERNAL_DISPLAY); assertThat(getVote(GLOBAL_ID, PRIORITY_SYNCHRONIZED_REFRESH_RATE)).isEqualTo(null); } @@ -395,6 +403,7 @@ public class DisplayObserverTest { when(mDisplayManagerFlags.isDisplaysRefreshRatesSynchronizationEnabled()).thenReturn(true); init(); assertThat(getVote(GLOBAL_ID, PRIORITY_SYNCHRONIZED_REFRESH_RATE)).isEqualTo(null); + mObserver.onExternalDisplayReadyToBeEnabled(EXTERNAL_DISPLAY); mObserver.onDisplayAdded(EXTERNAL_DISPLAY); assertThat(getVote(GLOBAL_ID, PRIORITY_SYNCHRONIZED_REFRESH_RATE)).isEqualTo(null); } diff --git a/services/tests/servicestests/src/com/android/server/pm/TEST_MAPPING b/services/tests/servicestests/src/com/android/server/pm/TEST_MAPPING index 861562d11f10..305108ea0229 100644 --- a/services/tests/servicestests/src/com/android/server/pm/TEST_MAPPING +++ b/services/tests/servicestests/src/com/android/server/pm/TEST_MAPPING @@ -1,31 +1,11 @@ { "presubmit": [ { - "name": "FrameworksServicesTests", - "options": [ - { - "include-filter": "com.android.server.pm." - }, - { - "include-annotation": "android.platform.test.annotations.Presubmit" - }, - { - "exclude-annotation": "androidx.test.filters.FlakyTest" - }, - { - "exclude-annotation": "org.junit.Ignore" - } - ] + "name": "FrameworksServicesTests_pm_presubmit" } ], "postsubmit": [ { - // Presubmit is intentional here while testing with SLO checker. - // Tests are flaky, waiting to bypass. - "name": "FrameworksServicesTests_pm_presubmit" - }, - { - // Leave postsubmit here when migrating "name": "FrameworksServicesTests_pm_postsubmit" } ] diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelExtractorTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelExtractorTest.java index ad25d76e2db7..770712a191fd 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelExtractorTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationChannelExtractorTest.java @@ -26,14 +26,18 @@ import static android.media.AudioAttributes.USAGE_NOTIFICATION; import static android.media.AudioAttributes.USAGE_NOTIFICATION_RINGTONE; import static android.media.AudioAttributes.USAGE_UNKNOWN; import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT; +import static com.android.server.notification.NotificationChannelExtractor.RESTRICT_AUDIO_ATTRIBUTES; import static com.google.common.truth.Truth.assertThat; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertNull; +import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyInt; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.Flags; @@ -43,12 +47,14 @@ import android.app.PendingIntent; import android.app.Person; import android.media.AudioAttributes; import android.net.Uri; +import android.os.RemoteException; import android.os.UserHandle; import android.platform.test.annotations.EnableFlags; import android.platform.test.flag.junit.SetFlagsRule; import android.provider.Settings; import android.service.notification.StatusBarNotification; +import com.android.internal.compat.IPlatformCompat; import com.android.server.UiServiceTestCase; import org.junit.Before; @@ -60,6 +66,8 @@ import org.mockito.MockitoAnnotations; public class NotificationChannelExtractorTest extends UiServiceTestCase { @Mock RankingConfig mConfig; + @Mock + IPlatformCompat mPlatformCompat; @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT); @@ -73,6 +81,7 @@ public class NotificationChannelExtractorTest extends UiServiceTestCase { mExtractor = new NotificationChannelExtractor(); mExtractor.setConfig(mConfig); mExtractor.initialize(mContext, null); + mExtractor.setCompatChangeLogger(mPlatformCompat); } private NotificationRecord getRecord(NotificationChannel channel, Notification n) { @@ -82,7 +91,7 @@ public class NotificationChannelExtractorTest extends UiServiceTestCase { } @Test - public void testExtractsUpdatedConversationChannel() { + public void testExtractsUpdatedConversationChannel() throws RemoteException { NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_LOW); final Notification n = new Notification.Builder(getContext()) .setContentTitle("foo") @@ -101,7 +110,7 @@ public class NotificationChannelExtractorTest extends UiServiceTestCase { } @Test - public void testInvalidShortcutFlagEnabled_looksUpCorrectNonChannel() { + public void testInvalidShortcutFlagEnabled_looksUpCorrectNonChannel() throws RemoteException { NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_LOW); final Notification n = new Notification.Builder(getContext()) .setContentTitle("foo") @@ -122,7 +131,7 @@ public class NotificationChannelExtractorTest extends UiServiceTestCase { } @Test - public void testInvalidShortcutFlagDisabled_looksUpCorrectChannel() { + public void testInvalidShortcutFlagDisabled_looksUpCorrectChannel() throws RemoteException { NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_LOW); final Notification n = new Notification.Builder(getContext()) .setContentTitle("foo") @@ -143,7 +152,7 @@ public class NotificationChannelExtractorTest extends UiServiceTestCase { @Test @EnableFlags(Flags.FLAG_RESTRICT_AUDIO_ATTRIBUTES_CALL) - public void testAudioAttributes_callStyleCanUseCallUsage() { + public void testAudioAttributes_callStyleCanUseCallUsage() throws RemoteException { NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_HIGH); channel.setSound(Uri.EMPTY, new AudioAttributes.Builder() .setUsage(USAGE_NOTIFICATION_RINGTONE) @@ -162,11 +171,12 @@ public class NotificationChannelExtractorTest extends UiServiceTestCase { assertThat(mExtractor.process(r)).isNull(); assertThat(r.getAudioAttributes().getUsage()).isEqualTo(USAGE_NOTIFICATION_RINGTONE); assertThat(r.getChannel()).isEqualTo(channel); + verify(mPlatformCompat, never()).reportChangeByUid(anyLong(), anyInt()); } @Test @EnableFlags(Flags.FLAG_RESTRICT_AUDIO_ATTRIBUTES_CALL) - public void testAudioAttributes_nonCallStyleCannotUseCallUsage() { + public void testAudioAttributes_nonCallStyleCannotUseCallUsage() throws RemoteException { NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_HIGH); channel.setSound(Uri.EMPTY, new AudioAttributes.Builder() .setUsage(USAGE_NOTIFICATION_RINGTONE) @@ -180,13 +190,14 @@ public class NotificationChannelExtractorTest extends UiServiceTestCase { assertThat(mExtractor.process(r)).isNull(); // instance updated assertThat(r.getAudioAttributes().getUsage()).isEqualTo(USAGE_NOTIFICATION); + verify(mPlatformCompat).reportChangeByUid(RESTRICT_AUDIO_ATTRIBUTES, r.getUid()); // in-memory channel unchanged assertThat(channel.getAudioAttributes().getUsage()).isEqualTo(USAGE_NOTIFICATION_RINGTONE); } @Test @EnableFlags(Flags.FLAG_RESTRICT_AUDIO_ATTRIBUTES_ALARM) - public void testAudioAttributes_alarmCategoryCanUseAlarmUsage() { + public void testAudioAttributes_alarmCategoryCanUseAlarmUsage() throws RemoteException { NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_HIGH); channel.setSound(Uri.EMPTY, new AudioAttributes.Builder() .setUsage(USAGE_ALARM) @@ -201,11 +212,12 @@ public class NotificationChannelExtractorTest extends UiServiceTestCase { assertThat(mExtractor.process(r)).isNull(); assertThat(r.getAudioAttributes().getUsage()).isEqualTo(USAGE_ALARM); assertThat(r.getChannel()).isEqualTo(channel); + verify(mPlatformCompat, never()).reportChangeByUid(anyLong(), anyInt()); } @Test @EnableFlags(Flags.FLAG_RESTRICT_AUDIO_ATTRIBUTES_ALARM) - public void testAudioAttributes_nonAlarmCategoryCannotUseAlarmUsage() { + public void testAudioAttributes_nonAlarmCategoryCannotUseAlarmUsage() throws RemoteException { NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_HIGH); channel.setSound(Uri.EMPTY, new AudioAttributes.Builder() .setUsage(USAGE_ALARM) @@ -219,13 +231,14 @@ public class NotificationChannelExtractorTest extends UiServiceTestCase { assertThat(mExtractor.process(r)).isNull(); // instance updated assertThat(r.getAudioAttributes().getUsage()).isEqualTo(USAGE_NOTIFICATION); + verify(mPlatformCompat).reportChangeByUid(RESTRICT_AUDIO_ATTRIBUTES, r.getUid()); // in-memory channel unchanged assertThat(channel.getAudioAttributes().getUsage()).isEqualTo(USAGE_ALARM); } @Test @EnableFlags(Flags.FLAG_RESTRICT_AUDIO_ATTRIBUTES_MEDIA) - public void testAudioAttributes_noMediaUsage() { + public void testAudioAttributes_noMediaUsage() throws RemoteException { NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_HIGH); channel.setSound(Uri.EMPTY, new AudioAttributes.Builder() .setUsage(USAGE_MEDIA) @@ -239,13 +252,14 @@ public class NotificationChannelExtractorTest extends UiServiceTestCase { assertThat(mExtractor.process(r)).isNull(); // instance updated assertThat(r.getAudioAttributes().getUsage()).isEqualTo(USAGE_NOTIFICATION); + verify(mPlatformCompat).reportChangeByUid(RESTRICT_AUDIO_ATTRIBUTES, r.getUid()); // in-memory channel unchanged assertThat(channel.getAudioAttributes().getUsage()).isEqualTo(USAGE_MEDIA); } @Test @EnableFlags(Flags.FLAG_RESTRICT_AUDIO_ATTRIBUTES_MEDIA) - public void testAudioAttributes_noUnknownUsage() { + public void testAudioAttributes_noUnknownUsage() throws RemoteException { NotificationChannel channel = new NotificationChannel("a", "a", IMPORTANCE_HIGH); channel.setSound(Uri.EMPTY, new AudioAttributes.Builder() .setUsage(USAGE_UNKNOWN) @@ -259,6 +273,7 @@ public class NotificationChannelExtractorTest extends UiServiceTestCase { assertThat(mExtractor.process(r)).isNull(); // instance updated assertThat(r.getAudioAttributes().getUsage()).isEqualTo(USAGE_NOTIFICATION); + verify(mPlatformCompat).reportChangeByUid(RESTRICT_AUDIO_ATTRIBUTES, r.getUid()); // in-memory channel unchanged assertThat(channel.getAudioAttributes().getUsage()).isEqualTo(USAGE_UNKNOWN); } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index 805bc172035e..011f2e39d6f8 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -200,6 +200,7 @@ import android.content.pm.ActivityInfo; import android.content.pm.ApplicationInfo; import android.content.pm.IPackageManager; import android.content.pm.LauncherApps; +import android.content.pm.ModuleInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; import android.content.pm.ParceledListSlice; @@ -450,6 +451,9 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { private static final String VALID_CONVO_SHORTCUT_ID = "shortcut"; private static final String SEARCH_SELECTOR_PKG = "searchSelector"; + private static final String ADSERVICES_MODULE_PKG = "com.android.adservices"; + private static final String ADSERVICES_APK_PKG = "com.android.adservices.api"; + @Mock private NotificationListeners mListeners; @Mock @@ -740,7 +744,11 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { // Return first true for RoleObserver main-thread check when(mMainLooper.isCurrentThread()).thenReturn(true).thenReturn(false); - + ModuleInfo moduleInfo = new ModuleInfo(); + moduleInfo.setApexModuleName(ADSERVICES_MODULE_PKG); + moduleInfo.setApkInApexPackageNames(List.of(ADSERVICES_APK_PKG)); + when(mPackageManagerClient.getInstalledModules(anyInt())) + .thenReturn(List.of(moduleInfo)); if (upToBootPhase >= SystemService.PHASE_SYSTEM_SERVICES_READY) { mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY, mMainLooper); } @@ -13218,6 +13226,33 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { } @Test + public void fixSystemNotification_defaultAdservices_withOnGoingFlag_nondismissible() + throws Exception { + final ApplicationInfo ai = new ApplicationInfo(); + ai.packageName = ADSERVICES_APK_PKG; + ai.uid = mUid; + ai.flags |= ApplicationInfo.FLAG_SYSTEM; + + when(mPackageManagerClient.getApplicationInfoAsUser(anyString(), anyInt(), anyInt())) + .thenReturn(ai); + when(mAppOpsManager.checkOpNoThrow( + AppOpsManager.OP_SYSTEM_EXEMPT_FROM_DISMISSIBLE_NOTIFICATIONS, ai.uid, + ai.packageName)).thenReturn(AppOpsManager.MODE_IGNORED); + // Given: a notification from an app on the system partition has the flag + // FLAG_ONGOING_EVENT set + Notification n = new Notification.Builder(mContext, "test") + .setOngoing(true) + .build(); + + // When: fix the notification with NotificationManagerService + mService.fixNotification(n, ADSERVICES_APK_PKG, "tag", 9, 0, mUid, NOT_FOREGROUND_SERVICE, + true); + + // Then: the notification's flag FLAG_NO_DISMISS should be set + assertNotSame(0, n.flags & Notification.FLAG_NO_DISMISS); + } + + @Test public void fixCallNotification_withOnGoingFlag_shouldNotBeNonDismissible() throws Exception { // Given: a call notification has the flag FLAG_ONGOING_EVENT set diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java index aeeca2ae86f5..5033a380fa4d 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/PreferencesHelperTest.java @@ -3981,7 +3981,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { pm.applicationInfo = new ApplicationInfo(); pm.applicationInfo.uid = UID_O; List<PackageInfo> packages = ImmutableList.of(pm); - when(mPm.getInstalledPackagesAsUser(any(), anyInt())).thenReturn(packages); + when(mPm.getInstalledPackagesAsUser(eq(0), anyInt())).thenReturn(packages); mHelper.updateFixedImportance(users); assertTrue(mHelper.isImportanceLocked(PKG_O, UID_O)); @@ -4097,7 +4097,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { pm.applicationInfo = new ApplicationInfo(); pm.applicationInfo.uid = UID_O; List<PackageInfo> packages = ImmutableList.of(pm); - when(mPm.getInstalledPackagesAsUser(any(), eq(0))).thenReturn(packages); + when(mPm.getInstalledPackagesAsUser(0, 0)).thenReturn(packages); mHelper.updateFixedImportance(users); assertTrue(mHelper.getNotificationChannel(PKG_O, UID_O, a.getId(), false) @@ -4120,7 +4120,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { pm.applicationInfo = new ApplicationInfo(); pm.applicationInfo.uid = UID_O; List<PackageInfo> packages = ImmutableList.of(pm); - when(mPm.getInstalledPackagesAsUser(any(), eq(0))).thenReturn(packages); + when(mPm.getInstalledPackagesAsUser(0, 0)).thenReturn(packages); mHelper.updateFixedImportance(users); NotificationChannel a = new NotificationChannel("a", "a", IMPORTANCE_HIGH); @@ -4309,7 +4309,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { pm.applicationInfo = new ApplicationInfo(); pm.applicationInfo.uid = UID_O; List<PackageInfo> packages = ImmutableList.of(pm); - when(mPm.getInstalledPackagesAsUser(any(), eq(0))).thenReturn(packages); + when(mPm.getInstalledPackagesAsUser(0, 0)).thenReturn(packages); mHelper.updateFixedImportance(users); ArraySet<String> toRemove = new ArraySet<>(); @@ -4341,7 +4341,7 @@ public class PreferencesHelperTest extends UiServiceTestCase { pm.applicationInfo = new ApplicationInfo(); pm.applicationInfo.uid = UID_O; List<PackageInfo> packages = ImmutableList.of(pm); - when(mPm.getInstalledPackagesAsUser(any(), eq(0))).thenReturn(packages); + when(mPm.getInstalledPackagesAsUser(0, 0)).thenReturn(packages); mHelper.updateFixedImportance(users); assertTrue(mHelper.isImportanceLocked(PKG_O, UID_O)); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java index ad420f6bf502..527001df995f 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/RankingHelperTest.java @@ -55,6 +55,7 @@ import androidx.test.InstrumentationRegistry; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; +import com.android.internal.compat.IPlatformCompat; import com.android.server.UiServiceTestCase; import org.junit.Before; @@ -155,7 +156,8 @@ public class RankingHelperTest extends UiServiceTestCase { NotificationManager.Policy.STATE_CHANNELS_BYPASSING_DND, 0); when(mMockZenModeHelper.getNotificationPolicy()).thenReturn(mTestNotificationPolicy); mHelper = new RankingHelper(getContext(), mHandler, mConfig, mMockZenModeHelper, - mUsageStats, new String[] {ImportanceExtractor.class.getName()}); + mUsageStats, new String[] {ImportanceExtractor.class.getName()}, + mock(IPlatformCompat.class)); mNotiGroupGSortA = new Notification.Builder(mContext, TEST_CHANNEL_ID) .setContentTitle("A") diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java index 2c88ed2db2d6..7356b4376e8a 100644 --- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java +++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java @@ -1717,6 +1717,7 @@ public class DisplayContentTests extends WindowTestsBase { // The display should be rotated after the launch is finished. doReturn(false).when(app).isAnimating(anyInt(), anyInt()); mDisplayContent.mAppTransition.notifyAppTransitionFinishedLocked(app.token); + waitHandlerIdle(mWm.mH); mStatusBarWindow.finishSeamlessRotation(t); mNavBarWindow.finishSeamlessRotation(t); diff --git a/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java b/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java index 7b5b07c0fbf6..f31a87f2b1bf 100644 --- a/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java +++ b/telephony/common/com/android/internal/telephony/util/TelephonyUtils.java @@ -349,15 +349,16 @@ public final class TelephonyUtils { } /** - * @param plmn target plmn for validation. - * @return {@code true} if the target plmn is valid {@code false} otherwise. + * @param input string that want to be compared. + * @param regex string that express regular expression + * @return {@code true} if matched {@code false} otherwise. */ - public static boolean isValidPlmn(@Nullable String plmn) { - if (TextUtils.isEmpty(plmn)) { + private static boolean isValidPattern(@Nullable String input, @Nullable String regex) { + if (TextUtils.isEmpty(input) || TextUtils.isEmpty(regex)) { return false; } - Pattern pattern = Pattern.compile("^(?:[0-9]{3})(?:[0-9]{2}|[0-9]{3})$"); - Matcher matcher = pattern.matcher(plmn); + Pattern pattern = Pattern.compile(regex); + Matcher matcher = pattern.matcher(input); if (!matcher.matches()) { return false; } @@ -365,6 +366,22 @@ public final class TelephonyUtils { } /** + * @param countryCode two letters country code based on the ISO 3166-1. + * @return {@code true} if the countryCode is valid {@code false} otherwise. + */ + public static boolean isValidCountryCode(@Nullable String countryCode) { + return isValidPattern(countryCode, "^[A-Za-z]{2}$"); + } + + /** + * @param plmn target plmn for validation. + * @return {@code true} if the target plmn is valid {@code false} otherwise. + */ + public static boolean isValidPlmn(@Nullable String plmn) { + return isValidPattern(plmn, "^(?:[0-9]{3})(?:[0-9]{2}|[0-9]{3})$"); + } + + /** * @param serviceType target serviceType for validation. * @return {@code true} if the target serviceType is valid {@code false} otherwise. */ diff --git a/telephony/java/android/telephony/euicc/EuiccManager.java b/telephony/java/android/telephony/euicc/EuiccManager.java index ebabbf911399..ca4a643d7b20 100644 --- a/telephony/java/android/telephony/euicc/EuiccManager.java +++ b/telephony/java/android/telephony/euicc/EuiccManager.java @@ -1039,17 +1039,18 @@ public class EuiccManager { * subscription on the * current eUICC and the subscription to be downloaded according to the subscription metadata. * Without the former, an {@link #EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR} will be - * eturned in the callback intent to prompt the user to accept the download. + * returned in the callback intent to prompt the user to accept the download. * * <p> Starting from Android {@link android.os.Build.VERSION_CODES#VANILLA_ICE_CREAM}, * if the caller has the * {@code android.Manifest.permission#MANAGE_DEVICE_POLICY_MANAGED_SUBSCRIPTIONS} permission or - * is a profile owner or device owner, and - * {@code switchAfterDownload} is {@code false}, then the downloaded subscription - * will be managed by that caller. If {@code switchAfterDownload} is true, - * an {@link #EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR} will be - * returned in the callback intent to prompt the user to accept the download and the - * subscription will not be managed. + * is a profile owner or device owner, then the downloaded subscription + * will be managed by that caller. + * In case the caller is device owner or profile owner of an organization-owned device, {@code + * switchAfterDownload} can be set to true to automatically enable the subscription after + * download. If the caller is a profile owner on non organization owned device + * {@code switchAfterDownload} should be false otherwise the operation will fail with + * {@link #EMBEDDED_SUBSCRIPTION_RESULT_ERROR}. * * <p>On a multi-active SIM device, requires the * {@code android.Manifest.permission#WRITE_EMBEDDED_SUBSCRIPTIONS} permission, or a calling app diff --git a/tests/TelephonyCommonTests/src/com/android/internal/telephony/tests/TelephonyUtilsTest.java b/tests/TelephonyCommonTests/src/com/android/internal/telephony/tests/TelephonyUtilsTest.java index 755833234e02..f88d82bf29a8 100644 --- a/tests/TelephonyCommonTests/src/com/android/internal/telephony/tests/TelephonyUtilsTest.java +++ b/tests/TelephonyCommonTests/src/com/android/internal/telephony/tests/TelephonyUtilsTest.java @@ -85,6 +85,8 @@ public class TelephonyUtilsTest { assertTrue(TelephonyUtils.isValidPlmn("45006")); assertFalse(TelephonyUtils.isValidPlmn("1234567")); assertFalse(TelephonyUtils.isValidPlmn("1234")); + assertFalse(TelephonyUtils.isValidPlmn("")); + assertFalse(TelephonyUtils.isValidPlmn(null)); } @Test @@ -94,6 +96,19 @@ public class TelephonyUtilsTest { assertFalse(TelephonyUtils.isValidService(FIRST_SERVICE_TYPE - 1)); assertFalse(TelephonyUtils.isValidService(LAST_SERVICE_TYPE + 1)); } + + @Test + public void testIsValidCountryCode() { + assertTrue(TelephonyUtils.isValidCountryCode("US")); + assertTrue(TelephonyUtils.isValidCountryCode("cn")); + assertFalse(TelephonyUtils.isValidCountryCode("11")); + assertFalse(TelephonyUtils.isValidCountryCode("USA")); + assertFalse(TelephonyUtils.isValidCountryCode("chn")); + assertFalse(TelephonyUtils.isValidCountryCode("U")); + assertFalse(TelephonyUtils.isValidCountryCode("G7")); + assertFalse(TelephonyUtils.isValidCountryCode("")); + assertFalse(TelephonyUtils.isValidCountryCode(null)); + } } diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/IpSecPacketLossDetectorTest.java b/tests/vcn/java/com/android/server/vcn/routeselection/IpSecPacketLossDetectorTest.java index 0a83a53a7b59..c8b60e5c335f 100644 --- a/tests/vcn/java/com/android/server/vcn/routeselection/IpSecPacketLossDetectorTest.java +++ b/tests/vcn/java/com/android/server/vcn/routeselection/IpSecPacketLossDetectorTest.java @@ -17,8 +17,11 @@ package com.android.server.vcn.routeselection; import static android.net.vcn.VcnManager.VCN_NETWORK_SELECTION_IPSEC_PACKET_LOSS_PERCENT_THRESHOLD_KEY; +import static android.net.vcn.VcnManager.VCN_NETWORK_SELECTION_MAX_SEQ_NUM_INCREASE_PER_SECOND_KEY; import static android.net.vcn.VcnManager.VCN_NETWORK_SELECTION_POLL_IPSEC_STATE_INTERVAL_SECONDS_KEY; +import static com.android.server.vcn.routeselection.IpSecPacketLossDetector.MIN_VALID_EXPECTED_RX_PACKET_NUM; +import static com.android.server.vcn.routeselection.IpSecPacketLossDetector.getMaxSeqNumIncreasePerSecond; import static com.android.server.vcn.util.PersistableBundleUtils.PersistableBundleWrapper; import static org.junit.Assert.assertEquals; @@ -65,6 +68,7 @@ public class IpSecPacketLossDetectorTest extends NetworkEvaluationTestBase { private static final int REPLAY_BITMAP_LEN_BYTE = 512; private static final int REPLAY_BITMAP_LEN_BIT = REPLAY_BITMAP_LEN_BYTE * 8; private static final int IPSEC_PACKET_LOSS_PERCENT_THRESHOLD = 5; + private static final int MAX_SEQ_NUM_INCREASE_DEFAULT_DISABLED = -1; private static final long POLL_IPSEC_STATE_INTERVAL_MS = TimeUnit.SECONDS.toMillis(30L); @Mock private IpSecTransformWrapper mIpSecTransform; @@ -91,6 +95,9 @@ public class IpSecPacketLossDetectorTest extends NetworkEvaluationTestBase { eq(VCN_NETWORK_SELECTION_IPSEC_PACKET_LOSS_PERCENT_THRESHOLD_KEY), anyInt())) .thenReturn(IPSEC_PACKET_LOSS_PERCENT_THRESHOLD); + when(mCarrierConfig.getInt( + eq(VCN_NETWORK_SELECTION_MAX_SEQ_NUM_INCREASE_PER_SECOND_KEY), anyInt())) + .thenReturn(MAX_SEQ_NUM_INCREASE_DEFAULT_DISABLED); when(mDependencies.getPacketLossCalculator()).thenReturn(mPacketLossCalculator); @@ -112,6 +119,20 @@ public class IpSecPacketLossDetectorTest extends NetworkEvaluationTestBase { .build(); } + private static IpSecTransformState newNextTransformState( + IpSecTransformState before, + long timeDiffMillis, + long rxSeqNoDiff, + long packtCountDiff, + int packetInWin) { + return new IpSecTransformState.Builder() + .setTimestampMillis(before.getTimestampMillis() + timeDiffMillis) + .setRxHighestSequenceNumber(before.getRxHighestSequenceNumber() + rxSeqNoDiff) + .setPacketCount(before.getPacketCount() + packtCountDiff) + .setReplayBitmap(newReplayBitmap(packetInWin)) + .build(); + } + private static byte[] newReplayBitmap(int receivedPktCnt) { final BitSet bitSet = new BitSet(REPLAY_BITMAP_LEN_BIT); for (int i = 0; i < receivedPktCnt; i++) { @@ -165,7 +186,7 @@ public class IpSecPacketLossDetectorTest extends NetworkEvaluationTestBase { // Verify the first polled state is stored assertEquals(mTransformStateInitial, mIpSecPacketLossDetector.getLastTransformState()); verify(mPacketLossCalculator, never()) - .getPacketLossRatePercentage(any(), any(), anyString()); + .getPacketLossRatePercentage(any(), any(), anyInt(), anyString()); // Verify next poll is scheduled assertNull(mTestLooper.nextMessage()); @@ -278,7 +299,7 @@ public class IpSecPacketLossDetectorTest extends NetworkEvaluationTestBase { xfrmStateReceiver.onResult(newTransformState(1, 1, newReplayBitmap(1))); verify(mPacketLossCalculator, never()) - .getPacketLossRatePercentage(any(), any(), anyString()); + .getPacketLossRatePercentage(any(), any(), anyInt(), anyString()); } @Test @@ -289,7 +310,7 @@ public class IpSecPacketLossDetectorTest extends NetworkEvaluationTestBase { xfrmStateReceiver.onError(new RuntimeException("Test")); verify(mPacketLossCalculator, never()) - .getPacketLossRatePercentage(any(), any(), anyString()); + .getPacketLossRatePercentage(any(), any(), anyInt(), anyString()); } private void checkHandleLossRate( @@ -301,7 +322,7 @@ public class IpSecPacketLossDetectorTest extends NetworkEvaluationTestBase { startMonitorAndCaptureStateReceiver(); doReturn(mockPacketLossRate) .when(mPacketLossCalculator) - .getPacketLossRatePercentage(any(), any(), anyString()); + .getPacketLossRatePercentage(any(), any(), anyInt(), anyString()); // Mock receiving two states with mTransformStateInitial and an arbitrary transformNew final IpSecTransformState transformNew = newTransformState(1, 1, newReplayBitmap(1)); @@ -311,7 +332,10 @@ public class IpSecPacketLossDetectorTest extends NetworkEvaluationTestBase { // Verifications verify(mPacketLossCalculator) .getPacketLossRatePercentage( - eq(mTransformStateInitial), eq(transformNew), anyString()); + eq(mTransformStateInitial), + eq(transformNew), + eq(MAX_SEQ_NUM_INCREASE_DEFAULT_DISABLED), + anyString()); if (isLastStateExpectedToUpdate) { assertEquals(transformNew, mIpSecPacketLossDetector.getLastTransformState()); @@ -351,6 +375,22 @@ public class IpSecPacketLossDetectorTest extends NetworkEvaluationTestBase { false /* isCallbackExpected */); } + @Test + public void testHandleLossRate_unusualSeqNumLeap_highLossRate() throws Exception { + checkHandleLossRate( + PacketLossCalculationResult.unusualSeqNumLeap(22), + true /* isLastStateExpectedToUpdate */, + false /* isCallbackExpected */); + } + + @Test + public void testHandleLossRate_unusualSeqNumLeap_lowLossRate() throws Exception { + checkHandleLossRate( + PacketLossCalculationResult.unusualSeqNumLeap(2), + true /* isLastStateExpectedToUpdate */, + true /* isCallbackExpected */); + } + private void checkGetPacketLossRate( IpSecTransformState oldState, IpSecTransformState newState, @@ -358,7 +398,8 @@ public class IpSecPacketLossDetectorTest extends NetworkEvaluationTestBase { throws Exception { assertEquals( expectedLossRate, - mPacketLossCalculator.getPacketLossRatePercentage(oldState, newState, TAG)); + mPacketLossCalculator.getPacketLossRatePercentage( + oldState, newState, MAX_SEQ_NUM_INCREASE_DEFAULT_DISABLED, TAG)); } private void checkGetPacketLossRate( @@ -397,6 +438,21 @@ public class IpSecPacketLossDetectorTest extends NetworkEvaluationTestBase { } @Test + public void testGetPacketLossRate_expectedPacketNumTooFew() throws Exception { + final int oldRxNo = 4096; + final int oldPktCnt = 4096; + final int pktCntDiff = MIN_VALID_EXPECTED_RX_PACKET_NUM - 1; + final byte[] bitmapReceiveAll = newReplayBitmap(4096); + + final IpSecTransformState oldState = + newTransformState(oldRxNo, oldPktCnt, bitmapReceiveAll); + final IpSecTransformState newState = + newTransformState(oldRxNo + pktCntDiff, oldPktCnt + pktCntDiff, bitmapReceiveAll); + + checkGetPacketLossRate(oldState, newState, PacketLossCalculationResult.invalid()); + } + + @Test public void testGetPacketLossRate_againstInitialState() throws Exception { checkGetPacketLossRate(mTransformStateInitial, 7000, 7001, 4096, 0); checkGetPacketLossRate(mTransformStateInitial, 7000, 6000, 4096, 15); @@ -443,6 +499,45 @@ public class IpSecPacketLossDetectorTest extends NetworkEvaluationTestBase { checkGetPacketLossRate(oldState, 20000, 14000, 3000, 10); } + private void checkGetPktLossRate_unusualSeqNumLeap( + int maxSeqNumIncreasePerSecond, + int timeDiffMillis, + int rxSeqNoDiff, + PacketLossCalculationResult expected) + throws Exception { + final IpSecTransformState oldState = mTransformStateInitial; + final IpSecTransformState newState = + newNextTransformState( + oldState, + timeDiffMillis, + rxSeqNoDiff, + 1 /* packtCountDiff */, + 1 /* packetInWin */); + + assertEquals( + expected, + mPacketLossCalculator.getPacketLossRatePercentage( + oldState, newState, maxSeqNumIncreasePerSecond, TAG)); + } + + @Test + public void testGetPktLossRate_unusualSeqNumLeap() throws Exception { + checkGetPktLossRate_unusualSeqNumLeap( + 10000 /* maxSeqNumIncreasePerSecond */, + (int) TimeUnit.SECONDS.toMillis(2L), + 30000 /* rxSeqNoDiff */, + PacketLossCalculationResult.unusualSeqNumLeap(100)); + } + + @Test + public void testGetPktLossRate_unusualSeqNumLeap_smallSeqNumDiff() throws Exception { + checkGetPktLossRate_unusualSeqNumLeap( + 10000 /* maxSeqNumIncreasePerSecond */, + (int) TimeUnit.SECONDS.toMillis(2L), + 5000 /* rxSeqNoDiff */, + PacketLossCalculationResult.valid(100)); + } + // Verify the polling event is scheduled with expected delays private void verifyPollEventDelayAndScheduleNext(long expectedDelayMs) { if (expectedDelayMs > 0) { @@ -469,4 +564,24 @@ public class IpSecPacketLossDetectorTest extends NetworkEvaluationTestBase { // Verify the 3rd poll is scheduled with configured delay verifyPollEventDelayAndScheduleNext(POLL_IPSEC_STATE_INTERVAL_MS); } + + @Test + public void testGetMaxSeqNumIncreasePerSecond() throws Exception { + final int seqNumLeapNegative = 500_000; + when(mCarrierConfig.getInt( + eq(VCN_NETWORK_SELECTION_MAX_SEQ_NUM_INCREASE_PER_SECOND_KEY), anyInt())) + .thenReturn(seqNumLeapNegative); + assertEquals(seqNumLeapNegative, getMaxSeqNumIncreasePerSecond(mCarrierConfig)); + } + + @Test + public void testGetMaxSeqNumIncreasePerSecond_negativeValue() throws Exception { + final int seqNumLeapNegative = -10; + when(mCarrierConfig.getInt( + eq(VCN_NETWORK_SELECTION_MAX_SEQ_NUM_INCREASE_PER_SECOND_KEY), anyInt())) + .thenReturn(seqNumLeapNegative); + assertEquals( + MAX_SEQ_NUM_INCREASE_DEFAULT_DISABLED, + getMaxSeqNumIncreasePerSecond(mCarrierConfig)); + } } diff --git a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkEvaluationTestBase.java b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkEvaluationTestBase.java index af6daa17e223..6189fb0834d8 100644 --- a/tests/vcn/java/com/android/server/vcn/routeselection/NetworkEvaluationTestBase.java +++ b/tests/vcn/java/com/android/server/vcn/routeselection/NetworkEvaluationTestBase.java @@ -123,6 +123,7 @@ public abstract class NetworkEvaluationTestBase { mSetFlagsRule.enableFlags(Flags.FLAG_VALIDATE_NETWORK_ON_IPSEC_LOSS); mSetFlagsRule.enableFlags(Flags.FLAG_EVALUATE_IPSEC_LOSS_ON_LP_NC_CHANGE); + mSetFlagsRule.enableFlags(Flags.FLAG_HANDLE_SEQ_NUM_LEAP); when(mNetwork.getNetId()).thenReturn(-1); |